Skip to content
签到签到

第 6 章:Session Log 与派生上下文

本章解决的问题

普通聊天产品把消息数组当真相。DeepSeek Harness 把只追加事件日志当真相。这一章解释这种设计为什么适合 Agent。

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

大白话理解

Session Log 就像 Git 历史:

  • 每次发生的事都追加一条记录。
  • 当前视图由历史推导出来。
  • 可以回放、分叉和审计。

模型的上下文不是额外保存的一份聊天数组,而是从日志中派生的。

必须掌握的概念

1. Append-only Log

事件一旦写入就不修改。事件包括:

  • turn/start
  • step/start
  • user/message
  • assistant/chunk
  • assistant/message
  • tool/call
  • tool/result
  • step/end
  • turn/end

2. Surface Event

不是所有事件都会进入模型历史。

会进入模型历史的事件主要是:

  • user/message
  • assistant/message
  • tool/result

assistant/chunk 用于流式展示和回放,不直接作为下一条模型消息。

3. deriveMessages

deriveMessages() 从 surface 节点投影出模型历史。

4. Model-visible 等价于 Logged

任何送给模型的内容都必须能从日志重建。这个原则保证 UI、回放、审计和模型看到的内容一致。

DeepSeek Harness 对应实现

DeepSeek Harness 的 Session 是事件日志,SurfaceManager 维护模型可见 surface。

deriveMessages() 只处理 surface 节点。assistant/chunk 保留原始流,assistant/message 是组装后的模型消息。

这个设计的价值:

  • 用户看到的聊天记录来自日志。
  • 模型看到的历史来自 surface。
  • 持久化保存原始事件。
  • 回放、标题、遥测和投影都基于同一份日志。

进一步阅读:

  • packages/core/session/src/index.ts
  • packages/core/session/src/surface.ts

代码示例

下面用简化 TypeScript 表达事件日志和派生历史。

ts
type SessionEvent =
  | { type: 'user/message'; content: string }
  | { type: 'assistant/chunk'; text: string }
  | { type: 'assistant/message'; content: string }
  | { type: 'tool/result'; name: string; content: string }

class SessionLog {
  private events: SessionEvent[] = []

  append(event: SessionEvent): void {
    this.events.push(event)
  }

  deriveMessages(): Array<{ role: string; content: string }> {
    const messages: Array<{ role: string; content: string }> = []

    for (const event of this.events) {
      if (event.type === 'user/message') {
        messages.push({ role: 'user', content: event.content })
      }
      if (event.type === 'assistant/message') {
        messages.push({ role: 'assistant', content: event.content })
      }
      if (event.type === 'tool/result') {
        messages.push({
          role: 'tool',
          content: `${event.name}: ${event.content}`,
        })
      }
    }

    return messages
  }
}

注意,assistant/chunk 保留在日志中,但没有被 deriveMessages() 放进模型历史。

面试怎么问

问:会话记录和模型上下文有什么区别?

回答要点:

  • 会话记录用于 UI、审计、回放。
  • 模型上下文是真正发给模型的消息。
  • 不是每条 UI 记录都要发给模型。
  • 可以用日志派生模型上下文。

问:为什么模型可见内容必须可重建?

回答要点:

  • 回放时要知道模型当时看到什么。
  • 调试和审计需要完整输入。
  • 恢复会话不能只靠 UI 文本。
  • 前端展示和模型上下文不能漂移。

问:前端如何消费事件日志?

回答要点:

  • 订阅 session/event
  • 把边界、消息、chunk 和工具结果映射成 UI 节点。
  • 保持原始事件和 UI 节点的对应关系。
  • 流式 token 高频时合批更新。

自测题

  1. 为什么 assistant/chunk 不直接进入模型历史?
  2. deriveMessages() 的作用是什么?
  3. 只追加日志适合解决什么场景?
  4. 前端显示聊天记录时,为什么最好从事件日志投影?

本章小结

Agent 产品里,会话日志是“真相”,模型上下文和前端 UI 都是投影。这个设计让回放、恢复、审计和 UI 保持同源。

进一步阅读:

  • packages/core/session/src/types.ts
  • docs/subsystems/session.md