雨天小六

读懂 Codex(8.33):AgentGraphStore 恢复与 Thread History 恢复的协作

· 更新于 2026-08-03 · 专栏:读懂 Codex

#Codex#Agent Runtime#持久化#Rollout#软件架构

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 在恢复流程中的分工

正常路径:先看顺序点

AgentGraphStore 恢复与 Thread History 恢复的协作的正常路径时序图,展示从 ThreadStore 恢复 root、若协议需要则查询 Open direct children、按 BFS 队列计算深度、逐 child 从 ThreadStore 恢复 Session、成功 child 再加入队列继续后代
图 8.33-1:从 ThreadStore 恢复 root → 若协议需要则查询 Open direct children → 按 BFS 队列计算深度 → 逐 child 从 ThreadStore 恢复 Session → 成功 child 再加入队列继续后代。这张图标出本节的实际顺序点与状态所有者。

这条路径可以压缩成五步:

  1. 从 ThreadStore 恢复 root。
  2. 若协议需要则查询 Open direct children。
  3. 按 BFS 队列计算深度。
  4. 逐 child 从 ThreadStore 恢复 Session。
  5. 成功 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 是可观察屏障,以及失败后保留的是已提交前缀、未提交后缀,还是完全独立的外部副作用。

失败、取消与恢复

AgentGraphStore 恢复与 Thread History 恢复的协作的失败路径图,区分Graph 有边、child Rollout 缺失、Rollout 可恢复、Graph 边缺失、Graph DB 不可用、Closed 边下 child 仍有历史
图 8.33-2:同一操作在不同故障点留下的状态不同,恢复责任必须回到拥有该状态的组件。
故障点已留下的状态可观察结果恢复责任
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。

源码导航

相邻测试也很重要:

本节结论

AgentGraphStore 恢复“谁由谁 spawn、边是否 Open”,ThreadStore 恢复“每个 Thread 说过和做过什么”;V1 后代恢复先用图找身份,再逐个用 Rollout 创建新 Session。

阅读导航

上一节:8.32 · 下一节:8.34

评论


← 返回文章列表