流式传输 + 分块
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:模型流事件(非流式模型可能稀疏)。chunker:EmbeddedBlockChunker,应用最小/最大边界 + 断开偏好。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)。 - 通道分块模式:
*.chunkMode(length默认,newline在长度分块前按空白行(段落边界)拆分)。 - Discord 软上限:
channels.discord.maxLinesPerMessage(默认 17)拆分高回复以避免 UI 裁剪。
边界语义:
text_end:一旦 chunker 发出块就流式传输;在每个text_end刷新。message_end:等待助手消息完成,然后刷新缓冲输出。
如果缓冲文本超过 maxChars,message_end 仍使用 chunker,因此可以在结尾发出多个块。
分块算法(低/高边界)
阻止分块由 EmbeddedBlockChunker 实现:
- 低边界: 直到缓冲区 >=
minChars才发出(除非强制)。 - 高边界: 优先在
maxChars前拆分;如被迫,在maxChars拆分。 - 断开偏好:
paragraph→newline→sentence→whitespace→ 硬断开。 - 代码围栏: 永远不在围栏内拆分;如果在
maxChars强制拆分,关闭 + 重新打开围栏以保持 Markdown 有效。
maxChars 被限制为通道 textChunkLimit,因此你不能超过每个通道的上限。
合并(合并流式块)
启用阻止流式传输时,OpenClaw 可以在发送前合并连续块。这减少了”单行刷屏”,同时仍提供渐进式输出。
- 合并等待空闲间隙(
idleMs)刷新。 - 缓冲区由
maxChars上限,如果超过会刷新。 minChars防止微小片段发送,直到足够文本积累(最终刷新总是发送剩余文本)。- 连接器来自
blockStreamingChunk.breakPreference(paragraph→\n\n、newline→\n、sentence→ 空格)。 - 通道覆盖通过
*.blockStreamingCoalesce提供(包括每个账户配置)。 - 除非覆盖,否则 Signal/Slack/Discord 的默认合并
minChars提高到 1500。
块之间的人性化节奏
启用阻止流式传输时,你可以在块回复之间添加随机暂停(第一个块之后)。这使多气泡响应感觉更自然。
- 配置:
agents.defaults.humanDelay(通过agents.list[].humanDelay按代理覆盖)。 - 模式:
off(默认)、natural(800–2500ms)、custom(minMs/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:生成期间的进度/状态预览,完成时提供最终答案。
通道映射
| 通道 | off | partial | block | progress |
|---|---|---|---|---|
| Telegram | ✅ | ✅ | ✅ | 映射到 partial |
| Discord | ✅ | ✅ | ✅ | 映射到 partial |
| Slack | ✅ | ✅ | ✅ | ✅ |
仅 Slack:
channels.slack.nativeStreaming在streaming=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使用状态预览文本,然后提供最终答案。