Chunk Aggregator

LLM Streaming 分层架构中的关键中间层——将模型输出的原始 token 聚合为更有语义意义的 chunk(按标点、Markdown 块、function call 边界等),解决 char-level split 问题。

#type / concept #status / evergreen #tech / ai #tech / architecture

[!info] related notes

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,前端在浏览器端聚合。但推荐在后端做,减少网络传输量。
创建于 2026/6/29 更新于 2026/7/15