AgentGraphStore 恢复“谁由谁 spawn、边是否 Open”,ThreadStore 恢复“每个 Thread 说过和做过什么”;V1 后代恢复先用图找身份,再逐个用 Rollout 创建新 Session。
本节解决的不是“把对象存一下”这种抽象问题,而是把问题限定在:研究拓扑 Store、live registry 与历史 Store 在恢复流程中的分工。输入:root ThreadId、持久化 spawn edges、每个 child 的 StoredThread/Rollout;状态所有者:AgentControl/ThreadManager 协调两个独立 Store;成功结果:恢复的 root Session 与可选 Open descendants Session。先把这些边界钉住,后面的顺序、失败和恢复才不会混成一句“持久化失败后重试”。
状态边界
| 问题 | 本节答案 |
|---|---|
| 输入 | root ThreadId、持久化 spawn edges、每个 child 的 StoredThread/Rollout |
| 状态所有者 | AgentControl/ThreadManager 协调两个独立 Store |
| 成功产物 | 恢复的 root Session 与可选 Open descendants Session |
| 研究范围 | 研究拓扑 Store、live registry 与历史 Store 在恢复流程中的分工 |
正常路径:先看顺序点
这条路径可以压缩成五步:
- 从 ThreadStore 恢复 root。
- 若协议需要则查询 Open direct children。
- 按 BFS 队列计算深度。
- 逐 child 从 ThreadStore 恢复 Session。
- 成功 child 再加入队列继续后代。
图中的箭头不是“可能调用”的依赖图,而是源码中决定可见性和所有权转移的先后关系。前一步没有确认时,后一步不能替它作出更强的成功承诺。
源码机制拆解
Graph trait 只有四类拓扑操作
upsert edge、set incoming edge status、list direct children、list descendants。Child 至多一个 persisted parent;列表要求稳定排序,便于与 live in-memory state 合并。
Local adapter 复用 StateRuntime
LocalAgentGraphStore 把中立 status 映射为 state DB 的 DirectionalThreadSpawnEdgeStatus,并调用 thread_spawn_edges 查询;它不读取 child Rollout 内容。
Resume queue 把发现与装载分开
恢复 root 后,V1 逻辑以 (thread_id, depth) BFS。发现 child 已加载则直接继续;否则构造新的 SubAgentSource 并调用 resume_single_agent_from_rollout。
只有成功恢复的 child 才扩展后代
某 child Rollout 缺失/损坏时记录 warning 并跳过,不把它加入 queue,因此不会错误地恢复一个无法建立父 Session 的更深分支。
V2 可只恢复身份元数据
V2 Registry/Residency 允许 cold agent;restore_v2_agent_metadata 可以从持久化关系恢复地址/角色而不立刻打开所有运行时。Graph 可见与 loaded Session 仍是两种状态。
Python 风格伪代码
下面的伪代码只保留设计职责、状态和失败顺序;它不逐行翻译 Rust,也不借 Python 语法虚构源码中不存在的事务:
async def resume_agent_tree(manager, root_id, config):
root = await manager.resume_from_thread_store(root_id, config)
if root.multi_agent_version == V2:
await manager.restore_v2_identity_metadata(root_id)
return root
if manager.agent_graph_store is None:
return root
queue = deque([(root_id, root.spawn_depth)])
while queue:
parent_id, depth = queue.popleft()
try:
child_ids = await manager.agent_graph_store.list_children(parent_id, status=OPEN)
except GraphStoreError as error:
warn(error); continue
for child_id in child_ids:
if manager.is_loaded(child_id):
child_ok = True
else:
child_ok = await try_resume_child_from_rollout(
child_id, parent_id, depth + 1, config)
if child_ok:
queue.append((child_id, depth + 1))
return root
阅读时要特别看三处:哪个对象拥有可变状态,哪一个 await 是可观察屏障,以及失败后保留的是已提交前缀、未提交后缀,还是完全独立的外部副作用。
失败、取消与恢复
| 故障点 | 已留下的状态 | 可观察结果 | 恢复责任 |
|---|---|---|---|
| Graph 有边、child Rollout 缺失 | 拓扑身份存在 | child Resume warning/失败 | 不把其后代加入 queue |
| Rollout 可恢复、Graph 边缺失 | child 内容孤立 | root 恢复不会自动发现它 | 修复/重建 topology metadata |
| Graph DB 不可用 | root history 仍可读 | 只恢复 root | 降级而不伪造 child edges |
| Closed 边下 child 仍有历史 | Thread 可单独恢复 | Open-tree 恢复不加载 | 显式 resume 或查询全部状态 |
这里没有统一的“回滚一切”。内存状态、日志行、SQLite 投影、父子拓扑和工具造成的文件/网络变化分别有自己的提交点。恢复代码只能根据已经存在的权威证据继续,不能用较弱的投影替较强的事实背书。
必须保持的不变量
- GraphStore 不返回对话内容
- ThreadStore 不推断父子 Open/Closed 控制状态
- 发现 child 不等于 child Session 已加载
- 只有成功恢复节点才驱动更深 BFS
这些不变量比“最终能 Resume”更严格:正常路径要成立,Writer 竞争、任务取消、坏尾行、投影落后和旧格式兼容时也必须成立。
设计取舍
拓扑与内容分库可能出现暂时不一致,但允许 V2 cold identity、独立删除策略和根 Thread 降级恢复;协调层必须把部分失败当正常分支。
源码可以直接证明字段、分支、调用顺序和测试期望;“为什么这样设计”的表述是基于这些事实作出的工程归纳,不把它包装成未公开的产品承诺。
Mini Codex 复刻
Graph 保存 edge,EventLog 保存每 node history;tree resume 是 orchestrator,逐节点容错,不在 Graph 行里嵌入会话快照。
复刻时先验证协议不变量,再补性能优化。一个能在故障注入下说明“留下了什么”的小实现,比一个只在正常路径调用 save() 的演示更接近真实 Runtime。
源码导航
- codex-rs/agent-graph-store/src/store.rs:存储中立拓扑契约与稳定 BFS 语义
- codex-rs/agent-graph-store/src/local.rs:StateRuntime adapter
- codex-rs/core/src/agent/control/spawn.rs:Resume root 后按 Open children BFS 恢复
- codex-rs/core/src/thread_manager.rs:Graph Store 构造与 live edge 快照
相邻测试也很重要:
- codex-rs/agent-graph-store/src/local.rs:direct child、status update 和 descendants BFS
- codex-rs/core/src/thread_manager_tests.rs:FakeAgentGraphStore 与恢复/列表协调
- codex-rs/core/src/agent/control_tests.rs:恢复 spawned descendants 和 V2 metadata
本节结论
AgentGraphStore 恢复“谁由谁 spawn、边是否 Open”,ThreadStore 恢复“每个 Thread 说过和做过什么”;V1 后代恢复先用图找身份,再逐个用 Rollout 创建新 Session。
评论
登录后即可评论