Delta Stream vs Append Stream
Delta Stream 和 Append Stream 表面上都"只返回增量",但增量对象完全不同——Delta 增量的是同一文本字段,Append 增量的是事件日志中的新增记录。
#synthesis
#evergreen
#ai
#architecture
[!info] related notes
- 所属 MOC: LLM Streaming MOC
- 相关概念: Delta Stream, Append Event Stream, Full State Stream
- 输出模型: LLM 输出四层模型
- 高级组合: Streaming 高级协议模式
Delta Stream vs Append Stream
范围
比较两种可组合的应用层视角:Delta 描述文本如何变化,Append 描述事件记录如何增加。一个追加到日志中的事件可以携带文本 delta,因此不是互斥协议选项。
为什么要放在一起理解
很多开发者混淆这两种协议,因为它们都”只传增量”。但混淆建模层次会导致:
- 只定义文本片段而没有工具/状态事件 → 无法完整表达 Agent 生命周期
- 将每条事件误当独立消息 → 文本被拆成多个气泡,而不是拼入同一消息
区分消息内容与事件记录,才能定义一致的消费方式。
核心区别:增量对象不同
Delta Stream:增量的是 content
// 前端操作
message.content += delta.content
教学事件示例(JSONL,每行一个对象):
{ "type": "text.delta", "content": "你" }
{ "type": "text.delta", "content": "好" }
{ "type": "text.delta", "content": "世界" }
所有 delta 都在扩展同一段文本。最终结果:
message.content = "你好世界"
Append Stream:增量的是 event
// 前端操作
events.push(event)
教学事件示例(JSONL,每行一个对象):
{ "type": "message.append", "message_id": "m1", "content": "你好" }
{ "type": "tool_call", "name": "symptom_extract", "args": {} }
{ "type": "extracted_info", "data": { "symptom": "头痛" } }
{ "type": "message.append", "message_id": "m1", "content": "根据你的描述..." }
每个事件是独立的结构化记录,可能有不同的类型和目标。
两种消费视角
// 如果前端收到事件后主要做:
currentMessage += content
// → 文本增量消费,可位于下面的事件分发内部
// 如果前端收到事件后主要做:
switch (event.type) {
case "message.append":
case "tool_call":
case "extracted_info":
case "status":
}
// → 按事件类型分发,也可消费文本 delta
对比表
| 对比点 | Delta Stream | Append Stream |
|---|---|---|
| 增量对象 | 同一个文本字段 | 新事件记录(不一定是新消息) |
| 前端操作 | content += delta | events.push(event) 或 reducer 分发 |
| 关注点 | 一条消息怎么逐步生成 | 一个 AI 流程产生了哪些事件 |
| 数据形态 | 追加型文本片段 | 偏 event log |
| UI 效果 | 渐进文本输出 | 可组合文本输出与多步骤状态展示 |
| 典型用途 | ChatGPT 回答生成 | Agent / workflow / tool call |
| 带宽 | 取决于文本和封装/重传 | 取决于事件 payload、封装和重传 |
| 延迟 | 取决于生成、缓冲和调度 | 同样可即时发送,不能按名称排名 |
| 前端复杂度 | 中(维护 buffer) | 中(reducer 分发) |
依赖路径 / 演进链
简单文本生成 → 定义文本 delta 与完成/失败事件
↓ 增加 function calling
需要表达工具调用 → 在 Delta 基础上扩展事件类型
↓ 增加结构化提取、多步骤流程
事件类型增加 → 按目标消息/工具归约状态
↓ 需要统一事件格式
补充标识、版本、顺序、终态与恢复策略 → 可验证的协议契约
实际系统的做法
一种常见可行的组合是:
外层用 Append 管理流程,内层用 Delta 管理文本生成。
{ "type": "message.started", "message_id": "m1" }
{ "type": "text.delta", "message_id": "m1", "content": "你好" }
{ "type": "tool_call", "name": "symptom_extract" }
{ "type": "extracted_info", "data": {} }
{ "type": "text.delta", "message_id": "m1", "content": ",我已经整理了你的症状。" }
{ "type": "message.completed", "message_id": "m1" }
易混淆点
- “都是增量”不等于”一样”:Delta 的增量是文本扩展,Append 的增量是事件追加。
- Append 不排斥文本效果:Append Stream 也可以实现打字机效果,可将
message.append的文本增量拼入同一消息;动画平滑度取决于渲染策略。 - 选择需看状态目标与恢复约束:按 message_id 归约文本,按 event_id/序号处理重复和顺序;是否 Agent 并不单独决定协议。