Markdown-Formatierung
OpenClaw formatiert ausgehende Markdown-Nachrichten, indem es sie zuerst in eine gemeinsame Zwischenrepräsentation (IR) umwandelt, bevor die channel-spezifische Ausgabe gerendert wird. Die IR behält den Quelltext bei und enthält Style- und Link-Spans, damit Chunking und Rendering über alle Channels hinweg konsistent bleiben.
Ziele
- Konsistenz: ein Parse-Schritt, mehrere Renderer.
- Sicheres Chunking: Text wird vor dem Rendering aufgeteilt, damit Inline-Formatierungen nie über Chunk-Grenzen hinweg brechen.
- Channel-Anpassung: dieselbe IR wird auf Slack mrkdwn, Telegram HTML und Signal Style-Ranges gemappt — ohne Markdown erneut zu parsen.
Pipeline
- Markdown -> IR parsen
- Die IR ist Klartext plus Style-Spans (bold/italic/strike/code/spoiler) und Link-Spans.
- Offsets sind UTF-16 Code Units, damit Signal Style-Ranges mit der API übereinstimmen.
- Tabellen werden nur geparst, wenn ein Channel die Tabellenkonvertierung aktiviert hat.
- IR chunken (Format-first)
- Das Chunking erfolgt auf dem IR-Text vor dem Rendering.
- Inline-Formatierungen werden nicht über Chunks aufgeteilt; Spans werden pro Chunk geschnitten.
- Pro Channel rendern
- Slack: mrkdwn-Tokens (bold/italic/strike/code), Links als
<url|label>. - Telegram: HTML-Tags (
<b>,<i>,<s>,<code>,<pre><code>,<a href>). - Signal: Klartext +
text-styleRanges; Links werden zulabel (url), wenn das Label abweicht.
- Slack: mrkdwn-Tokens (bold/italic/strike/code), Links als
IR-Beispiel
Eingabe-Markdown:
Hello **world** — see [docs](https://docs.openclaw.ai).
IR (schematisch):
{
"text": "Hello world — see docs.",
"styles": [{ "start": 6, "end": 11, "style": "bold" }],
"links": [{ "start": 19, "end": 23, "href": "https://docs.openclaw.ai" }]
}
Wo es verwendet wird
- Slack-, Telegram- und Signal-Outbound-Adapter rendern aus der IR.
- Andere Channels (WhatsApp, iMessage, MS Teams, Discord) verwenden weiterhin Klartext oder ihre eigenen Formatierungsregeln, wobei die Markdown-Tabellenkonvertierung vor dem Chunking angewendet wird, wenn aktiviert.
Tabellenbehandlung
Markdown-Tabellen werden nicht einheitlich von Chat-Clients unterstützt. Verwende
markdown.tables, um die Konvertierung pro Channel (und pro Account) zu steuern.
code: Tabellen als Code-Blöcke rendern (Standard für die meisten Channels).bullets: Jede Zeile in Aufzählungspunkte umwandeln (Standard für Signal + WhatsApp).off: Tabellen-Parsing und -Konvertierung deaktivieren; roher Tabellentext wird durchgereicht.
Config-Keys:
channels:
discord:
markdown:
tables: code
accounts:
work:
markdown:
tables: off
Chunking-Regeln
- Chunk-Limits kommen von Channel-Adaptern/Config und werden auf den IR-Text angewendet.
- Code-Fences werden als einzelner Block mit abschließendem Newline erhalten, damit Channels sie korrekt rendern.
- Listen-Präfixe und Blockquote-Präfixe sind Teil des IR-Texts, sodass das Chunking nicht mitten im Präfix trennt.
- Inline-Styles (bold/italic/strike/inline-code/spoiler) werden nie über Chunks aufgeteilt; der Renderer öffnet Styles innerhalb jedes Chunks neu.
Mehr zum Chunking-Verhalten über Channels hinweg findest du unter Streaming + Chunking.
Link-Policy
- Slack:
[label](url)-><url|label>; nackte URLs bleiben nackt. Autolink ist beim Parsen deaktiviert, um Doppel-Verlinkung zu vermeiden. - Telegram:
[label](url)-><a href="url">label</a>(HTML-Parse-Modus). - Signal:
[label](url)->label (url), außer das Label entspricht der URL.
Spoiler
Spoiler-Marker (||spoiler||) werden nur für Signal geparst, wo sie auf
SPOILER-Style-Ranges gemappt werden. Andere Channels behandeln sie als Klartext.
Einen Channel-Formatter hinzufügen oder aktualisieren
- Einmal parsen: Verwende den gemeinsamen
markdownToIR(...)-Helper mit channel-spezifischen Optionen (autolink, heading style, blockquote prefix). - Rendern: Implementiere einen Renderer mit
renderMarkdownWithMarkers(...)und einer Style-Marker-Map (oder Signal Style-Ranges). - Chunken: Rufe
chunkMarkdownIR(...)vor dem Rendering auf; rendere jeden Chunk. - Adapter verdrahten: Aktualisiere den Channel-Outbound-Adapter, um den neuen Chunker und Renderer zu verwenden.
- Testen: Füge Format-Tests hinzu oder aktualisiere sie, sowie einen Outbound-Delivery-Test, wenn der Channel Chunking verwendet.
Häufige Stolperfallen
- Slack-Angle-Bracket-Tokens (
<@U123>,<#C123>,<https://...>) müssen erhalten bleiben; escape rohes HTML sicher. - Telegram HTML erfordert das Escapen von Text außerhalb von Tags, um kaputtes Markup zu vermeiden.
- Signal Style-Ranges hängen von UTF-16-Offsets ab; verwende keine Code-Point-Offsets.
- Erhalte abschließende Newlines für Fenced-Code-Blöcke, damit schließende Marker auf ihrer eigenen Zeile landen.