流式传输 + 分块

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

  • 阻止流式传输(通道): 当助手写入时发出完成的。这些是普通通道消息(不是 token 增量)。
  • 预览流式传输(Telegram/Discord/Slack): 生成时更新临时预览消息。

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

阻止流式传输(通道消息)

阻止流式传输在可用时以粗块形式发送助手输出。

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

图例:

  • 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:等待助手消息完成,然后刷新缓冲输出。

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

分块算法(低/高边界)

阻止分块由 EmbeddedBlockChunker 实现:

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

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

合并(合并流式块)

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

  • 合并等待空闲间隙idleMs)刷新。
  • 缓冲区由 maxChars 上限,如果超过会刷新。
  • minChars 防止微小片段发送,直到足够文本积累(最终刷新总是发送剩余文本)。
  • 连接器来自 blockStreamingChunk.breakPreferenceparagraph\n\nnewline\nsentence → 空格)。
  • 通道覆盖通过 *.blockStreamingCoalesce 提供(包括每个账户配置)。
  • 除非覆盖,否则 Signal/Slack/Discord 的默认合并 minChars 提高到 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(默认:true)时切换 Slack 原生流式传输 API 调用。

旧键迁移:

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

运行时行为

Telegram:

  • 使用 Bot API sendMessage + editMessageText
  • 当明确启用 Telegram 阻止流式传输时,跳过预览流式传输(避免双重流式传输)。
  • /reasoning stream 可以将推理写入预览。

Discord:

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

Slack:

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