06. Session 与 Event Sourcing:日志、Surface、Replay、Fork 与恢复

06. Session 与 Event Sourcing:日志、Surface、Replay、Fork 与恢复

系列:DeepSeek Harness 从入门到源码与二次开发

基于仓库:deepseek-ai/deepseek-harnessmaster 分支(核对日期:2026-08-26)

项目状态:官方仍标记为 Developer Preview ,内部 API、配置结构和包边界可能继续发生破坏性变化。

阅读约定:本文优先解释"设计与执行链",示例代码若标注"示意",应以仓库当前类型定义与生成文档为最终准绳。

DSH 的 Session 文档非常长,因为它承载的是整个系统最关键的事实边界之一。最重要的一句话是:Session 是 append-only typed SessionEvent log;LLM history 从日志派生,不单独维护。

如果你只记住这一句话,仍然不够。下面需要理解:什么应该记录、什么进入模型 surface、为什么请求头也要记录、如何做 replay/fork,以及自定义业务状态应该怎么加入。

1. 为什么不直接存 messages[]

最简单的聊天系统会保存:

json 复制代码
[
  {"role":"user","content":"..."},
  {"role":"assistant","content":"..."}
]

但 Agent 系统还存在:

  • Turn/Step 边界;
  • raw stream chunk;
  • Tool Call/Result;
  • 中途取消;
  • system prompt/tool schema 变化;
  • compaction;
  • plan mode / goal / todo 等业务状态;
  • resume/fork;
  • 持久化 checkpoint。

如果这些都只靠 messages 推断,就会产生大量模糊状态。

2. Session Event Log 是 single source of truth

概念上:

text 复制代码
seq=1  turn/start
seq=2  step/start
seq=3  user/message
seq=4  assistant/chunk
seq=5  assistant/chunk
seq=6  assistant/message
seq=7  tool/call
seq=8  tool/result
seq=9  step/end
seq=10 turn/end

所有派生视图都从这里来:

text 复制代码
log → deriveMessages()
log → UI transcript
log → projections
log → goal/plan/todo state
log → resume
log → fork

3. Event vocabulary 是可扩展的

官方 Session 文档说明 SessionEventMap 可以通过 TypeScript declaration merging 扩展。

