Streaming + 分块

OpenClaw 有两个独立的 “Streaming” 层:

  • 块 Streaming (Channel): 在助手写入时发出完整的。这些是普通的 Channel 消息(不是 Token 增量)。
  • 类 Token Streaming (仅 Telegram): 在生成时用部分文本更新草稿气泡;最终消息在结束时发送。

目前没有真正的 Token Streaming 到外部 Channel 消息。Telegram 草稿 Streaming 是唯一的部分流式传输界面。

块 Streaming (Channel 消息)

块 Streaming 在助手输出可用时以粗粒度块的形式发送。

模型输出
  └─ text_delta/events
       ├─ (blockStreamingBreak=text_end)
       │    └─ chunker 在缓冲区增长时发出块
       └─ (blockStreamingBreak=message_end)
            └─ chunker 在 message_end 时刷新
                   └─ channel send (块回复)

图例:

  • text_delta/events: 模型流事件(对于非流式模型可能稀疏)。
  • chunker: EmbeddedBlockChunker 应用最小/最大边界 + 断点偏好。
  • channel send: 实际的出站消息(块回复)。

控制选项:

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

边界语义:

  • text_end: 一旦 chunker 发出就流式传输块;在每个 text_end 时刷新。
  • message_end: 等到助手消息完成,然后刷新缓冲输出。

message_end 如果缓冲文本超过 maxChars,仍会使用 chunker,所以它可以在最后发出多个块。

分块算法(下限/上限)

块分块由 EmbeddedBlockChunker 实现:

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

maxChars 被限制在 Channel 的 textChunkLimit,所以你不能超过每个 Channel 的上限。

合并(合并流式块)

当启用块 Streaming 时,OpenClaw 可以在发送前合并连续的块。这减少了”单行垃圾信息”,同时仍提供渐进式输出。

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

块之间的类人节奏

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

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

“流式传输块或全部”

这对应于:

  • 流式传输块: blockStreamingDefault: "on" + blockStreamingBreak: "text_end" (边生成边发出)。非 Telegram Channel 还需要 *.blockStreaming: true
  • 在结束时流式传输全部: blockStreamingBreak: "message_end" (一次刷新,如果很长可能有多个块)。
  • 无块 Streaming: blockStreamingDefault: "off" (仅最终回复)。

Channel 注意: 对于非 Telegram Channel,块 Streaming 默认关闭,除非 *.blockStreaming 显式设置为 true。Telegram 可以在没有块回复的情况下流式传输草稿(channels.telegram.streamMode)。

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

Telegram 草稿 Streaming (类 Token)

Telegram 是唯一具有草稿 Streaming 的 Channel:

  • 带主题的私聊中使用 Bot API sendMessageDraft
  • channels.telegram.streamMode: "partial" | "block" | "off"
    • partial: 用最新的流文本更新草稿。
    • block: 以分块块更新草稿(相同的 chunker 规则)。
    • off: 无草稿 Streaming。
  • 草稿块配置(仅用于 streamMode: "block"): channels.telegram.draftChunk (默认: minChars: 200, maxChars: 800)。
  • 草稿 Streaming 与块 Streaming 分离;块回复默认关闭,仅通过非 Telegram Channel 上的 *.blockStreaming: true 启用。
  • 最终回复仍然是普通消息。
  • /reasoning stream 将推理写入草稿气泡(仅 Telegram)。

当草稿 Streaming 激活时,OpenClaw 禁用该回复的块 Streaming 以避免双重流式传输。

Telegram (私聊 + 主题)
  └─ sendMessageDraft (草稿气泡)
       ├─ streamMode=partial → 更新最新文本
       └─ streamMode=block   → chunker 更新草稿
  └─ final reply → 普通消息

图例:

  • sendMessageDraft: Telegram 草稿气泡(不是真实消息)。
  • final reply: 普通 Telegram 消息发送。