Concept internals

Markdown の書式設定

OpenClaw は、チャネル固有の出力をレンダリングする前に、送信する Markdown を共有中間表現 (IR)へ変換します。IR はプレーンテキストとスタイル/リンクのスパンを保持するため、 1 回の解析ですべてのチャネルに対応でき、チャンク分割によって スパンの途中で書式が分断されることもありません。

パイプライン

  1. Markdown を IR に解析markdownToIR)- プレーンテキストとスタイルスパン (太字、斜体、取り消し線、コード、コードブロック、スポイラー、引用ブロック、 見出し 1~6)、およびリンクスパン。オフセットは UTF-16 コード単位なので、Signal のスタイル 範囲をその API に直接合わせられます。テーブルは、チャネルが テーブルモードを有効にした場合のみ解析されます。
  2. IR をチャンク分割chunkMarkdownIR / renderMarkdownIRChunksWithinLimit
    • 分割はレンダリング前の IR テキストに対して行われるため、インラインスタイルと リンクは境界をまたいで分断されず、チャンクごとに切り分けられます。
  3. チャネルごとにレンダリングrenderMarkdownWithMarkers)- スタイルマーカーのマップが スパンをチャネルのネイティブマークアップに変換します。
チャネル レンダラー 備考
Slack mrkdwn トークン(*bold*_italic_`code`、コードフェンス) リンクは <url|label> になります。二重リンクを避けるため、解析時は自動リンクが無効です
Telegram HTML タグ(<b><i><s><code><pre><code><a href><tg-spoiler> richMessages がオンの場合、リッチメッセージのテーブルと見出し(<h1><h6>)にも対応します
Signal プレーンテキスト + text-style 範囲 ラベルが URL と異なる場合、リンクは label (url) としてレンダリングされます
Discord、WhatsApp、iMessage、Microsoft Teams、およびその他のチャネル プレーンテキスト IR ベースのスタイルはありません。Markdown テーブルの変換は引き続き convertMarkdownTables を介して実行されます

IR の例

入力 Markdown:

markdown
こんにちは **世界** - [ドキュメント](https://docs.openclaw.ai)を参照してください。

IR(概略):

json
{  "text": "こんにちは 世界 - ドキュメントを参照してください。",  "styles": [{ "start": 6, "end": 11, "style": "bold" }],  "links": [{ "start": 19, "end": 23, "href": "https://docs.openclaw.ai" }]}

テーブルの処理

markdown.tables は、チャネルが Markdown テーブルを変換する方法を、チャネルごと、 必要に応じてアカウントごとに制御します。

モード 動作
code コードブロック内で整列された ASCII テーブルとしてレンダリングします(フォールバックのデフォルト)
bullets 各行を label: value の箇条書き項目に変換します
block トランスポートが対応している場合はネイティブテーブルを維持し、それ以外は code にフォールバックします
off テーブル解析を無効にし、生のテーブルテキストを変更せずに渡します

チャネル別 Plugin のデフォルト:Signal、WhatsApp、Matrix のデフォルトは bullets、Mattermost のデフォルトは off、Telegram のデフォルトは block です(アカウントで richMessages が有効でない限り、code に解決されます)。明示的な Plugin のデフォルトがない チャネルは code にフォールバックします。

yaml
channels:  discord:    markdown:      tables: code    accounts:      work:        markdown:          tables: off

チャンク分割のルール

  • チャンク制限はチャネルアダプター/設定から取得され、レンダリングされた出力ではなく IR テキストに適用されます。
  • フェンス付きコードブロックは、チャネルが終了フェンスを正しくレンダリングできるよう、 末尾の改行を含む 1 つのブロックとして維持されます。
  • リストと引用ブロックの接頭辞は IR テキストの一部であるため、チャンク分割によって 接頭辞の途中で分断されることはありません。
  • インラインスタイルがチャンク間で分断されることはありません。レンダラーは、次のチャンクの 先頭で継続中のスタイルを再開します。

チャネル間のチャンク境界と配信動作については、ストリーミングとチャンク分割を 参照してください。

リンクポリシー

  • Slack: [label](url)<url|label>。裸の URL はそのまま維持されます。
  • Telegram: [label](url)<a href="url">label</a>(HTML 解析モード)。
  • Signal: ラベルがすでに URL と一致している場合を除き、[label](url)label (url)

スポイラー

スポイラーマーカー(||spoiler||)は Signal(SPOILER の スタイル範囲にマッピング)と Telegram(<tg-spoiler> にマッピング)で解析されます。その他のチャネルでは、 ||...|| はプレーンテキストとして扱われます。

チャネルフォーマッターの追加または更新

  1. チャネルに適したオプション(autolinkheadingStyleblockquotePrefixtableMode)を渡し、 markdownToIR(...)一度だけ解析します。
  2. スタイルマーカーのマップ(または Signal のようなトランスポート向けのカスタムスタイル範囲ロジック)と renderMarkdownWithMarkers(...)レンダリングします。
  3. 各チャンクをレンダリングする前に、chunkMarkdownIR(...) または renderMarkdownIRChunksWithinLimit(...)チャンク分割します。
  4. 送信処理から新しいチャンク分割処理とレンダラーを呼び出すよう、アダプターを接続します。
  5. チャネルがチャンク分割を行う場合は、書式テストと送信配信テストでテストします。

よくある落とし穴

  • Slack の山括弧トークン(<@U123><#C123><https://...>)は エスケープ後も維持する必要があります。生の HTML も引き続き安全にエスケープする必要があります。
  • Telegram HTML では、マークアップの破損を避けるため、タグ外のテキストをエスケープする必要があります。
  • Signal のスタイル範囲は、コードポイントのオフセットではなく UTF-16 オフセットを使用します。
  • 終了マーカーが独立した行に配置されるよう、フェンス付きコードブロックの末尾の改行を維持します。

関連項目

Was this useful?
On this page

On this page