Streaming + chunking
Moltbot 有两层彼此独立的“streaming”:
- Block streaming(channels):当 assistant 生成内容时,按完成的 blocks 逐段发出。这些是普通的渠道消息(不是 token deltas)。
- Token-ish streaming(仅 Telegram):在生成过程中,用部分文本更新一个 draft bubble;结束时再发送最终消息。
目前对外部渠道消息来说,并不存在真正的 token streaming。Telegram 的 draft streaming 是唯一的“部分流式”对外表面。
Block streaming(渠道消息)
Block streaming 会把 assistant 输出以较粗的分块方式在可用时发送出去。
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:模型的流事件(对非 streaming 模型可能很稀疏)。chunker:EmbeddedBlockChunker,应用 min/max 边界 + break 偏好。channel send:实际向外发送的消息(block replies)。
控制项:
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? }(发送前合并流式 blocks)。- 渠道硬上限:
*.textChunkLimit(例如channels.whatsapp.textChunkLimit)。 - 渠道分块模式:
*.chunkMode(默认length;newline会先按空行(段落边界)拆分,再做长度分块)。 - Discord 软上限:
channels.discord.maxLinesPerMessage(默认 17)会拆分“过高”的回复以避免 UI 裁切。
边界语义:
text_end:chunker 一产出 block 就立刻发送;每个text_end都会触发 flush。message_end:等 assistant 消息完成后,再一次性 flush 缓冲内容。
即使使用 message_end,当缓冲文本超过 maxChars 时仍会使用 chunker,因此可能在结尾一次性发出多个 chunks。
分块算法(低/高边界)
Block chunking 由 EmbeddedBlockChunker 实现:
- 低边界(Low bound):buffer <
minChars时不输出(除非强制)。 - 高边界(High bound):尽量在
maxChars之前找分割点;若被迫则在maxChars硬切。 - 断点偏好(Break preference):
paragraph→newline→sentence→whitespace→ 硬断。 - 代码围栏(Code fences):绝不在围栏内部拆分;当被迫在
maxChars处硬切时,会先关闭再重开围栏,以保持 Markdown 合法。
maxChars 会被 clamp 到渠道的 textChunkLimit,因此不能超过每渠道上限。
Coalescing(合并连续 blocks)
当启用 block streaming 时,Moltbot 可以在发送前合并连续的 block chunks。 这能减少“一行一条消息”的刷屏,同时仍提供渐进式输出。
- Coalescing 会等待空闲间隔(
idleMs)后再 flush。 - buffer 有
maxChars上限,超过就会立即 flush。 minChars会阻止太小的碎片被发送,直到累积到足够长度(最终 flush 总会发送剩余文本)。- 连接符(joiner)来自
blockStreamingChunk.breakPreference(paragraph→\n\n,newline→\n,sentence→ 空格)。 - 渠道也可通过
*.blockStreamingCoalesce(含按账号配置)覆盖。 - 对 Signal/Slack/Discord,默认 coalesce 的
minChars会提升到 1500(除非覆盖)。
blocks 之间更像人类的节奏
启用 block streaming 后,你可以在 block replies 之间加入随机暂停(从第二个 block 开始)。这会让多气泡回复更自然。
- 配置:
agents.defaults.humanDelay(可在agents.list[].humanDelay里按 agent 覆盖)。 - 模式:
off(默认)、natural(800–2500ms)、custom(minMs/maxMs)。 - 只作用于 block replies,不作用于最终回复或 tool summaries。
“流式分块还是一次性发完”
其对应关系为:
- 流式分块(Stream chunks):
blockStreamingDefault: "on"+blockStreamingBreak: "text_end"(边生成边发)。非 Telegram 渠道还需要显式设置*.blockStreaming: true。 - 末尾一次性发完(Stream everything at end):
blockStreamingBreak: "message_end"(结尾 flush 一次;很长时可能分多个 chunks)。 - 不做 block streaming:
blockStreamingDefault: "off"(只发最终回复)。
**渠道注意:**对非 Telegram 渠道,除非显式设置 *.blockStreaming 为 true,否则 block streaming 默认关闭。Telegram 可以在不发送 block replies 的情况下进行 draft streaming(channels.telegram.streamMode)。
配置位置提示:blockStreaming* 的 defaults 位于 agents.defaults,而不是根配置。
Telegram draft streaming(token-ish)
Telegram 是唯一支持 draft streaming 的渠道:
- 在带 topics 的私聊中使用 Bot API
sendMessageDraft。 channels.telegram.streamMode: "partial" | "block" | "off"。partial:用最新的流文本更新 draft。block:以分块 blocks 的方式更新 draft(同一套 chunker 规则)。off:不启用 draft streaming。
- Draft 分块配置(仅
streamMode: "block"时生效):channels.telegram.draftChunk(默认:minChars: 200,maxChars: 800)。 - Draft streaming 与 block streaming 相互独立;block replies 默认关闭,且对非 Telegram 渠道仅在
*.blockStreaming: true时启用。 - 最终回复仍然是普通消息。
/reasoning stream会把 reasoning 写入 draft bubble(仅 Telegram)。
当 draft streaming 处于激活状态时,Moltbot 会为该条回复禁用 block streaming,以避免双重 streaming。
Telegram (private + topics)
└─ sendMessageDraft (draft bubble)
├─ streamMode=partial → update latest text
└─ streamMode=block → chunker updates draft
└─ final reply → normal message图例:
sendMessageDraft:Telegram 的 draft bubble(不是真实消息)。final reply:普通 Telegram 消息发送。