第 12 章:持久化、线程与恢复 —— 对话不是一张 messages 表
DeerFlow 把对话状态、运行控制、事件历史和业务元数据分开保存。分层看似复杂,却让每种数据使用合适的一致性模型。
四类持久数据
| 数据 | 典型后端 | 用途 |
|---|---|---|
| LangGraph checkpoint | memory / SQLite / Postgres | 精确恢复图状态 |
| Thread metadata | store / SQL | title、owner、pin、branch 等 |
| Run records | memory / SQL | 状态、租约、模型、token、错误 |
| Run events | memory / DB / JSONL | SSE 重放、子代理步骤、审计 |
文件 workspace 又是第五类数据面,由沙箱/路径系统管理。不要试图把所有东西塞进 checkpoint:SSE 事件是 append-only 日志,run 状态需要查询和唯一约束,workspace 是文件系统语义。
Thread 与 Run 的关系
一个 Thread 有多个 Run;每个 Run 从某个 checkpoint 开始,生成更多 checkpoint 和事件。Thread 的“当前状态”是最新有效 checkpoint,而 Run 记录“这一次尝试发生了什么”。
Run 状态与 finalizing
运行不是简单的 pending/running/done。RunManager 还追踪 cancel intent、finalizing、owner worker、lease expiry、stop reason、delivery receipt 等。
finalizing 用来阻止后续编辑/重放在前一运行尚未完成 workspace snapshot、消息持久化时抢跑。RunRecord 的持久状态是跨进程仲裁依据,进程内 asyncio Task 只是当前 owner 的执行句柄。
所有权租约与孤儿恢复
多 worker 部署下,运行由某个 worker 领取并定期 heartbeat 续租。取消请求若发现 owner 在本机,就直接 signal;若 owner 在另一 worker,则先写 durable cancel,再通过共享机制请求远端。
worker 崩溃后 lease 过期,周期性 reconciliation 找到 orphaned inflight runs。系统依据 checkpoint、终态事件和配置决定标记失败、恢复或清理,而不是永远留下 running。
数据库级唯一索引是最后仲裁者。应用层“先查有没有 active run”有 await 窗口,两个请求都可能通过;只有部分唯一索引能原子阻止同一线程同时占据 active 状态。
Checkpoint full 与 delta
Full channel 每次保存完整 messages,读取简单但长对话写放大明显。Delta channel 保存 reducer writes,并按频率写 snapshot;恢复时线性折叠增量。
模式属于进程/数据库级契约,启动后冻结。编辑重跑、rollback 和 state replacement 都必须理解该模式:full 可直接替换列表,delta 需要用 Overwrite 或线性化写入,避免 reducer 把“替换”解释成“追加”。
分支、重生成与编辑重跑
这三者语义不同:
- regenerate:找到目标 HumanMessage 前的 base checkpoint,以相同输入再跑;
- edit-regenerate:只允许最新可编辑轮,替换用户文本并隐藏被取代消息;
- branch:创建新 thread,复制目标 checkpoint 以及用户 workspace 数据,从历史位置形成独立后续。
分支不能只复制 messages。它还要处理 thread metadata、branch lineage、用户文件、checkpoint config 和目标消息是否位于可见 turn 边界。
历史为何来自 events + checkpoints
自动摘要会从当前 checkpoint 删除旧消息;编辑重跑会隐藏 superseded run;部分控制消息本来就不应显示。因此 Gateway 的 messages page 需要扫描持久记录,过滤 hidden/control message,保留 thread-global event sequence,并附加 run duration、模型和 workspace changes 等 run-scoped 元数据。
前端分页时保留这个 seq 顺序,不根据本地时间重新排序。这样即使 checkpoint 只有稀疏窗口,完整可见历史仍能稳定重建。
数据库选择
memory:最快速开发,不跨重启;sqlite:单机持久化,部署简单;postgres:多 worker 与生产一致性;- run events 还可选 JSONL,适合轻量单节点审计;
- StreamBridge 多进程通常使用 Redis,与 run event store 职责不同。
配置系统把 database、checkpointer、run_events、stream_bridge 分开,就是允许在不同规模下组合,而不是假设“用了 Postgres 一切都自动共享”。
源码锚点
下一章把外部入口、身份与能力授权连起来。