进阶第 3 章:会话日志与压缩
业务问题
长会话会越来越贵,也会超过 context window。AI 编码助手 / 客服 Agent 需要在保留重要信息的同时控制上下文大小。
交互图解:Session Log、Surface 与多种投影 · 查看图解导读
核心原理
会话日志是只追加事件流。模型上下文不是把日志全部塞进去,而是从 surface 派生。
Surface 只包含:
user/messageassistant/messagetool/result
assistant/chunk、turn 边界和请求头属于 replay 数据,不直接进入模型历史。
Compaction 会把一段旧历史替换成摘要。前端仍应保留原始事件,用于用户查看和审计。
真实项目中的映射
客服 Agent 的长会话需要:
- 用户能看到历史消息。
- 模型不需要重复处理每一段原文。
- 被压缩的内容要可追踪。
- 会话恢复不能丢失关键事实。
DeepSeek Harness 对应实现
DeepSeek Harness 中:
Session.append()写入事件。SurfaceManager维护模型可见 surface。deriveMessages()从 surface 投影模型历史。- compaction 使用
surfaceOp: replace替换旧节点。
进一步阅读:
packages/core/session/src/index.tspackages/core/session/src/surface.ts
代码示例
ts
type SurfaceOp =
| { kind: 'append' }
| { kind: 'replace'; start: number; end: number }
interface SessionEvent {
seq: number
type: string
surfaceOp?: SurfaceOp
}
class Surface {
private nodes: number[] = []
apply(event: SessionEvent): void {
if (!event.surfaceOp) return
if (event.surfaceOp.kind === 'append') {
this.nodes.push(event.seq)
return
}
const { start, end } = event.surfaceOp
const startIndex = this.nodes.indexOf(start)
const endIndex = this.nodes.indexOf(end)
this.nodes.splice(startIndex, endIndex - startIndex + 1, event.seq)
}
get current(): number[] {
return [...this.nodes]
}
}这个例子说明:替换不是删除历史,而是改变模型可见的 surface。
面试追问
问:为什么不能直接把完整日志发给模型?
回答要点:
- 成本高。
- 可能超过 context window。
- 很多日志只用于 UI、审计和回放,不是模型需要的上下文。
问:Compaction 和删除历史有什么区别?
回答要点:
- 删除会破坏原始事实。
- Compaction 保留原始日志,只改变模型可见投影。
- UI 可以继续展示原始内容,模型看到摘要。
问:前端如何展示被压缩的历史?
回答要点:
- 标记哪些范围被摘要替换。
- 用户可展开原始事件或摘要。
- 显示“已压缩”的提示。
实践任务
实现一个简化 Surface,把三个旧消息替换为一个摘要节点,并说明原始日志为什么仍然存在。
验收标准
- 能解释 surface 和原始日志的区别。
- 能说明
surfaceOp: replace的作用。 - 能设计前端展示压缩历史的方案。