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:模型流事件(对于非流式模型可能稀疏)。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:等待助手消息完成,然后刷新缓冲输出。
message_end 仍然使用 chunker,如果缓冲文本超过 maxChars,因此可以在末尾发出多个块。
分块算法(低/高边界)
块分块由 EmbeddedBlockChunker 实现:
- 低边界: 不要发出,直到缓冲区 >=
minChars(除非强制)。 - 高边界: 喜欢在
maxChars之前分割;如果强制,在maxChars分割。 - 中断偏好:
paragraph→newline→sentence→whitespace→ 硬中断。 - 代码围栏: 永远不要在围栏内分割;当在
maxChars强制时,关闭 + 重新打开围栏以保持 Markdown 有效。
maxChars 被限制为频道 textChunkLimit,所以你不能超过每个频道的上限。
合并(合并流式块)
当启用块流式传输时,OpenClaw 可以合并连续的块块在发送之前。这减少了"单行垃圾邮件",同时仍提供渐进式输出。
- 合并等待空闲间隔(
idleMs) before flushing. - 缓冲区由
maxChars限制,如果超过则刷新。 minChars防止微小片段发送,直到积累足够的文本(最终刷新总是发送剩余文本)。- 连接符源自
blockStreamingChunk.breakPreference(paragraph→\n\n,newline→\n,sentence→ 空格)。 - 频道覆盖可通过
*.blockStreamingCoalesce获得(包括每个账户配置)。 - 默认合并
minChars对于 Signal/Slack/Discord 提升到 1500,除非覆盖。
块之间的人类节奏
当启用块流式传输时,你可以在块回复之间添加随机暂停(第一个块之后)。这使得多气泡响应感觉更自然。
- 配置:
agents.defaults.humanDelay(通过agents.list[].humanDelay覆盖每个 agent)。 - 模式:
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时切换 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使用状态预览文本,然后最终答案。