LLM Streaming 分层架构
生产级 LLM Streaming 系统的五层架构:Token Stream → Chunk Aggregator → Semantic Router → Event Bus → Frontend State Manager,各层职责清晰,可独立演进。
#type / synthesis
#status / evergreen
#tech / ai
#tech / architecture
[!info] related notes
- 所属 MOC: LLM Streaming MOC
- 各层概念: Chunk Aggregator, Semantic Router
- 输出模型: LLM 输出四层模型
- 协议设计: Delta Stream, Append Event Stream
- 前端消费: 前端 SSE 消费
- SSE 管道: 多服务 SSE 管道
LLM Streaming 分层架构
范围
描述生产级 LLM Streaming 系统的完整分层架构,从模型 token 输出到前端 UI 渲染的全链路。
为什么要分层
一个简单的 SSE demo 可以不做分层——直接把 LLM token 透传给前端。但生产系统需要:
- 聚合:token 太细,需要聚合成语义 chunk
- 分流:输出不只有文本,还有工具调用、结构化数据、推理过程
- 统一协议:前端、日志、监控、回放系统需要统一的事件格式
- 状态管理:前端需要集中管理流式状态,而不是散落在各组件
五层架构
┌─────────────────────────────────┐
│ ① Token Stream Layer │ 模型层:接收原始 token
│ (OpenAI / Claude / Qwen) │
└──────────────┬──────────────────┘
↓
┌─────────────────────────────────┐
│ ② Chunk Aggregator │ 聚合层:token → 语义 chunk
│ (按标点/Markdown/tool边界) │
└──────────────┬──────────────────┘
↓
┌─────────────────────────────────┐
│ ③ Semantic Router │ 路由层:按类型分流输出
│ (text/tool/thinking/cite) │
└──────────────┬──────────────────┘
↓
┌─────────────────────────────────┐
│ ④ Event Bus (SSE/WebSocket) │ 传输层:统一协议输出
│ (Structured Event Stream) │
└──────────────┬──────────────────┘
↓
┌─────────────────────────────────┐
│ ⑤ Frontend State Manager │ 状态层:管理 UI 状态
│ (Zustand / Redux / XState) │
└─────────────────────────────────┘
各层职责详解
① Token Stream Layer(模型层)
职责:接收 LLM Provider 的原始 token 流。
输入:OpenAI / Claude / Qwen 等 provider 的 streaming API
输出:
token: "你"
token: "好"
关键点:
- 不同 provider 的 token 粒度不同
- 有些 provider 已经做了初步聚合
- 这一层通常由 LLM Provider SDK 封装
② Chunk Aggregator(聚合层)
职责:将 token 聚合为更有语义意义的 chunk。
聚合策略:
- 按标点(句号、换行)
- 按 Markdown 块(代码块、列表)
- 按 function call boundary
- 按时间窗口(每 N ms 发送一次)
- 按长度(累积 N 个字符后发送)
输出:
chunk: "你好世界。"
详见 Chunk Aggregator。
③ Semantic Router(路由层)
职责:根据内容类型将输出分流到不同处理通道。
分流目标:
| 通道 | 目标 |
|---|---|
text | 主聊天 UI |
tool | 后端执行器 + 工具状态 UI |
thinking | 隐藏面板(可展开) |
citation | 引用面板 |
structured | 专用 UI 组件 |
详见 Semantic Router。
④ Event Bus(传输层)
职责:通过 SSE / WebSocket 输出统一格式的事件。
协议格式(推荐 Structured Streaming):
{ "type": "message.delta", "channel": "text", "data": { "content": "你好" } }
{ "type": "tool_call.started", "channel": "tool", "data": { "name": "search" } }
{ "type": "message.completed", "channel": "text", "data": { "message_id": "m1" } }
传输方式选择:
| 方式 | 适用场景 |
|---|---|
| SSE | 单向推送,HTTP 兼容,最常用 |
| WebSocket | 双向通信,需要客户端发送控制信号 |
| HTTP Streaming | fetch + ReadableStream,灵活控制 |
⑤ Frontend State Manager(状态层)
职责:集中管理流式状态,供 UI 组件消费。
状态结构:
{
messages: Message[], // 消息列表
streamingBuffer: string, // 当前正在生成的文本 buffer
toolCalls: ToolCall[], // 工具调用列表
extractedInfo: object, // 结构化提取信息
status: "idle" | "streaming" | "error" // 流状态
}
推荐工具:
- Zustand(轻量,适合中小项目)
- Redux Toolkit(成熟,适合大型项目)
- XState(状态机,适合复杂状态流转)
依赖路径
Token Stream → Chunk Aggregator → Semantic Router → Event Bus → Frontend State Manager
↑ ↑ ↑ ↑ ↑
LLM Provider 聚合策略 分流规则 传输协议 状态模型
每层可以独立演进:
- 换 LLM Provider → 只改第 ① 层
- 优化聚合策略 → 只改第 ② 层
- 增加新的输出类型 → 改第 ③ 层 + 前端 reducer
- 从 SSE 切到 WebSocket → 只改第 ④ 层
- 换状态管理库 → 只改第 ⑤ 层
对比与易混淆点
- 分层不是必须的:简单场景可以跳过某些层(比如不需要 Semantic Router,直接透传 text)。
- 层与层之间不是严格串行:有些事件可能跳过 Chunk Aggregator 直接到 Semantic Router(比如 tool_call 事件)。
- Chunk Aggregator 是最容易被忽视的层:很多系统直接透传 token,导致 char-level split 问题。
- Event Bus 不只是 SSE:SSE 只是传输方式,Event Bus 还需要处理事件格式化、错误处理、重连逻辑。