Skip to content
签到签到

进阶第 3 章:会话日志与压缩

业务问题

长会话会越来越贵,也会超过 context window。AI 编码助手 / 客服 Agent 需要在保留重要信息的同时控制上下文大小。

交互图解:Session Log、Surface 与多种投影 · 查看图解导读

核心原理

会话日志是只追加事件流。模型上下文不是把日志全部塞进去,而是从 surface 派生。

Surface 只包含:

  • user/message
  • assistant/message
  • tool/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.ts
  • packages/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 的作用。
  • 能设计前端展示压缩历史的方案。