DeepSeek Harness 07:Session Log——事实源、视图派生与安全 Fork

DeepSeek Harness 07:Session Log——事实源、视图派生与安全 Fork
Asaakii在构建基于大语言模型的应用时,最直观的会话存储往往是一个可变数组:
1 | // 脆弱的初级设计:将可变数组作为整个系统的状态真相 |
这种朴素结构在工业级 Agent 中会迅速崩塌:
- 数组只能告诉你“当前有什么”,无法回答“系统经历过什么”;
- 它无法记录用户在何年何月何秒发起了插话;
- 它无法记录某个耗时 30 秒的 Shell 执行是在第几秒被用户强行按下了打断;
- 面对崩溃重试、时间旅行(Time Travel)调试、安全审计与多分支探索(Forking),可变数组完全无能为力。
DeepSeek Harness 彻底摒弃了内存数组设计,确立了事件溯源(Event Sourcing)模式下的 Session Log 作为全系统唯一的绝对事实源。
一、铁律:模型可见即已记录
DSH 确立了一条底层的架构铁律:任何能够进入大模型上下文的内容,都必须能够从 Session Log 中无损重建。
大模型在每次推理时所看到的消息列表,绝对不是由某个业务模块在内存中凭空拼接出的临时对象,而是持久化事件日志在当前时刻的确定性投影(Deterministic Projection)。
flowchart LR Log["追加式 Session Log<br/>(单调自增 seq / 绝对事实)"] -->|deriveMessages| Surface["模型上下文 Surface<br/>(只读投影视图)"] Log -->|UI 投影| Timeline["前端交互时间线"] Log -->|持久化| Storage["JSONL / SQLite 存储引擎"] Log -->|审计与遥测| Audit["安全合规审计中心"] Log -->|前缀克隆| Fork["Subagent 会话分支 (Fork)"] Surface --> Model["下一次模型推理决策"] Model --> Log
这条规则从根本上禁止了“在请求发出前悄悄往数组里插一条隐藏 Prompt”的投机取巧。所有想要影响模型的因素,必须先具象化为日志事实,然后才能被模型读取。
二、保存事实,派生视图
Session 是一个由不可变事件构成的流式序列。每个事件都拥有严格单调递增的序号 seq、纳秒级时间戳、结构化事件类型以及强类型的载荷数据:
| 核心事件类型 | 记录的核心事实 | 是否直接投影进模型上下文 |
|---|---|---|
turn/*, step/* |
轮次开启、结束原因、步进边界标识 | 否(用于控制平面状态机驱动) |
user/message |
用户原始输入、外部 Webhook 注入或断点续接指令 | 是(投影为 user 角色消息) |
assistant/chunk |
模型流式返回的细粒度原始切片与思维链增量 | 否(用于前端极低延迟回放与排障) |
assistant/message |
单步推理聚合完毕的模型回复与 Token 用量 | 是(投影为 assistant 角色消息) |
tool/call |
模型发起的工具调用声明与原始 JSON 字符串参数 | 否(已由 assistant/message 承载) |
tool/result |
工具管线最终落地的、规范化的权威输出结果 | 是(投影为 tool 结果块) |
request/header |
本次调用所选定的 Provider、模型版本与参数快照 | 否(用于事后对账与重演测试) |
[!NOTE]
双重记录的必要性:assistant/chunk与assistant/message并存绝不是空间浪费。前者用于在客户端以最高保真度完整重现模型的“思考与打字动画”,而后者则是不可变且聚合完毕的模型历史,为后续轮次提供确定的上下文参考。
三、Surface 投影机制与上下文遮蔽
会话日志只增不减,但大模型的上下文窗口是有限的。DSH 通过 deriveMessages() 纯函数从日志中派生模型所需的上下文视图(Surface):
- 追加模式(Append):常规的单步推进,新的消息块按顺序追加到 Surface 尾部。
- 替换模式(Replace / 遮蔽):当触发上下文压缩(Context Compaction)时,系统会在日志中写入一个结构化的摘要节点。该节点在投影时会遮蔽(Shadow)前面一大批历史事件,但底层日志从未物理删除任何原始数据!
1 | // 投影层记录:将 seq 10 到 58 的详细工具交互替换为一个紧凑的摘要节点 |
任何时候如果需要调查模型为什么在第 60 步做出了某种决策,审计人员只需展开被遮蔽的 shadowedSeqs,就能完整还原当时每一个工具的真实输出。
四、安全会话分支(Session Fork)
在复杂任务编排中,我们经常需要分化出一个 Subagent 去做探索性调研,或者让用户针对某个历史节点尝试不同方案。
DSH 提供了受控的分叉 API:ctx.sessions.fork(sourceSessionId, boundarySeq):
flowchart TD
subgraph ParentSession ["主会话 (Parent)"]
E1["seq: 1 (用户需求)"] --> E2["seq: 2 (模型规划)"]
E2 --> E3["seq: 3 (工具读文件)"]
E3 --> E4["seq: 4 (主会话后续推进...)"]
end
subgraph ChildSession ["子会话 (Child Fork)"]
E3 -.->|快照前缀克隆| C1["seq: 1 (继承上下文)"]
C1 --> C2["seq: 2 (继承上下文)"]
C2 --> C3["seq: 3 (继承上下文)"]
C3 --> C4["seq: 4 (子智能体独立探索...)"]
end
[!IMPORTANT]
Fork 的切片约束:Fork 只能在没有开放 Turn 的稳定闭合点进行!如果调用方试图在一个正在流式输出或工具尚未返回的活跃步进中强行 Fork,API 会直接抛出错误。这杜绝了子会话继承到一个半生不熟、存在悬空状态的脏上下文。
通过将事实源(Session Log)、持久化适配器(JSONL / SQLite)和投影层(Surface)彻底解耦,DSH 在保证高并发安全的同时,为长程 Agent 赋予了完整的“时间旅行”与崩溃自愈能力。











