Streaming and Chunking

Streaming + chunking

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

  • 块流式传输(频道): 助手写入时发出完成的。这些是正常的频道消息(不是令牌增量)。
  • 预览流式传输(Telegram/Discord/Slack): 在生成时更新临时预览消息

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

块流式传输(频道消息)

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

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:一旦 chunker 发出就流式传输块;在每个 text_end 刷新。
  • message_end:等待助手消息完成,然后刷新缓冲输出。

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

分块算法(低/高边界)

块分块由 EmbeddedBlockChunker 实现:

  • 低边界: 不要发出,直到缓冲区 >= minChars(除非强制)。
  • 高边界: 喜欢在 maxChars 之前分割;如果强制,在 maxChars 分割。
  • 中断偏好: paragraphnewlinesentencewhitespace → 硬中断。
  • 代码围栏: 永远不要在围栏内分割;当在 maxChars 强制时,关闭 + 重新打开围栏以保持 Markdown 有效。

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

合并(合并流式块)

当启用块流式传输时,OpenClaw 可以合并连续的块块在发送之前。这减少了"单行垃圾邮件",同时仍提供渐进式输出。

  • 合并等待空闲间隔idleMs) before flushing.
  • 缓冲区由 maxChars 限制,如果超过则刷新。
  • minChars 防止微小片段发送,直到积累足够的文本(最终刷新总是发送剩余文本)。
  • 连接符源自 blockStreamingChunk.breakPreferenceparagraph\n\nnewline\nsentence → 空格)。
  • 频道覆盖可通过 *.blockStreamingCoalesce 获得(包括每个账户配置)。
  • 默认合并 minChars 对于 Signal/Slack/Discord 提升到 1500,除非覆盖。

块之间的人类节奏

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

  • 配置:agents.defaults.humanDelay(通过 agents.list[].humanDelay 覆盖每个 agent)。
  • 模式: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:

  • 在 DM 中使用 Bot API sendMessageDraft(当可用时),在群组/主题预览更新中使用 sendMessage + editMessageText
  • 当显式启用 Telegram 块流式传输时,跳过预览流式传输(以避免双重流式传输)。
  • /reasoning stream 可以将推理写入预览。

Discord:

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

Slack:

  • partial 在可用时可以使用 Slack 本机流式(chat.startStream/append/stop)。
  • block 使用追加式草稿预览。
  • progress 使用状态预览文本,然后最终答案。