Chunk Aggregator
LLM Streaming 分层架构中的关键中间层——将模型输出的原始 token 聚合为更有语义意义的 chunk(按标点、Markdown 块、function call 边界等),解决 char-level split 问题。
#type / concept
#status / evergreen
#tech / ai
#tech / architecture
[!info] related notes
- 所属 MOC: LLM Streaming MOC
- 架构位置: LLM Streaming 分层架构
- 上游: LLM 输出四层模型(第 1 层 raw tokens)
- 下游: Semantic Router
- 相关: Function Calling 流式累积
Chunk Aggregator
一句话定义
Chunk Aggregator 是 LLM Streaming 分层架构中的关键中间层——将模型输出的原始 token 聚合为更有语义意义的 chunk,解决 char-level split(逐字拆分)问题,是决定流式体验好坏的核心环节。
核心机制
为什么需要这一层
模型输出的原始 token 粒度太细:
token: "你" → 单独发送 = 逐字拆分,体验差
token: "好"
token: "世"
token: "界"
token: "。"
Chunk Aggregator 将它们聚合为语义单元:
chunk: "你好世界。" → 按句号聚合,体验好
聚合策略
按标点聚合
最常见的策略。遇到句号、感叹号、问号、换行符时发送 chunk。
"你好" + "世界" + "。" → "你好世界。"
按 Markdown 块聚合
适合需要渲染 Markdown 的场景。代码块、列表、标题等作为完整 chunk 发送。
"```python\n" + "print('hello')\n" + "```" → 整个代码块作为一个 chunk
按 function call boundary 聚合
工具调用的开始/结束作为 chunk 边界。
text delta → text chunk
[tool_call 开始] → tool_call chunk(切换通道)
tool_call delta → 累积到 tool_call chunk
[tool_call 结束] → 完整 tool_call chunk
按时间窗口聚合
每隔 N 毫秒发送一次累积的 chunk。适合需要控制帧率的场景。
let buffer = ""
setInterval(() => {
if (buffer) {
emit(buffer)
buffer = ""
}
}, 50) // 每 50ms 发送一次
按长度聚合
累积到一定字符数后发送。
if (buffer.length >= 10) {
emit(buffer)
buffer = ""
}
实现示例
class ChunkAggregator {
private buffer = ""
private flushTimeout: NodeJS.Timeout | null = null
addToken(token: string) {
this.buffer += token
// 按标点切分
if (/[。!?\n]/.test(token)) {
this.flush()
}
}
private flush() {
if (this.buffer) {
this.onChunk(this.buffer)
this.buffer = ""
}
}
onChunk: (chunk: string) => void
}
最小场景
从”逐字显示”升级为”逐句显示”:
// ❌ 没有 Chunk Aggregator:逐字显示
onToken(token => render(buffer += token))
// ✅ 有 Chunk Aggregator:逐句显示
onChunk(chunk => render(buffer += chunk))
char-level split 问题
如果前端出现每个字符单独显示(不是流畅的打字效果,而是一顿一顿的),问题不在 SSE 本身,而是 Chunk Aggregator 层没有正确聚合——后端直接把每个 token 透传给了前端。
修复方法:在后端 stream 处理层加入 Chunk Aggregator,按语义边界聚合后再发送。
边界与易混淆点
- Chunk Aggregator 不是必须的:简单场景可以直接透传 token,但体验通常不好。
- 聚合粒度需要平衡:太细(逐字)体验差,太粗(整段)延迟高。
- 和 Function Calling 流式累积的关系:Function Calling 的 arguments 累积也是一种 chunk aggregation,只是聚合的是 tool_call 的 JSON 碎片。
- 可以在前端做:后端发送 token,前端在浏览器端聚合。但推荐在后端做,减少网络传输量。