ARCHITECTURE · 06

会话日志:DeepSeek Harness 的单一事实来源

直接答案

DeepSeek Harness 的会话(Session)是一份仅追加的类型化事件日志,是智能体完整交互历史的唯一事实来源。模型看到的对话历史不单独存储,而是从日志实时派生;回放就是拿同一组事件重新派生一遍。官方不变量是"模型可见即已记录":抵达模型请求的一切必须能从日志重建。

仅追加日志:只往后写,从不改历史

官方事实

Session 是一份仅追加的类型化 SessionEvent(会话事件)日志。每条事件携带:

  • 单调递增的 seq(序号);
  • epoch 毫秒时间戳 time
  • type 判别的 data(数据载荷)。

所有事件必须是无损 JSON——Session.append 在运行时会校验,塞不进 JSON 的东西根本进不了日志。这条约束保证了日志永远可以被序列化、传输和回放。

核心事件词汇(按官方 session 文档口径)

官方事实

按官方 docs/subsystems/session.zh.md(逐行对齐源码 SessionEventMap),核心会话事件共 13 种:

  • 轮次与步骤:turn/startturn/endstep/startstep/end
  • 消息:user/messageassistant/chunkassistant/message
  • 工具:tool/calltool/result
  • 其他:todo/writerequest/headerrequest/contextsession/end-seed

这些事件在循环哪个节点产生,见 Agent Loop页;tool/calltool/result 的执行侧见工具系统页。两点澄清(这是常见误读):

  • 没有 steering/message 这个事件类型。中途引导(steering)产生的是 user/message,只是它的 source(来源标记)不同。
  • 上面是核心词汇。插件可以通过声明合并扩展事件类型,比如官方的 compaction(上下文压缩)插件就加了 compaction/*hook/invoked 等事件(机制见插件系统页第 6 节)。

派生历史:模型看到的是"投影"

官方事实

模型历史由 deriveMessages()(派生消息函数)从日志算出来,规则包括:

  • assistant/chunk(流式碎片)只用于回放和界面保真,派生时跳过;
  • 内容为空、只有用量信息的 assistant/message 也跳过;
  • tool/result 投影成一条带 tool-result 块的 user 消息送回给模型。
示意图
01左侧纵向事件流(turn/start → user/message → assistant/chunk×3 → assistant/message → tool/call → tool/result → turn/end);中间 deriveMessages() 过滤器把 chunk 和空消息标灰跳过、tool/result 转换为 user 消息;右侧是模型实际看到的精简消息列表。底部小字"回放 = 用同一组事件重新派生"。

所以会话日志 ≠ 聊天记录:日志里有碎片、请求头、todo 快照等全部事实,模型看到的只是投影。这也解释了为什么改模型配置不影响旧会话——每个会话保留自己日志里记录的模型信息;以及为什么图片一旦进了会话日志,同一会话的后续请求仍可能携带它(本站教程实测口径,实测细节见《模型》教程)。

request/header:让"模型可见即已记录"落地

官方事实

request/header 事件(EpochHeader)记录了一次模型请求的调用配置、渲染后的系统提示词和已组装的工具 schema。有了它,每次对话请求都是日志的纯函数——给定同样的日志前缀,就能重建模型当时看到的一切。这就是架构总览里那条不变量(第 5 节)的实现机制。

崩溃恢复与轮次结束原因

官方事实

轮次结束原因(TurnEndReasonMap)有六种:

  • completed:完成;
  • aborted:实时取消;
  • blocked:被拦截;
  • error:出错;
  • max-tokens:超长度;
  • interrupted:中断。

特别注意 interrupted:它是唯一不由循环发出的结束原因——由崩溃恢复流程合成,用来关闭崩溃时遗留的开放轮次,且不截断已经持久化的事件。interruptedaborted:前者是事后修复,后者是当场取消。

持久化与分叉

官方事实
  • 持久化是独立的能力接缝:ctx.sessionPersistence 定义 locate / create / append 等操作,后端有 JSONL(默认)和可选 SQLite。session/event 是同步通知,落盘用固定批处理窗口,session/flush 是排空检查点。
  • 会话元数据(SessionHeader)与日志分开存储:格式版本、工作目录、谱系、seed 边界、委派深度(delegationDepth,跨重启保留递归预算)、Agent 预设等。
  • 分叉(fork):ctx.sessions.fork(source, boundary?, childSessionId?) 选取到指定边界为止的事件前缀开子会话;拒绝结束于开放轮次内的前缀——不会静默截断。子会话元数据带 parentSessionseedLength,继承工作目录。
示意图
02一条主会话时间线,两个候选切点:切点 A 在已关闭轮次之后(绿色勾,允许分叉出子会话);切点 B 落在开放轮次中间(红色叉,标注"拒绝,不静默截断")。子会话分支标注 parentSession、seedLength。
  • 兼容性提醒:日志格式版本 SESSION_FORMAT_VERSION = 0,官方不提供兼容承诺;当前构建不认识的必需事件类型会被拒绝加载,而不是静默跳过(除非事件信封标了 ignorable: true)。

EVIDENCE

证据与来源

本页包含:官方事实 / 教程实测

#事实声明状态来源
1仅追加日志、派生历史、回放重新派生官方事实
2核心事件词汇 13 种(含 request/context、session/end-seed,无 steering/message)官方事实CV1 校正口径
3steering 是 user/message 的 source 而非独立事件类型官方事实CV1 校正口径
4插件经声明合并扩展事件(compaction/*、hook/invoked)官方事实
5事件携带 seq / time / data、isJsonValue 运行时校验官方事实
6派生规则:chunk 跳过、空 assistant/message 跳过、tool/result 投影为 user 消息官方事实
7request/header(EpochHeader)内容、"模型可见即已记录"机制官方事实
8TurnEndReasonMap 六值、interrupted 由崩溃恢复合成官方事实
9持久化 seam、JSONL 默认 + 可选 SQLite、批处理窗口、flush官方事实
10SessionHeader 字段(delegationDepth、agentPreset 等)官方事实
11fork API、拒绝开放轮次前缀、子会话元数据官方事实
12SESSION_FORMAT_VERSION=0、未知必需事件拒绝加载除非 ignorable官方事实
13改模型配置不影响旧会话、图片随会话日志携带教程实测本站已验证内容
  • 本站教程内容 src/content/tutorials/models.ts:384-403
适用版本
DSH 0.1.1-rc.2(2026 年 8 月,开发者预览阶段)
发布
2026-08-26
更新
2026-08-26
最后核验
2026-08-26