流式与分块
OpenClaw 有两个独立的流式传输层,并且没有真正的 token 增量流式传输到频道消息:
- 块流式传输(频道): 在助手生成时发出完整的块。这些是正常的频道消息,而不是 token 增量。
- 预览流式传输(Telegram/Discord/Slack/Matrix/Mattermost/MS Teams): 在生成过程中更新临时的预览消息(发送 + 编辑/追加)。
控制界面启动状态¶
当 chat.send 确认一个活跃的运行后,Gateway 可以在助手文本或工具活动可见之前发送一个带类型的、粗略的启动状态。Control UI 会在工作指示器旁边显示此状态,包含工作区准备、环境预置、上下文准备和模型启动等阶段。
第一个助手增量或工具启动会永久替换该运行的启动状态。当工具等待操作员操作时,审批状态优先显示。工作树创建和初始云分发发生在聊天运行存在之前,因此它们运行前的 RPC 进度不会作为运行启动状态呈现;只有当活跃运行对回收的工作器重新进行环境预置时,环境预置才会出现在这里。
块流式传输(频道消息)¶
块流式传输以粗粒度块的形式,在助手输出可用时将其发送。
Model output
└─ text_delta/events
├─ (blockStreamingBreak=text_end)
│ └─ chunker emits blocks as buffer grows
└─ (blockStreamingBreak=message_end)
└─ chunker flushes at message_end
└─ channel send (block replies)
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 写法会被校验拒绝。在启动 Gateway 之前,运行 openclaw doctor --fix 将旧配置迁移为嵌套结构。请参阅 Doctor 迁移指南。
边界语义 用于 blockStreamingBreak:
text_end:只要 chunker 发出块就立即流式传输;在每个text_end时刷新。message_end:等待助手消息结束,然后刷新缓冲的输出。如果缓冲文本超过maxChars,仍会使用 chunker,因此在结束时可以发出多个块。
使用块流式传输的媒体投递¶
当插件使用 before_agent_finalize 来校验内置运行时的答案时,助手回复会延迟到该决策完成。后续答案会取代早期工具回合中延迟的文本,包括最终答案为 NO_REPLY 的情况。这既适用于回复块,也适用于预览更新;它不会撤回已经发送的回复。评论性内容保持实时传输,且对先前用户输入的媒体、推理和已完成答案会被保留。每个被导引的用户输入都会获得自己已投递的答案,即使其待处理工具被跳过。被取代答案中的媒体会在没有旧标题的情况下被投递。
关闭块流式传输时,包含媒体的助手消息仍可在消息边界发送,并附带其标题。预览更新不计为单独投递的标题。
流式媒体必须使用结构化负载字段,如 mediaUrl 或 mediaUrls;流式文本不会被解析为附件命令。当块流式传输提前发送媒体时,OpenClaw 会为该回合记住该投递。如果最终的助手负载重复了相同的媒体 URL,最终投递会去除重复的媒体,而不是再次发送附件。
完全重复的最终负载会被抑制。如果最终负载在已经流式传输的媒体周围添加了不同的文本,OpenClaw 仍会发送新文本,同时保持媒体单次投递。这可以防止在 Telegram 等频道上出现重复的语音消息或文件。
分块算法(低/高边界)¶
块分块由 EmbeddedBlockChunker 实现:
- 低边界: 在缓冲区 >=
minChars之前不发出(除非被强制)。 - 高边界: 优先在
maxChars之前拆分;如果被强制,则在maxChars处拆分。 - 断行偏好链:
paragraph->newline->sentence-> 空白字符 -> 硬换行。 - 代码围栏: 绝不在围栏内部拆分;当在
maxChars处被迫拆分时,关闭并重新打开围栏以保持 Markdown 有效。 - 表格: 能容纳在
maxChars内的 Markdown 表格会保持在一个块中,即使这意味着在低于minChars时于表格之前断开,以便渲染表格的频道能一起看到表头和各行。较大的表格会在行边界处拆分。
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"(最终文本回复;携带媒体的消息仍可在消息边界发送)。
块流式传输遵循 agents.defaults.blockStreamingDefault,除非渠道或账户显式设置 *.streaming.block.enabled。QQ Bot 没有 streaming.block 键,除非 channels.qqbot.streaming.mode 为 "off",否则它会流式传输块回复。渠道可以在没有块回复的情况下流式传输实时预览(channels.<channel>.streaming.mode)。blockStreaming* 默认值位于 agents.defaults 下,而不是配置根。
对于 Discord 和 Telegram,显式配置的非 off 预览模式优先于继承的 agents.defaults.blockStreamingDefault: "on"。当块回复应覆盖其预览时,设置该渠道的 streaming.block.enabled: true。如果某轮次预览不可用,继承的块投递仍然适用。
预览流式传输模式¶
规范键:channels.<channel>.streaming(嵌套 { mode, ... };旧版顶层布尔/字符串写法会被 openclaw doctor --fix 重写)。
| 模式 | 行为 |
|---|---|
off |
禁用预览流式传输 |
partial |
单个预览被最新文本替换 |
block |
预览以分块/追加步骤更新 |
progress |
生成期间显示进度/状态预览,完成时显示最终答案 |
streaming.mode: "block" 是面向支持编辑的渠道(如 Discord 和 Telegram)的预览流式传输模式;它本身不会在这些渠道启用渠道块投递。普通块回复请使用 streaming.block.enabled。Microsoft Teams 是例外:它没有草稿预览块传输,因此 streaming.mode:
"block" 会完全禁用原生流式传输,回复将以普通块投递形式到达,而不是原生 partial/progress 流式传输。Mattermost 也不同:在 block 模式下,它会在已完成文本和工具活动块之间轮换预览,因此较早的块会作为独立帖子保持可见,而不是在一个可编辑草稿中被覆盖。
渠道映射¶
Discord 在 streaming 未设置时默认为 off,Telegram 和 Slack 默认为 progress,Mattermost 和 MS Teams 默认为 partial。
| 渠道 | 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)(nativeTransport默认为true)。- Slack 原生流式传输和 Slack 助手线程状态需要回复线程目标。顶层 DM 不会显示该线程式预览,但仍可使用 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模式会在可编辑的状态草稿中保留工具进度,在答案流式传输已激活但尚无可用工具行时生成状态标签,在完成后清除草稿,并通过正常投递发送最终答案。- 当
channels.telegram.richMessages为true时,计划预览使用原生复选框;否则使用可读的 HTML 清单。已完成的步骤会被勾选,活动步骤会标记为“进行中”。 - 如果在完成文本被确认之前最终编辑失败,OpenClaw 会使用正常最终投递并清理过期的预览。
- 当明确启用 Telegram 块流式传输时,会跳过预览流式传输,以避免双重流式传输。
/reasoning stream可以将推理写入临时预览,该预览会在最终投递后删除。- Telegram 的选定引用回复是例外:当
replyToMode不为"off"且存在选定引用文本时,OpenClaw 会跳过该轮次的回答预览流(最终答案必须通过原生引用回复路径),因此工具进度预览行无法渲染。没有选定引用文本的当前消息回复仍保留预览流式传输。详见 Telegram 渠道文档。
Discord¶
- 使用发送 + 编辑预览消息。
block模式使用草稿分块(draftChunk)。- 当明确启用 Discord 块流式传输时,会跳过预览流式传输。
progress默认保持安静:仅显示标题、作者撰写的评论和推理、计划里程碑以及审批请求。中间工具失败和非零命令退出会被隐藏。相同的默认设置也适用于其他共享进度卡片渲染器;streaming.progress.toolProgress: true会添加带有图标的滚动工具日志。- 当父级让渡给已接受的子代理时,
progress模式可以将其已确认的卡片转移到核心。同一条消息会保留其清单,并接收子级活动和终端更新;最终答案单独发送。详见 子代理让渡交接。 - 如果没有该交接,
progress模式会在最终答案投递后删除状态草稿,因此繁忙渠道不会在回复上方保留孤立的工具日志。错误最终回复会保留草稿作为失败轮次的记录。 - 最终媒体、错误和显式回复负载会取消待处理的预览,而不会刷新新草稿,然后使用正常投递。
Slack¶
- 当
streaming.progress.toolProgress: false时,紧凑进度会保留最新的模型前言。当commentary: true、label: false且maxLines: 1时,它是一条斜体的临时消息,不包含推理、工具图标、命令失败、计划或文件编辑计数器。第一条帖子会等待完整的前言,以便其 Slack 通知可读;后续前言会编辑该消息。可操作的审批请求仍会显示。最终答案是一条新回复,并且只有在 Slack 确认投递后才会删除预览。成功的静默轮次也会删除其预览;显式消息工具帖子会保持持久。 - 当可用时,
partial可以使用 Slack 原生流式传输(chat.startStream/append/stop)。 block使用追加式草稿预览。- 在回复线程中,
progress默认流式传输 Slack 的原生代理卡片:一条消息承载叙述、实时计划卡片(作者撰写的里程碑,或在streaming.progress.toolProgress: true为每个工具调用提供一行之前的一行工作摘要)以及最终答案。常规进度更新按一秒间隔合并;需关注和完成会立即刷新。卡片仅出现在执行实际工作的轮次中,因此普通问题会在没有卡片的情况下得到回答。streaming.progress.nativeTaskCards: false会回退到 Block Kit 会话卡片,该卡片会最终化为成功或错误,并将助手的最终文本作为单独消息发布。 - 仅当会话确实可打开时,卡片才会包含 在 OpenClaw 中打开:
gateway.publicOrigin已设置且gateway.controlUi.enabled不为false。 - 在没有回复线程的情况下,默认
progress轮次仅保留最终答案,并在工作期间使用临时的hourglass_flowing_sand正在输入表情回应。已配置的typingReaction优先;""会禁用它。任何显式的streaming.progress设置都会让顶层轮次启用预览,包括commentary: true或自定义label。唯一例外是nativeTaskCards: true,它仅影响线程。空的进度设置保持安静。该规则使用合并后的根设置和账户设置。这包括普通的顶层私聊;Agent View 和 Assistant View 保留其线程行为。显式的off、partial和block模式保持不变。 - 原生和草稿预览流式传输会抑制该轮次的块回复,因此 Slack 回复仅通过一条投递路径流式传输。
- 没有可见回复的成功轮次仍会删除其草稿卡片。失败的无回复轮次会保留卡片并处于错误状态。
Mattermost¶
- 在
partial模式下,会将思考内容和部分回复文本流式写入一条草稿预览帖子,并在最终答案可以安全发送时就地定稿。 - 在
progress模式下,会将思考内容和工具活动流式写入一条状态预览,并在最终答案可以安全发送时就地定稿。 - 在
block模式下,会在已完成文本帖子和工具活动帖子之间轮换;并行和连续的工具更新共享当前工具活动帖子。 - 如果预览帖子在定稿时被删除或不可用,则回退为发送一条新的最终帖子。
- 最终媒体/错误负载会在正常投递前取消待处理的预览更新,而不是发送一个临时预览帖子。
Matrix¶
- 当最终文本可以复用预览事件时,草稿预览会就地定稿。
- 仅媒体、错误以及回复目标不匹配的最终消息会在正常投递前取消待处理的预览更新;已可见的过期预览会被撤回。
工具进度预览更新¶
预览流式传输还可以包含工具进度更新:例如“正在搜索网络”、“正在读取文件”或“正在调用工具”等简短状态行,它们会在工具运行期间出现在同一条预览消息中,并先于最终回复出现。 在 Codex app-server 模式下,Codex 前言/旁白消息使用相同的预览路径,因此简短的“我正在检查……”进度说明可以流式写入可编辑草稿,而不会成为最终答案的一部分。这使多步工具轮次在首次思考预览和最终答案之间保持视觉上的活跃,而不是静默。
Responses 旁白会在工具交接过程中保持每个消息项的身份。 后续工具更新不会将较早的旁白作为额外的合并前言重放。
长时间运行的工具可能在返回前发出类型化进度。例如,
web_fetch 启动时会设置一个五秒计时器:如果获取仍在等待中,
预览会显示 Fetching page content...;如果获取在此之前完成或
取消,则不会发出进度行。稍后的最终工具
结果仍会正常投递给模型。
支持的平台:
- 当预览流式传输处于活动状态时,Discord、Slack、Telegram 和 Matrix 默认会将工具进度和 Codex 前言更新流式写入实时预览编辑。Microsoft Teams 在个人聊天中使用其原生进度流。
- Telegram 自
v2026.4.22起已启用工具进度预览更新;保持启用可保留该已发布行为。 - Mattermost 在
partial和progress模式下将工具活动合并到一条预览帖子中,或在block模式下在文本块之间使用一条工具活动帖子(见上文)。 - 工具进度编辑遵循当前活动的预览流式传输模式;当预览流式传输为
off或块流式传输已接管 消息时,它们会被跳过。在 Telegram 上,streaming.mode: "off"表示仅最终消息:通用 进度闲聊也会被抑制,而不是作为独立状态消息投递,而审批提示、媒体负载和错误仍会正常 路由。 streaming.preview.toolProgress(默认true)控制partial和block预览中的工具进度 行;streaming.progress.toolProgress(默认false)让progress草稿加入滚动工具日志。若要 保留工具进度行可见,同时隐藏命令/执行文本, 请将streaming.preview.commandText或streaming.progress.commandText设置为"status"(默认值)。将任一选项设置为"raw"可启用命令 文本。此策略由使用 OpenClaw 紧凑进度渲染器的草稿/进度通道 共享,包括 Discord、Matrix、 Microsoft Teams、Mattermost、Slack 会话卡片和 Telegram。若要完全禁用 预览编辑,请将streaming.mode设置为off。
进度草稿渲染¶
进度卡片 会在启用计划更新的 partial、block 和 progress 预览中替换完整计划状态。
可见步骤遵循通道的行数限制。清除卡片会移除其
清单和状态,同时保留其他活动;若草稿除此之外为空,则在通道支持删除时删除该草稿。Microsoft Teams 会用其进度标签替换已清除的
临时预览。当 streaming.progress.label: false 时,
Teams 会保留临时预览,直到下一次更新或最终回复。失败或被
阻止的写入会保留之前的计划。活动预览会保留一条安全的
失败通知。
进度模式草稿(streaming.progress.*)具有以下按通道设置:
| 键 | 默认值 | 行为 |
|---|---|---|
streaming.progress.maxLines |
8 |
草稿标签下方保留的最大紧凑进度行数 |
streaming.progress.maxLineChars |
120 |
截断前每行紧凑文本的最大字符数(按词感知) |
streaming.progress.label |
"auto" |
草稿标题;自定义字符串,或 false 以隐藏 |
streaming.progress.labels |
内置池 | 当 label: "auto" 时使用的候选标签 |
Slack 始终将进度模式渲染为其固定的会话卡片布局;这些 限制仍然约束该卡片内的活动行和计划文本。
旁白进度通道¶
除工具进度外,紧凑进度渲染器还可以在草稿中显示另一个通道:
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 草稿预览。对于 progress-draft 模式,请将相同策略放在 streaming.progress 下:
{
"channels": {
"telegram": {
"streaming": {
"mode": "progress",
"progress": {
"toolProgress": true,
"commandText": "status"
}
}
}
}
}
相关¶
本页原文 Markdown:在 AtomGit 查看·内容源自开源项目 cl/openclaw