Markdown 格式化
OpenClaw 在渲染特定频道的输出之前,会将出站 Markdown 转换为共享的中间表示(IR)。IR 保留纯文本以及样式/链接跨度,因此一次解析步骤即可服务于所有频道,且分块永远不会在跨度中间拆分格式。
流水线¶
- 将 Markdown 解析为 IR(
markdownToIR)- 纯文本加样式跨度(粗体、斜体、删除线、代码、代码块、剧透、块引用、标题 1-6)和链接跨度。偏移量采用 UTF-16 码元,因此 Signal 样式范围可直接与其 API 对齐。仅当频道选择使用表格模式时才解析表格。 - 对 IR 进行分块(
chunkMarkdownIR/renderMarkdownIRChunksWithinLimit)- 样式和链接跨度随文本一起切片。渲染尺寸分块器在频道转义和链接重写后测量每个候选块,并返回已接受的源切片及其渲染后的负载。 - 按频道渲染(
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 样式标记 | 使用共享 IR;样式化块在渲染后进行测量 |
IR 示例¶
输入 Markdown:
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。
分块规则¶
- 分块限制来自频道适配器/配置。
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** 后跟可见正文,这样不会隐藏或丢失任何内容。
添加或更新频道格式化器¶
- 解析一次,使用
markdownToIR(...),传入适合频道的选项(autolink、headingStyle、blockquotePrefix、tableMode)。 - 渲染,使用
renderMarkdownWithMarkers(...)和样式标记映射(或针对 Signal 等传输的自定义样式范围逻辑)。 - 分块,使用
chunkMarkdownIR(...)或renderMarkdownIRChunksWithinLimit(...)。后者返回测量好的rendered负载;请直接使用它,而不是重新渲染源代码。 - 接入适配器,让出站发送路径调用新的分块器和渲染器。
- 测试,使用格式测试;如果频道支持分块,则还需进行出站投递测试。
常见陷阱¶
- Slack 尖括号标记(
<@U123>、<#C123>、<https://...>)必须在转义后保留;原始 HTML 仍需安全转义。 - Telegram HTML 要求对标签外的文本进行转义,以避免标记损坏。
- Signal 样式范围使用 UTF-16 偏移量,而非码点偏移量。
- 保留围栏代码块末尾的换行符,使结束标记位于独立行。
- 代码跨度解析会保留全空格内容。仅当两端都存在空格且内容不全为空格时,它才会从两端各移除一个周围空格。
- 助手回复清理会移除 Markdown 代码之外的合法
<final>标记,包括嵌套或游离标记,同时保留其包裹的回答文本。请将字面量<final>payload</final>示例放在行内代码或围栏代码块中,以保留其标签。
相关¶
出站流式行为、分块边界以及特定频道的投递方式。
模型在对话前看到的内容,包括注入的工作区文件。
本页原文 Markdown:在 AtomGit 查看·内容源自开源项目 cl/openclaw