跳转到内容

流式传输与分块

OpenClaw 有两个独立的流式传输层:

  • 块流式传输(频道): 当助手写入时发送已完成的。这些是普通的频道消息(不是 token 增量)。
  • 预览流式传输(Telegram/Discord/Slack): 在生成过程中更新临时的预览消息

目前,向频道消息没有真正的 token 增量流式传输。预览流式传输是基于消息的(发送 + 编辑/追加)。

块流式传输会在助手输出可用时,以粗粒度的块形式发送。

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:模型流式事件(对于非流式模型可能较为稀疏)。
  • chunkerEmbeddedBlockChunker 应用最小/最大边界 + 断点偏好。
  • channel send:实际出站消息(块回复)。

控制项:

  • agents.defaults.blockStreamingDefault"on"/"off"(默认关闭)。
  • 频道覆盖:*.blockStreaming(以及按帐户变体)可强制每个频道使用 "on"/"off"
  • agents.defaults.blockStreamingBreak"text_end""message_end"
  • agents.defaults.blockStreamingChunk{ minChars, maxChars, breakPreference? }
  • agents.defaults.blockStreamingCoalesce{ minChars?, maxChars?, idleMs? }(在发送前合并流式块)。
  • 频道硬上限:*.textChunkLimit(例如 channels.whatsapp.textChunkLimit)。
  • 频道分块模式:*.chunkModelength 默认,newline 在长度分块前于空行(段落边界)处分割)。
  • Discord 软上限:channels.discord.maxLinesPerMessage(默认为 17)分割高大的回复以避免 UI 裁剪。

边界语义:

  • text_end:在分块器发出时立即流式传输块;在每个 text_end 上刷新。
  • message_end:等待直到助手消息完成,然后刷新缓冲输出。

message_end 如果缓冲文本超过 maxChars 仍会使用分块器,因此它可以在末尾发出多个块。

块分块由 EmbeddedBlockChunker 实现:

  • **低边界:**在缓冲区 >= minChars 之前不发出(除非强制)。
  • 上限: 优先在 maxChars 之前拆分;如果被迫拆分,则在 maxChars 处拆分。
  • 中断偏好: paragraphnewlinesentencewhitespace → 硬中断。
  • 代码围栏: 永远不在围栏内拆分;如果在 maxChars 处被迫拆分,则关闭并重新打开围栏以保持 Markdown 有效。

maxChars 被限制为频道 textChunkLimit,因此你不能超过每个频道的上限。

启用块流式传输时,OpenClaw 可以在发送之前合并连续的块块。 这减少了“单行刷屏”,同时仍然提供 渐进式输出。

  • 合并会在刷新前等待 空闲间隙 (idleMs)。
  • 缓冲区受 maxChars 限制,如果超过该限制则会刷新。
  • minChars 防止发送微小的片段,直到积累足够的文本(最终刷新总是会发送剩余的文本)。
  • 连接符派生自 blockStreamingChunk.breakPreference (paragraph\n\n, newline\n, sentence → 空格)。
  • 可以通过 *.blockStreamingCoalesce 进行频道覆盖(包括每个账户的配置)。
  • 默认合并 minChars 对于 Signal/Slack/Discord 被提升至 1500,除非被覆盖。

启用分块流式传输后,您可以在块回复之间添加随机暂停(在第一个块之后)。这使得多气泡响应感觉更自然。

  • 配置:agents.defaults.humanDelay(通过 agents.list[].humanDelay 对每个代理进行覆盖)。
  • 模式:off(默认),natural(800–2500ms),customminMs/maxMs)。
  • 仅适用于块回复,不适用于最终回复或工具摘要。

这对应于:

  • 流式分块: blockStreamingDefault: "on" + blockStreamingBreak: "text_end" (即时发送)。非 Telegram 频道还需要 *.blockStreaming: true
  • 在结束时流式传输所有内容: blockStreamingBreak: "message_end"(刷新一次,如果非常长则可能是多个分块)。
  • 无分块流式传输: blockStreamingDefault: "off"(仅最终回复)。

频道注意: 除非 *.blockStreaming 被显式设置为 true,否则分块流式传输是关闭的。频道可以流式传输实时预览 (channels.<channel>.streaming) 而无需块回复。

配置位置提醒:blockStreaming* 默认值位于 agents.defaults 下,而不是根配置中。

规范键:channels.<channel>.streaming

模式:

  • off:禁用预览流式传输。
  • partial:会被最新文本替换单个预览。
  • block:预览更新以分块/追加步骤进行。
  • progress:生成过程中的进度/状态预览,完成时的最终答案。
通道offpartialblockprogress
Telegram映射到 partial
Discord映射到 partial
Slack

Slack 专用:

  • channels.slack.nativeStreamingstreaming=partial 时切换 Slack 原生流式 API 调用(默认:true)。

旧键迁移:

  • Telegram:streamMode + 布尔值 streaming 自动迁移到 streaming 枚举。
  • Discord:streamMode + 布尔值 streaming 自动迁移到 streaming 枚举。
  • Slack:streamMode 自动迁移到 streaming 枚举;布尔值 streaming 自动迁移到 nativeStreaming

Telegram:

  • 在私信和群组/主题中使用 sendMessage + editMessageText 预览更新。
  • 当明确启用 Telegram 分块流式传输时,将跳过预览流式传输(以避免重复流式传输)。
  • /reasoning stream 可以将推理内容写入预览。

Discord:

  • 使用发送 + 编辑预览消息。
  • block 模式使用草稿分块(draftChunk)。
  • 当明确启用 Discord 分块流式传输时,将跳过预览流式传输。

Slack:

  • partial 在可用时可以使用 Slack 原生流式传输(chat.startStream/append/stop)。
  • block 使用附加式草稿预览。
  • progress 使用状态预览文本,然后是最终答案。
  • Messages — 消息生命周期与传递
  • Retry — 传递失败时的重试行为
  • Channels — 每个渠道的流式传输支持