Appearance
流式输出与分块(Streaming + Chunking)
OpenClaw 有两套独立的流式层:
- 区块流式(Block Streaming):助手写作过程中以完整的区块为单位向频道发送消息,是正常的频道消息(不是 token 增量)。
- 预览流式(Preview Streaming,适用于 Telegram / Discord / Slack):生成过程中更新一条临时预览消息。
目前没有真正的 token 级 delta 流式推送到频道消息。预览流式基于消息(发送 + 编辑/追加)实现。
区块流式(频道消息)
区块流式在输出可用时以粗粒度分块发送助手内容。
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"(默认 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 当缓冲文本超过 maxChars 时仍会使用 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按 agent 覆盖)。 - 模式:
off(默认)、natural(800–2500ms)、custom(minMs/maxMs)。 - 仅适用于区块回复,不影响最终回复或工具摘要。
"流式分块 vs 全量发送"映射
- 流式分块:
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:
- 在私聊和群组/话题中使用
sendMessage+editMessageText更新预览。 - 当 Telegram 区块流式显式开启时跳过预览流式(避免双重流式)。
/reasoning stream可将推理过程写入预览。
Discord:
- 使用发送 + 编辑方式更新预览消息。
block模式使用草稿分块(draftChunk)。- 当 Discord 区块流式显式开启时跳过预览流式。
Slack:
partial可使用 Slack 原生流式(chat.startStream/append/stop)。block使用追加式草稿预览。progress使用状态预览文本,完成后发出最终答案。