跳转至

Markdown 格式化

OpenClaw 在渲染特定频道的输出之前,会将出站 Markdown 转换为共享的中间表示(IR)。IR 保留纯文本以及样式/链接跨度,因此一次解析步骤即可服务于所有频道,且分块永远不会在跨度中间拆分格式。

流水线

  1. 将 Markdown 解析为 IR(markdownToIR)- 纯文本加样式跨度(粗体、斜体、删除线、代码、代码块、剧透、块引用、标题 1-6)和链接跨度。偏移量采用 UTF-16 码元,因此 Signal 样式范围可直接与其 API 对齐。仅当频道选择使用表格模式时才解析表格。
  2. 对 IR 进行分块(chunkMarkdownIR / renderMarkdownIRChunksWithinLimit)- 样式和链接跨度随文本一起切片。渲染尺寸分块器在频道转义和链接重写后测量每个候选块,并返回已接受的源切片及其渲染后的负载。
  3. 按频道渲染(renderMarkdownWithMarkers)- 样式标记映射将跨度转换为该频道的原生标记。

原始内联 HTML 词素在解析期间保留其原始字节。属性值或已识别的内联注释中的 Markdown 标记和实体不会被解析为 Markdown 内容。未序列化的作者标签范围允许支持 HTML 的渲染器解释这些标签;其他渲染器则将其保留为字面量或进行转义。HTML 块解析保持禁用状态,因此容器内的 Markdown 仍然有效,且其主体中的裸 URL 保留正常的链接化处理。

共享 IR 渲染器示例:

频道 渲染器 备注
Matrix HTML 标签,包括原生表格 自动回复、直接发送和媒体说明使用相同的格式化器
Signal 纯文本 + text-style 范围 当标签与 URL 不同时,链接渲染为 label (url)
Slack mrkdwn 标记(*bold*、_italic_、`code`、代码围栏) 链接变为 <url\|label>;解析期间禁用自动链接以避免重复链接
Telegram HTML 标签(<b>、<i>、<s>、<code>、<pre><code>、<a href>、<tg-spoiler>) 当启用 richMessages 时,还支持富消息表格和标题(<h1>-<h6>)
WhatsApp WhatsApp 样式标记 使用共享 IR;样式化块在渲染后进行测量

IR 示例

输入 Markdown:

Hello **world** - see [docs](https://docs.openclaw.ai).

IR(示意图):

{
  "text": "Hello world - see docs.",
  "styles": [{ "start": 6, "end": 11, "style": "bold" }],
  "links": [{ "start": 19, "end": 23, "href": "https://docs.openclaw.ai" }]
}

表格处理

markdown.tables 控制频道如何转换 Markdown 表格,可按频道设置,也可按账户设置:

Mode 行为
code 在代码块内渲染为对齐的 ASCII 表格(回退默认值)
bullets 将每行转换为 label: value 列表项
block 在传输支持的地方保留原生表格;否则回退到 code
off 禁用表格解析;原始表格文本原样通过

在每种启用的表格模式下,表格单元格中的内联代码都保留其解析后的内容,包括前导和尾随空格。

各频道的插件默认值:Matrix 默认为 block(原生表格);Mattermost 默认为 off;Signal 和 WhatsApp 默认为 bullets;Telegram 默认为 block(除非账户启用了 richMessages,否则解析为 code)。任何没有显式插件默认值的频道都回退到 code。

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

分块规则

  • 分块限制来自频道适配器/配置。chunkMarkdownIR 限制 IR 文本;renderMarkdownIRChunksWithinLimit 以传输的大小单位测量最终负载,包括转义和重写后的链接。
  • 围栏代码块作为一个块保留,并带有尾随换行符,以便频道正确渲染闭合围栏。
  • 列表和块引用前缀是 IR 文本的一部分,因此分块绝不会在前缀中间拆分。
  • 列表项中的段落、标题和代码块在 IR 中保持分离,包括嵌套在引用列表项中的段落。
  • 内联样式绝不会跨块拆分;渲染器会在下一个块的开头重新打开未关闭的样式。

参见流式传输和分块了解跨频道的块边界和投递行为。

  • Slack: [label](url) -> <url|label>;裸 URL 保持原样。
  • Telegram: [label](url) -> <a href="url">label</a>(HTML 解析模式)。
  • Signal: [label](url) -> label (url),除非标签已与 URL 匹配。

剧透

剧透标记(||spoiler||)会为 Signal(映射到 SPOILER 样式范围)和 Telegram(映射到 <tg-spoiler>)解析。其他频道将 ||...|| 视为纯文本。

可折叠详情

Control UI 和启用了 richMessages: true 的 Telegram 账户会将 <details><summary>Label</summary> 折叠内容渲染为原生可折叠区域。仅当当前回复界面支持时,OpenClaw 才会将此选项告知模型。其他频道(包括未启用富消息的 Telegram 账户)会将每个折叠内容展开为 **Summary** 后跟可见正文,这样不会隐藏或丢失任何内容。

添加或更新频道格式化器

  1. 解析一次,使用 markdownToIR(...),传入适合频道的选项(autolink、headingStyle、blockquotePrefix、tableMode)。
  2. 渲染,使用 renderMarkdownWithMarkers(...) 和样式标记映射(或针对 Signal 等传输的自定义样式范围逻辑)。
  3. 分块,使用 chunkMarkdownIR(...) 或 renderMarkdownIRChunksWithinLimit(...)。后者返回测量好的 rendered 负载;请直接使用它,而不是重新渲染源代码。
  4. 接入适配器,让出站发送路径调用新的分块器和渲染器。
  5. 测试,使用格式测试;如果频道支持分块,则还需进行出站投递测试。

常见陷阱

  • Slack 尖括号标记(<@U123>、<#C123>、<https://...>)必须在转义后保留;原始 HTML 仍需安全转义。
  • Telegram HTML 要求对标签外的文本进行转义,以避免标记损坏。
  • Signal 样式范围使用 UTF-16 偏移量,而非码点偏移量。
  • 保留围栏代码块末尾的换行符,使结束标记位于独立行。
  • 代码跨度解析会保留全空格内容。仅当两端都存在空格且内容不全为空格时,它才会从两端各移除一个周围空格。
  • 助手回复清理会移除 Markdown 代码之外的合法 <final> 标记,包括嵌套或游离标记,同时保留其包裹的回答文本。请将字面量 <final>payload</final> 示例放在行内代码或围栏代码块中,以保留其标签。

流式传输与分块

出站流式行为、分块边界以及特定频道的投递方式。

系统提示

模型在对话前看到的内容,包括注入的工作区文件。

本页原文 Markdown:在 AtomGit 查看·内容源自开源项目 cl/openclaw