Markdown Formatting
送信チャネル向けの Markdown フォーマット変換パイプライン。
OpenClaw は、チャネル固有の出力をレンダリングする前に、送信 Markdown を
共有の中間表現 (IR) に変換してフォーマットします。IR は
ソーステキストをそのまま保持しながらスタイル/リンクスパンを保持するため、
チャンキングとレンダリングをチャネル間で一貫させることができます。
Goals
- 一貫性: 1 回の解析ステップ、複数のレンダラー。
- 安全なチャンキング: レンダリング前にテキストを分割するため、
インラインフォーマットがチャンク境界で分割されません。
- チャネルマッチング: 同じ IR を Slack mrkdwn、Telegram HTML、Signal
スタイルスパンにマッピングし、Markdown を再解析しません。
パイプライン
1. Markdown -> IR の解析
- IR はプレーンテキストにスタイルスパン(太字/斜線/取り消し線/コード/スポイラー)とリンクスパンを加えたものです。
- オフセットは UTF-16 コード単位であるため、Signal スタイルスパンは API と一致します。
- テーブルは、チャネルがテーブル変換をオプトインした場合のみ解析されます。
2. IR のチャンキング(フォーマット優先)
- レンダリング前に IR テキストをチャンキングします。
- インラインフォーマットはチャンク境界で分割されません。スパンはチャンクごとにスライスされます。
3. チャネルごとのレンダリング
- ''Slack:'' mrkdwn tokens (bold/italic/strike/code), links as ''<url|label>''.
- ''Telegram:'' HTML tags (''<b>'', ''<i>'', ''<s>'', ''<code>'', ''<pre><code>'', ''<a href>'').
- ''Signal:'' プレーンテキスト + ''text-style'' スパン。ラベルが異なる場合、リンクは ''label (url)'' になります。
IR の例
Input markdown:
Hello **world** — see [docs](https://docs.openclaw.ai).
IR (schematic):
{ "text": "Hello world — see docs.", "styles": [{ "start": 6, "end": 11, "style": "bold" }], "links": [{ "start": 19, "end": 23, "href": "https://docs.openclaw.ai" }] }Where it's used
- Slack、Telegram、Signal の送信アダプターは IR からレンダリングします。
- 他のチャネル(WhatsApp、iMessage、MS Teams、Discord)は引き続きプレーンテキストまたは
独自のフォーマットルールを使用し、Markdown テーブル変換はチャンキング前に
有効な場合に適用されます。
テーブル処理
Markdown テーブルはチャットクライアントで一貫してサポートされていません。
''markdown.tables'' を使用してチャネルごと(およびアカウントごと)の変換を制御します。
- ''code'':テーブルをコードブロックとしてレンダリング(ほとんどのチャネルのデフォルト)。
- ''bullets'':各行を箇条書きに変換(Signal + WhatsApp のデフォルト)。
- ''off'':テーブル解析と変換を無効化。生のテーブルテキストが通過します。
設定キー:
channels: discord: markdown: tables: code accounts: work: markdown: tables: off
チャンキングルール
- チャンク制限はチャネルアダプター/設定から取得され、IR テキストに適用されます。
- コードフェンスは末尾の改行を含む単一のチャンクとして保持されるため、
チャネルが正しくレンダリングできます。
- リストプレフィックスとブロック引用プレフィックスは IR テキストの一部であるため、
チャンキングはプレフィックスの途中で分割しません。
- インラインスタイル(太字/斜線/取り消し線/インラインコード/スポイラー)は
チャンク境界で分割されません。レンダラーは各チャンク内でスタイルを再開します。
チャネル間のチャンキング動作について詳しく知りたい場合は、
''ストリーミング + チャンキング'' を参照してください。
リンクポリシー
- ''Slack:'' ''[label](url)'' -> ''<url|label>''; bare URLs stay bare. Autolinks
二重リンクを避けるために解析中に無効化されます。
- ''Telegram:'' ''[label](url)'' -> ''<a href="url">label</a>'' (HTML parse mode).
- ''Signal:'' ''[label](url)'' -> ''label (url)''(ラベルが URL と異なる場合)。
スポイラー
スポイラータグ (''||spoiler||'') は Signal でのみ解析され、
スポイラースタイル範囲にマッピングされます。他のチャネルではプレーンテキストとして扱われます。
チャネルフォーマッターの追加または更新方法
1. ''一度だけ解析:'' チャネルに適した共有の ''markdownToIR(...)'' ヘルパーを
使用します(自動リンク、見出しスタイル、ブロック引用プレフィックスなどのオプション)。
2. ''レンダリング:'' ''renderMarkdownWithMarkers(...)'' と実装レンダラーを
使用します(スタイルトークンマップまたは Signal スタイル範囲)。
3. ''チャンキング:'' レンダリング前に ''chunkMarkdownIR(...)'' を呼び出します。各チャンクをレンダリングします。
4. アダプターの配線: チャネル送信アダプターを更新して新しいチャンカーと
レンダラーを使用します。
5. テスト: フォーマットテストと送信テストを追加または更新します(
チャネルがチャンキングを使用する場合)。
よくある落とし穴
- Angle bracket markers (''<@U123>'', ''<#C123>'', ''<https://...>'') must be
保持する必要があります。生の HTML を安全にエスケープします。
- Telegram HTML は、マークアップの破損を避けるために、タグ外のテキストをエスケープする必要があります。
- Signal スタイル範囲は UTF-16 オフセットに依存します。コードポイントオフセットを使用しないでください。
- フェンスコードブロックの末尾の改行を保持して、閉じタグが
独自の行に着地するようにします。