这意味着插件可以加入自己的 durable facts,例如官方已有的:

  • compaction/*
  • plan/mode
  • goal/change
  • todo/write
  • 某些 hook protocol 的日志事件。

关键是:加入日志的应该是可恢复事实,不是随便什么内部变量。

4. Surface Event:哪些事件会影响模型看到的上下文

不是所有 Session Event 都进入 LLM history。

可以把事件分成:

text 复制代码
log-only events
    记录状态/审计,但不直接变成 message

surface events
    通过 surfaceOp 影响模型可见历史

例如 request/header 是重要的请求状态快照,但它不直接成为一条模型 message;plan/mode 记录 plan state,也不是消息。另一方面 user/messageassistant/messagetool/result 等会形成模型上下文表面。

5. "Model-visible means logged"

一个很有用的设计原则是:如果某段内容真正进入模型可见上下文,就应该能从 Session 中追溯到它来自哪里。

比如通过 agent.inject() 注入的文件变化通知、skill catalog、goal continuation,不应该偷偷只存在于临时数组。它们通常以带 sourceuser/message 形式进入 surface。

这样 resume/replay 时不会"忘记模型曾经看见过什么"。

6. source 为什么重要

两条都是 user-role message,但来源可能完全不同:

text 复制代码
真人输入
插件注入
goal continuation
skill invocation

如果只看 role,UI 和审计逻辑会误以为它们都来自用户键盘。DSH 的 message/source 设计让 provenance 与 role 分开。

这对科研系统尤其有价值:你可以区分"研究者给出的假设"和"系统自动注入的数据审计结论"。

7. Raw Chunk 为什么也写日志

有些系统只保存最终 assistant message。DSH 还记录 assistant/chunk,原因是 replay fidelity。

它能支持:

  • UI 重放真实 streaming;
  • 关联每个完整 message 的 source chunk seq;
  • 诊断 provider 在中途失败时已经输出了什么;
  • 区分"完全没有输出"和"输出了一半后取消"。

assistant/message 则是派生 history 使用的组装结果。

8. 中途取消如何表达

Session 文档当前说明:如果 Turn 在 streaming 中途被取消,已交付的 text/reasoning 前缀可被最终化为带 interrupted: trueassistant/message;没有 dispatch 的 tool calls 不应出现。

这很重要,因为不能简单根据 turn/end reason 推断每条 assistant 内容是否完整。事件自己携带更精确事实。

9. request/header:为什么模型请求配置也应该可重建

当前 Session 文档把 request/header 作为 logged request state,其中包含类似:

  • call configuration;
  • adapter-resolved defaults markers;
  • rendered system prompt;
  • assembled tool schemas。

意义是让"一次会话请求"更接近日志的纯函数:不是只知道说了什么,也知道当时 模型被怎样配置、系统提示是什么、有哪些工具可用

这对严格实验复现非常关键。

10. Replay:不是读取缓存的 messages

Replay 更接近:

text 复制代码
读取事件序列
  ↓
按规则 fold / derive
  ↓
重建模型 surface / projections

所以当新业务状态加入时,通常应该同时考虑:

  • event schema;
  • replay/fold 规则;
  • invalid event 如何 fail;
  • projection 如何从 seq 增量更新。

11. Fork:为什么事件日志天然适合分叉

一个 Session 可以在某个历史点分叉出新的工作线。事件溯源的优势是:

text 复制代码
共同前缀 events
        ├── branch A 后续 events
        └── branch B 后续 events

而不是复制一堆难以解释的 mutable object 状态。

对科研流水线,这可以对应:同一数据审计结论后,分叉 3 个模型想法分别实验,最后再汇总。

12. Compaction 不等于删除历史

DSH 的 compaction 不是"把旧 events 真删掉"。它通过 compaction events 与 surface replacement 等机制,让模型可见面变短,同时保留可审计历史。

这比直接从数据库删除旧 message 强很多,因为你仍然知道:

  • 哪些事件被摘要覆盖;
  • 摘要由哪次模型调用产生;
  • token 压力状态;
  • 何时发生 replacement generation。

13. Projection:不要每次都从头扫描整个日志

随着 Session 变长,某些 UI/业务状态不应该每次都全量重放。DSH 还有 session projection / projection cache 等能力,用 fold unit 和 watermark 维护增量视图。

理解层次:

text 复制代码
Event Log = 事实源
Projection = 从事实源得到的可查询视图
Cache = Projection 的性能优化

不要把 cache 反过来当事实源。

14. 自定义科研事件怎么设计

例如你需要保存 Idea 生命周期,可以设计:

text 复制代码
research/idea-created
research/idea-evaluated
research/idea-selected
research/idea-rejected

事件数据应尽量包含稳定、可重放事实,而不是临时 UI 文案。例如:

json 复制代码
{
  "ideaId": "I-003",
  "hypothesis": "...",
  "evidenceRefs": ["artifact:..."],
  "decision": "rejected",
  "reasonCode": "data-leakage"
}

然后通过 fold 得到当前 Idea Registry view。

15. 什么时候不该写 Session Event

以下通常不适合:

  • 当前按钮 hover 状态;
  • 纯 UI 展开/收起;
  • 一个可从 durable data 重新计算的临时缓存;
  • 单进程内短暂锁;
  • 不需要跨 resume/fork 保留的对象引用。

判断问题:进程崩掉并恢复后,我是否必须知道这件事发生过? 如果答案是否定的,通常不需要 durable event。

16. 本篇实践

设计一个"实验运行"事件链:

text 复制代码
experiment/submitted
experiment/started
experiment/metric
experiment/artifact
experiment/completed
experiment/rejected

然后分别标记:哪些进入模型 surface,哪些 log-only,哪些应该通过 projection 汇总成当前状态。

小结

Session Event Sourcing 把 DSH 从"聊天记录程序"提升为可恢复运行时:事件日志保存事实,surface 决定模型可见历史,projection 负责高效视图,persistence 负责落盘,resume/fork 都从同一事实源重建。 对需要科研审计、长流程自动化和多分支实验的系统,这一层尤其重要。

官方参考资料

相关推荐
曦云沐21 小时前
DeepSeek Harness 3 步跑通 Agent 运行时框架(npx 一键启动 Web UI)| 2026 实测
agent·deepseek·harness
圣殿骑士-Khtangc21 小时前
DeepSeek Harness Headless 模式:把 AI Agent 写进 CI/CD 流水线
智能体·harness
圣殿骑士-Khtangc1 天前
DeepSeek Harness vs AutoGPT/LangGraph/MetaGPT/CrewAI:Agent 框架到底怎么选
智能体·harness
oe10191 天前
以谈DSH为醋,包个饺子——Harness与RSI与Scaling
dsh·rsi
张忠琳2 天前
【deepseek-harness】DeepSeek Harness (dsh) 系统级架构分析之三
ai·agent·deepseek·harness
戒了,最后一次2 天前
DeepSeek Harness 源码安装教程(Windows 篇)
windows·腾讯云·deepseek·harness
CodeBlog-star2 天前
Codex Harness 全面开源:OpenAI的 AI Agent 底层执行框架
人工智能·开源·openai·codex·harness
伊玛目的门徒2 天前
deepseek harness DSH 安装皮肤脚手架(dsh‑client‑ui‑skin‑center)踩坑完整记录
ui·插件·皮肤·harness·dsh
程序员果子3 天前
DeepSeek Harness
aigc·agent·智能体·harness·dsh