消息与投递
流式传输和分块
OpenClaw 有两个相互独立的流式传输层,目前向渠道消息并不提供真正的 token 增量流式传输:
- 分块流式传输(渠道):在助手写入时发送已完成的内容块。 这些是普通的渠道消息,而不是 token 增量。
- 预览流式传输(Telegram/Discord/Slack/Matrix/Mattermost/MS Teams): 在生成期间更新临时的预览消息(发送 + 编辑/追加)。
Control UI 启动状态
在 chat.send 确认存在活跃运行后,Gateway 网关可以在助手文本或工具活动可见之前发送类型化的
粗粒度启动状态。Control UI 会在工作指示器旁显示此状态,其中包括
工作区准备、环境预置、上下文准备和
模型启动等阶段。
首次出现助手增量或工具启动后,该运行的启动状态将被永久替代。 当工具等待操作员操作时,审批状态优先显示。工作树创建和初始云端分派发生在聊天运行 存在之前,因此其运行前 RPC 进度不会作为运行启动状态显示; 仅当活跃运行重新预置已回收的工作节点时,环境预置才会显示在此处。
分块流式传输(渠道消息)
分块流式传输会在助手输出可用时以粗粒度分块发送。
模型输出 └─ text_delta/events ├─ (blockStreamingBreak=text_end) │ └─ 随着缓冲区增长,分块器发送内容块 └─ (blockStreamingBreak=message_end) └─ 分块器在 message_end 时刷新 └─ 渠道发送(分块回复)text_delta/events:模型流事件(对于非流式模型可能较为稀疏)。chunker:EmbeddedBlockChunker应用最小/最大边界和断点偏好。channel send:实际出站消息(分块回复)。
控制项(除非另有说明,否则全部位于 agents.defaults 下):
| 键 | 值/结构 | 默认值 |
|---|---|---|
blockStreamingDefault |
"on" / "off" |
"off" |
blockStreamingBreak |
"text_end" / "message_end" |
- |
blockStreamingChunk |
{ minChars, maxChars, breakPreference? } |
- |
blockStreamingCoalesce |
{ minChars?, maxChars?, idleMs? }(发送前合并流式内容块) |
- |
*.streaming.block.enabled(渠道覆盖项) |
true / false,强制按渠道(及按账户)启用分块流式传输 |
- |
*.textChunkLimit(例如 channels.whatsapp.textChunkLimit) |
数字,硬上限 | 4000 |
*.streaming.chunkMode |
"length" / "newline" |
"length" |
channels.discord.maxLinesPerMessage |
数字,用于拆分过长回复以避免 UI 裁切的软行数上限 | 17 |
streaming.chunkMode: "newline" 会按空行(段落边界)拆分,
而不是按每个换行符拆分;当文本超过限制后,才会回退到按长度分块。
内置渠道将这些覆盖项写作
channels.<id>.streaming.{chunkMode,block.enabled,block.coalesce}。扁平形式的
*.chunkMode / *.blockStreaming / *.blockStreamingCoalesce 写法
在所有内置渠道中均为旧版写法:openclaw doctor --fix 会将其迁移为
嵌套结构,渠道 schema 会拒绝这些写法。仍使用扁平写法的外部 SDK 插件
配置会通过已弃用的回退机制继续工作(并发出运行时警告),
直至下一个发布周期。
blockStreamingBreak 的边界语义:
text_end:分块器一发送内容块就立即进行流式传输;在每个text_end时刷新。message_end:等待助手消息结束,然后刷新缓冲的 输出。如果缓冲文本超过maxChars,仍会使用分块器,因此 最终可能发送多个分块。
使用分块流式传输发送媒体
流式媒体必须使用 mediaUrl 或
mediaUrls 等结构化有效负载字段;流式文本不会被解析为附件命令。当分块
流式传输提前发送媒体时,OpenClaw 会记住该轮次的此次发送。如果
最终助手有效负载重复相同的媒体 URL,最终发送会移除
重复媒体,而不会再次发送附件。
完全重复的最终有效负载会被抑制。如果最终有效负载在已通过流式传输发送的媒体周围添加了 不同文本,OpenClaw 仍会发送新文本,同时确保媒体只发送一次。 这可防止 Telegram 等渠道出现重复的语音消息或文件。
分块算法(下限/上限)
分块流式传输由 EmbeddedBlockChunker 实现:
- **下限:**缓冲区达到
minChars前不发送(强制发送除外)。 - **上限:**优先在
maxChars前拆分;如果强制拆分,则在maxChars处拆分。 - 断点偏好链:
paragraph->newline->sentence-> 空白字符 -> 强制断开。 - **代码围栏:**绝不在围栏内部拆分;在
maxChars处强制拆分时,关闭并 重新打开围栏,以保持 Markdown 有效。
maxChars 会限制在渠道的 textChunkLimit 以内,因此无法超过
各渠道的上限。
合并(合并流式内容块)
启用分块流式传输后,OpenClaw 可以在发送前合并连续的分块 片段,以减少单行消息刷屏,同时仍提供 渐进式输出。
- 合并会等待空闲间隔(
idleMs)后再刷新。 - 缓冲区受
maxChars限制,超过此值时会刷新。 minChars会阻止发送过小的片段,直至积累足够文本 (最终刷新始终会发送剩余文本)。- 连接符根据
blockStreamingChunk.breakPreference得出:paragraph->\n\n,newline->\n,sentence-> 空格。 - 可通过
*.streaming.block.coalesce设置渠道覆盖项(包括 按账户配置)。 - 除非被覆盖,否则 Discord、Signal 和 Slack 默认将合并设置为
{ minChars: 1500, idleMs: 1000 }。
内容块之间的拟人化节奏
启用分块流式传输后,从第二个内容块开始,在分块 回复之间添加随机暂停,使多气泡回复显得更自然。
agents.defaults.humanDelay.mode |
行为 |
|---|---|
off(默认) |
不暂停 |
natural |
随机暂停 800-2500ms |
custom |
minMs/maxMs |
可通过 agents.entries.*.humanDelay 按智能体覆盖。仅适用于分块
回复,不适用于最终回复或工具摘要。
“流式发送分块或全部内容”
- 流式发送分块:
blockStreamingDefault: "on"+blockStreamingBreak: "text_end"(生成时发送)。非 Telegram 渠道还需要*.streaming.block.enabled: true。 - 最终流式发送全部内容:
blockStreamingBreak: "message_end"(刷新 一次;如果内容很长,可能包含多个分块)。 - 不使用分块流式传输:
blockStreamingDefault: "off"(仅发送最终回复)。
除非将 *.streaming.block.enabled 显式
设置为 true,否则分块流式传输处于关闭状态(例外:QQ Bot 没有 streaming.block 键,且除非
channels.qqbot.streaming.mode 为 "off",否则会流式发送
分块回复)。渠道可以在不发送分块回复的情况下流式传输实时预览
(channels.<channel>.streaming.mode)。blockStreaming* 的默认值位于 agents.defaults 下,而不是
配置根级别。
预览流式传输模式
规范键:channels.<channel>.streaming(嵌套的 { mode, ... };旧版
顶层布尔值/字符串写法由 openclaw doctor --fix 重写)。
| 模式 | 行为 |
|---|---|
off |
禁用预览流式传输 |
partial |
用最新文本替换单条预览 |
block |
以分块/追加步骤更新预览 |
progress |
生成期间显示进度/状态预览,完成时显示最终答案 |
streaming.mode: "block" 是用于 Discord 和 Telegram 等支持编辑的
渠道的预览流式传输模式;它本身不会在这些渠道启用
分块发送。使用 streaming.block.enabled 发送普通分块回复。
Microsoft Teams 是
例外:它没有草稿预览分块传输,因此 streaming.mode: "block" 会完全禁用原生流式传输,回复将作为常规
分块发送,而不是原生的部分/进度流式传输。Mattermost 也有所不同:
在 block 模式下,它会在已完成文本和
工具活动块之间轮换预览,因此早先的内容块会作为独立帖子保持可见,
而不会在一个可编辑草稿中被覆盖。
渠道映射
| 渠道 | off |
partial |
block |
progress |
|---|---|---|---|---|
| Telegram | 是 | 是 | 是 | 可编辑的进度草稿 |
| Discord | 是 | 是 | 是 | 可编辑的进度草稿 |
| Slack | 是 | 是 | 是 | 是 |
| Mattermost | 是 | 是 | 是 | 是 |
| MS Teams | 是 | 是 | 是 | 原生进度流 |
预览分块配置(streaming.preview.chunk.*,例如位于
channels.discord.streaming 或 channels.telegram.streaming 下)默认值为
minChars: 200、maxChars: 800(限制在渠道的 textChunkLimit 以内)和
breakPreference: "paragraph"。
仅限 Slack:
channels.slack.streaming.nativeTransport会在channels.slack.streaming.mode="partial"时切换 Slack 原生流式传输 API 调用(chat.startStream/chat.appendStream/chat.stopStream)(默认值:true)。- Slack 原生流式传输和 Slack 助手线程状态需要一个回复 线程目标。顶层私信不会显示这种线程式预览,但仍可 使用 Slack 草稿预览帖子和编辑功能。
旧版键迁移
| 渠道 | 旧版键 | 状态 |
|---|---|---|
| Telegram | streamMode、标量/布尔值 streaming |
由 openclaw doctor --fix 重写为 streaming.mode;运行时不读取 |
| Discord | streamMode、布尔值 streaming |
由 openclaw doctor --fix 重写为 streaming.mode;运行时不读取 |
| Slack | streamMode;布尔值 streaming;旧版 nativeStreaming |
由 openclaw doctor --fix 重写为 streaming.mode(布尔值/旧版形式重写为 streaming.nativeTransport);运行时不读取 |
| Matrix | 标量/布尔值 streaming |
由 openclaw doctor --fix 重写为 streaming.mode(包括 Matrix 的 "quiet" 模式);运行时不读取 |
| Feishu | 布尔值 streaming |
由 openclaw doctor --fix 重写为 streaming.mode;运行时不读取 |
| QQ Bot | 布尔值 streaming;streaming.c2cStreamApi |
由 openclaw doctor --fix 重写为 streaming.mode(布尔值/c2cStreamApi 形式重写为 streaming.nativeTransport);运行时不读取 |
运行时行为
Telegram
- 在私信和群组/话题中使用
sendMessage+editMessageText预览更新;最终文本会直接编辑当前活动预览。Telegram 的 30 秒临时“正在输入”草稿(sendMessageDraft)不用于 回答流式传输。 - 较短的初始预览仍会为推送通知体验进行防抖,但会在 有限延迟后实际显示,因此活动运行不会在视觉上一直保持静默。
- 较长的最终回复会将预览消息复用于第一个分块,并只发送 其余分块。
block模式会在streaming.preview.chunk.maxChars时将预览轮换为新消息(默认 800,上限为 Telegram 的 4096 编辑限制);其他模式会让单个预览增长至最多 4096 个字符。progress模式将工具进度保留在可编辑的状态草稿中;当回答流式传输已激活但尚无工具行可用时, 会实际显示状态标签;完成时清除草稿,并通过常规投递方式 发送最终回答。- 如果在确认完整文本前最终编辑失败,OpenClaw 会使用 常规最终投递并清理过时的预览。
- 当显式启用 Telegram 分块流式传输时,会跳过预览流式传输, 以避免双重流式传输。
/reasoning stream可将推理写入临时预览, 该预览会在最终投递后删除。- Telegram 选定引用回复属于例外:当
replyToMode不是"off"且存在选定的引用文本时,OpenClaw 会跳过该轮的回答预览 流式传输(最终回答必须通过原生引用回复 路径发送),因此无法渲染工具进度预览行。不含选定引用文本的 当前消息回复仍会保留预览流式传输。详情请参阅 Telegram 渠道文档。
Discord
- 使用发送 + 编辑预览消息。
block模式使用草稿分块(draftChunk)。- 当显式启用 Discord 分块流式传输时,会跳过预览流式传输。
progress模式会在最终回答后附加一个简短的-#活动摘要(思考/工具调用 次数和耗时),并在该回答投递后删除状态草稿, 因此繁忙渠道不会在回复上方留下孤立的工具日志。 错误最终回复会保留草稿,作为失败轮次的记录。- 最终媒体、错误和显式回复载荷会取消待处理的预览, 而不刷新新草稿,随后使用常规投递。
Slack
partial可在可用时使用 Slack 原生流式传输(chat.startStream/append/stop)。block使用追加式草稿预览。progress先使用状态预览文本,再发送最终回答。- 没有回复线程的顶层私信使用草稿预览帖子和编辑, 而不是 Slack 原生流式传输。
- 原生和草稿预览流式传输会禁止该轮的分块回复,因此一条 Slack 回复只会通过一种投递路径进行流式传输。
- 最终媒体/错误载荷和进度最终回复不会创建用后即弃的草稿 消息;只有能够编辑预览的文本/分块最终回复才会刷新待处理的 草稿文本。
Mattermost
- 在
partial模式下,将思考过程和部分回复文本流式传输到单个草稿 预览帖子中,并在最终回答可以安全发送时直接完成该帖子。 - 在
progress模式下,将思考过程和工具活动流式传输到单个状态 预览中,并在最终回答可以安全发送时直接完成该预览。 - 在
block模式下,在已完成文本帖子和工具活动帖子之间轮换; 并行和连续的工具更新共享当前工具活动帖子。 - 如果预览帖子已被删除或在最终确定时因其他原因不可用, 则回退为发送新的最终帖子。
- 最终媒体/错误载荷会在常规投递前取消待处理的预览更新, 而不是刷新临时预览帖子。
Matrix
- 当最终文本可以复用预览事件时,草稿预览会直接完成。
- 仅媒体、错误和回复目标不匹配的最终回复会在常规投递前取消待处理的预览 更新;已经可见的过时预览会被撤回。
工具进度预览更新
预览流式传输还可以包含工具进度更新:在工具运行期间、最终回复之前, 同一条预览消息中会显示“正在搜索网页”“正在读取文件”或“正在调用工具”等简短状态 行。在 Codex app-server 模式下,Codex 前言/评注消息使用同一条 预览路径,因此简短的“我正在检查……”进度说明可以流式传输到 可编辑草稿中,而不会成为最终回答的一部分。这样可以使 多步骤工具轮次在第一次思考预览和最终回答之间保持视觉反馈,而非静默无响应。
长时间运行的工具可能会在返回前发出带类型的进度。例如,
web_fetch 启动时会设置一个五秒计时器:如果获取仍在
等待中,预览会显示 Fetching page content...;如果获取在此之前完成或
被取消,则不会发出进度行。之后的最终工具
结果仍会正常投递给模型。
支持的界面:
- 启用预览流式传输时,Discord、Slack、Telegram 和 Matrix 默认会将工具进度和 Codex 前言更新流式传输到实时预览编辑中。Microsoft Teams 在 个人聊天中使用其原生进度流。
- 自
v2026.4.22起,Telegram 发布版本便已启用工具进度预览更新; 保持启用可维持这一已发布行为。 - Mattermost 在
partial和progress模式下将工具活动合并到一个预览帖子中,或在block模式下将其合并到文本块之间的一个工具活动帖子中(见上文)。 - 工具进度编辑遵循当前活动的预览流式传输模式;当预览流式传输为
off或分块流式传输已接管消息时,会跳过这些编辑。 在 Telegram 上,streaming.mode: "off"仅用于最终回复:常规进度消息也会被禁止, 而不是作为独立状态消息投递,但审批提示、媒体载荷和错误仍会 正常路由。 - 若要保留预览流式传输但隐藏工具进度行,请将该渠道的
streaming.preview.toolProgress设置为false(默认值为true)。若要保持工具进度行可见,同时隐藏命令/执行文本, 请将streaming.preview.commandText设置为"status",或将streaming.progress.commandText设置为"status";默认值为"raw", 以维持已发布行为。此策略由使用 OpenClaw 紧凑进度渲染器的 草稿/进度渠道共享,包括 Discord、Matrix、Microsoft Teams、Mattermost、 Slack 草稿预览和 Telegram。若要完全禁用预览编辑,请将streaming.mode设置为off。
进度草稿渲染
进度模式草稿(streaming.progress.*)有大小限制,并可按
渠道配置:
| 键 | 默认值 | 行为 |
|---|---|---|
streaming.progress.maxLines |
8 |
草稿标签下方保留的紧凑进度行数上限 |
streaming.progress.maxLineChars |
120 |
截断前每个紧凑行的字符数上限(识别单词边界) |
streaming.progress.label |
"auto" |
草稿标题;可使用自定义字符串,或使用 false 将其隐藏 |
streaming.progress.labels |
内置池 | label: "auto" 时使用的候选标签 |
评注进度通道
除工具进度外,紧凑进度渲染器还可以在草稿中显示一个额外 通道:
streaming.progress.commentary- 渲染模型在调用工具前的 评注(简短的“我会先检查……然后……”叙述),并与 工具行交错显示在进度草稿中。在进度模式下的 Discord 和 Telegram 中, 即使关闭此可选通道,同一前言也会用作状态标题; 其他渠道保持现有的进度行为。请参阅 进度草稿。
{ "channels": { "discord": { "streaming": { "mode": "progress", "progress": { "commentary": true } } } }}保持进度行可见,但隐藏原始命令/执行文本:
{ "channels": { "telegram": { "streaming": { "mode": "partial", "preview": { "toolProgress": true, "commandText": "status" } } } }}在其他紧凑进度渠道键下使用相同结构,例如
channels.discord、channels.matrix、channels.msteams、
channels.mattermost 或 Slack 草稿预览。对于进度草稿模式,请将
相同策略放在 streaming.progress 下:
{ "channels": { "telegram": { "streaming": { "mode": "progress", "progress": { "toolProgress": true, "commandText": "status" } } } }}