这篇只解决一个问题:什么时候应该把一个 Agent Loop 升级成 Graph,以及升级后怎样避免它变成一团更昂贵的并发 Prompt。
如果你还没有分清 Context、Harness、Loop 和 Graph,建议先看入门篇《从 Context Engineering 到 Graph Engineering》。本文默认你已经有一个能停止、能恢复、能留下 Trace 的 Durable Loop。
图 1:单 Agent Loop 在出现真实独立分支后展开为可验证工作图
图 1:Graph 不是替代 Loop,而是组织多个局部 Loop、确定性节点和验证门禁。封面动画仅表示执行路径,不表示真实并发速度。
| 项目 | 说明 |
|---|---|
| 内容类型 | AI Agent 工程进阶第 9 篇 |
| 适合读者 | 已经能运行单 Agent,希望处理真实并行、独立验证或跨角色交付的人 |
| 阅读时间 | 约 12–15 分钟 |
| 跟做时间 | 约 30–45 分钟 |
| 可带走产物 | Graph 设计模板、六类失败案例、Graph Lab 0.9.0 |
| 代码要求 | Python 3.10+,零第三方依赖,不需要 API Key |
| 资料核对日期 | 2026-08-07 |
| 事实边界 | Graph Engineering 是实践标签,不是统一行业标准 |
配套代码固定在 GitHub commit 53e05c0。压缩包 SHA-256:
82B1190F1E5154C3DB68028571110E620F8D16384EECCE32FDD8BA69DDEB99F8
一分钟概览
先记住五句话:
- 一个所有者能够顺序完成的任务,优先保留 Durable Loop。
- 下游不消费上游输出的边是假边,删掉不会影响结果。
- 并行分支应写各自的私有状态,不能共同编辑一份草稿。
- Verifier 必须和生产者分开,Merge 只能接收已验证结果。
- 多 Agent 的提升可能来自额外 Token、工具调用和采样,不能自动归功于 Graph 拓扑。
本文的最小工作图是:
Plan
├─ Docs branch
├─ Code branch
└─ Policy branch
↓
Independent Verifier
↓ pass only
Merge
1. 先判断:你的任务真的需要 Graph 吗
复杂任务不一定需要 Graph。先问三个问题。
问题一:顺序是否完全已知
如果步骤固定,例如“解析文件、校验字段、写入数据库”,用普通 Pipeline。不要让模型决定本来就确定的顺序。
问题二:是否需要反复读取反馈
如果一个所有者需要多轮执行、检查和修正,用 Durable Loop。它可以有工具、检查点和重试,但仍然只有一个主要控制者。
问题三:是否存在真实独立分支
Graph 至少应带来一种明确收益:
- 分支可以使用不同上下文独立工作;
- 分支可以真正并行,缩短关键路径;
- 不同分支需要不同工具或权限;
- 结果必须由独立角色验证后才能合并;
- 某些决定必须由代码或人持有,而不能留给同一个 Agent。
图 2:用三个问题选择 Single Call、Pipeline、Durable Loop 或 Graph
图 2:先选最小可行结构。无法写清分支合同和验证标准时,继续优化单 Loop。
OpenAI 当前的 Agents SDK 文档也给出类似的保守建议:优先从一个 Agent 开始,只在能力隔离、策略隔离、Prompt 清晰度或 Trace 可读性确实改善时再增加专家。若主 Agent 应继续拥有最终回答,使用 agents-as-tools;只有专家应接管后续对话时才使用 handoff。OpenAI Orchestration and handoffs
这说明“多 Agent”不是默认升级,而是一项需要证明收益的架构选择。
2. 一张可执行 Graph 的五个部件
流程图画得漂亮,不代表它能运行。最小 Graph 需要五个可检查部件。
| 部件 | 必须回答的问题 | 最小合同 |
|---|---|---|
| Node | 谁做什么 | 输入、输出、工具、预算、失败状态 |
| Edge | 为什么必须等待上游 | 被消费的输出键或控制条件 |
| State | 中间结果放在哪里 | 命名空间、写入者、版本、可见范围 |
| Gate | 什么时候允许继续 | 代码规则、Verifier 结论或人工决定 |
| Trace | 实际走了哪条路径 | 节点状态、输入引用、预算和终态 |
Node 不是 Agent 的同义词
下面这些都可以是节点:
- 一个负责检索官方文档的 Agent;
- 一次单元测试或静态扫描;
- 一个 JSON Schema 校验器;
- 一段确定性合并函数;
- 一个等待人工批准的中断点。
能用代码稳定判断的事情,不必再增加一个 Agent。
Edge 必须表示真实依赖
可以用两个问题检查一条边:
- 下游节点是否读取了上游的某个明确输出?
- 删除这条边后,系统是否可能在输入不完整时错误执行?
如果两项都是否,这条边大概率只是视觉上的连线。
Graph Lab 的编译器先找出当前依赖已经满足的节点;如果仍有节点却找不到任何可运行节点,就把它判为环。每一层开始执行前,还会检查两个节点是否争写同一个状态键:
ready = tuple(
sorted(
node_id
for node_id in remaining
if set(nodes[node_id].depends_on).issubset(completed)
)
)
if not ready:
raise GraphCompileError("cycle_detected", "no runnable node")
validate_parallel_writes(ready, nodes)
这是真实实现的简化摘录。它没有让模型猜测下一步,而是把确定性调度留给代码。
例如:
Research A -> Research B
如果 B 从不读取 A 的结果,它们不该串行。更合理的是:
Plan -> Research A
Plan -> Research B
3. Diamond Pattern:先并行,再独立验证
Graph 最常用也最容易讲清的结构是 Diamond:一个规划节点展开多个分支,分支完成后经过验证屏障,再合并为最终结果。
图 3:Diamond Pattern 中三条分支经独立 Verifier 后才能合并
图 3:并行只是表面,真正重要的是分支隔离、独立验证和 Merge 的写入门禁。图中的 Human Gate 表示高风险动作仍可要求人工决定,具体策略留到第 10 篇。
为什么 Verifier 必须独立
“让原 Agent 再检查自己一次”通常仍会保留原来的假设和遗漏。独立至少包含三层:
- 上下文独立:Verifier 获得任务合同、分支产物和验收标准,不继承生产者的完整推理历史;
- 标准独立:判断依据来自固定 rubric、测试、数据或业务规则;
- 权限独立:生产者不能把自己的结果直接标记为已验证,也不能绕过 Merge Gate。
Verifier 可以是模型,也可以是测试命令。对于代码、Schema、权限和金额边界,确定性检查通常更合适。
Merge 是屏障,不是第四个自由创作 Agent
Merge 应该做的是:
读取已验证分支输出
→ 检查必需键是否齐全
→ 处理显式冲突
→ 生成最终产物
如果 Merge 重新搜索、重新推理并随意覆盖分支结论,前面的隔离和验证就失去了意义。
4. 状态所有权:不要让并行分支共享草稿
多 Agent 最常见的工程错误,不是 Prompt 写得差,而是两个分支同时修改同一份状态。
例如:
worker_a writes shared_draft
worker_b writes shared_draft
即使运行时没有报错,也很难回答:谁覆盖了谁、哪段内容通过了验证、失败重试是否会重复写入。
更稳的结构是:
worker_a writes branch/docs/findings
worker_b writes branch/code/findings
worker_c writes branch/policy/findings
verifier reads branch/*/findings
merge writes final_result
图 4:并行分支使用私有状态,Verifier 只读,Merge 独占最终写入
图 4:共享输入可以只读;并行输出先隔离,验证通过后再受控合并。
至少为每个状态键记录:
key: docs_findings
owner: research-docs
readers: [verify, merge]
scope: branch/docs
schema: FindingList.v1
这比笼统的 shared_state 更容易测试、恢复和审计。
5. 实操:运行 Graph Lab 0.9.0
实验延续前八篇使用的 Agent Reliability Lab。它不调用真实模型,而是用固定节点和故障案例验证 Graph 控制逻辑。
5.1 运行
解压下载包后进入目录:
cd .\agent-graph-lab-0.9.0
python run_lab.py graph-eval
关键输出应为:
{
"total_cases": 6,
"matched_cases": 6,
"status_counts": {
"completed": 1,
"invalid": 3,
"blocked": 2
},
"gate_passed": true
}
再运行完整回归:
python -m unittest discover -s tests -v
本文固定版本的结果是:
Ran 85 tests
OK
5.2 六个案例分别证明什么
| Case | 预期终态 | 检查点 |
|---|---|---|
valid-diamond | completed | Verify 完成后才执行 Merge |
missing-dependency | invalid | 缺失节点在运行前被拒绝 |
cycle-detected | invalid | 环依赖不会变成无限等待 |
shared-write-conflict | invalid | 同层节点不能争写同一状态键 |
verifier-blocks-merge | blocked | 验证失败时 Merge 不运行 |
budget-exhausted | blocked | 不超预算,并在可解释终态停止 |
图 5:PASS 只说明 fixture 与控制合同一致,不是模型质量或并行性能结论。
实验边界
Lab 按拓扑层设置同步屏障,并没有启动真实线程或分布式 worker;
cost是确定性预算单位,不是 Token 或货币;Verifier 的布尔结论来自 fixture,不评测模型判断质量。迁移到真实运行时后,还要分别验证并发、持久化、取消、Provider 用量和人工中断。
建议先读四个文件:
| 文件 | 读它是为了什么 |
|---|---|
datasets/graph-cases.jsonl | 看六个边界怎样被写成输入合同 |
agent_lab/graph.py | 看 DAG 编译、分层执行、预算和 Gate |
tests/test_graph.py | 看哪些行为真正被自动测试 |
reports/graph-review.md | 看最终门禁和每个 Case 的终态 |
5.3 两个故障练习
先复制数据集,避免改坏原始案例:
Copy-Item .\datasets\graph-cases.jsonl .\datasets\graph-cases-broken.jsonl
练习一:打开副本,把 valid-diamond 中 research-code 的输出键从 code_findings 改为 docs_findings,让它与 Docs 分支争写同一个键。
python run_lab.py graph-eval `
--graph-cases .\datasets\graph-cases-broken.jsonl `
--output .\reports\broken-write
预期:命令退出非零,valid-diamond 从 completed 变为 invalid/shared_write_conflict,总门禁失败。
练习二:重新复制原始文件,把 valid-diamond 的 Verifier verdict 从 true 改为 false,但保留原来的预期终态。
预期:Verifier 产生 verifier_failed,Merge 不执行,门禁因为实际终态与预期不一致而失败。
这两个练习分别证明:Graph 的价值不在成功路径,而在错误能否在正确位置停止。
6. 静态图、动态图和混合图
Graph 不一定全部写死,也不该全部交给模型临场决定。
| 模式 | 适合什么 | 主要风险 |
|---|---|---|
| 静态图 | 合规流程、发布流水线、固定审核 | 变化时需要改代码 |
| 动态图 | 调研、根因假设、未知数量的分支 | 成本和路径难预测 |
| 混合图 | 外层固定 Gate,内层允许动态探索 | 需要清楚区分谁拥有控制权 |
我的默认选择是混合模式:
代码固定:预算、权限、状态 Schema、验证和 Merge Gate
模型决定:查询词、分支数量、分支内部工具顺序
当前框架怎样映射
| 实现 | 它提供什么 | 使用时要记住什么 |
|---|---|---|
| OpenAI Agents SDK | Agent Loop、agents-as-tools、handoff、session、approval 和 trace | 先决定最终回答由 manager 还是 specialist 持有;这些原语不自动替你设计 DAG |
| Claude dynamic workflows | 动态拆分、并行 subagent、验证和合并模式 | 官方明确提醒会显著增加用量,适合复杂且高价值的任务 |
| LangGraph | 低层编排运行时、持久化、故障恢复和 Human-in-the-loop | 节点边界会影响检查点、恢复成本和可观测性 |
| Google ADK Go 2.0 | 图式工作流、节点超时、全局并发上限和分支隔离 | 这是 Go 2.0 的具体能力,不要直接外推到所有 ADK 语言版本 |
对应官方资料:
- OpenAI Agents SDK 概览
- Anthropic:dynamic workflows 的常见模式
- LangGraph overview
- Google ADK Go 2.0 graph workflow engine
Graph Engineering 这个说法在 2026 年的社区讨论中被进一步强调,但其中的 DAG、状态机、并行 worker 和验证屏障都不是新发明。本文保留三篇术语来源作为背景:IntuitMachine、EXM7777、0xCodila。产品能力仍以官方文档为准。
7. 不要把更多计算误认为更好拓扑
Anthropic 在多 Agent Research 系统复盘中指出,性能变化与 Token 用量、工具调用和模型选择高度相关,并明确提醒多 Agent 成本更高,而且依赖密集、必须共享大量上下文的任务通常不适合这种结构。How we built our multi-agent research system
因此,比较单 Agent 和 Graph 时至少固定:
相同任务集
相同模型
相同最大 Token 或成本预算
相同工具和数据权限
相同验收标准
重复运行次数
否则只能说“这个更大的系统得分更高”,不能说“Graph 拓扑更优”。
以下情况先不要使用 Graph:
- 任务可以由一次调用或一个 Loop 稳定完成;
- 分支必须共享几乎全部上下文,无法真正隔离;
- 找不到独立的验证信号;
- 每个分支都需要频繁等待其他分支,所谓并行只是排队;
- 任务价值不足以覆盖额外 Token、延迟和审查成本;
- 团队还不能可靠回答单 Agent 为什么失败。
8. 可复制的 Graph 设计模板
开始写框架代码前,先填下面这份最小合同:
graph:
goal: "最终要交付什么"
budget:
max_steps: 12
max_parallel: 3
max_cost: "由项目定义"
stop:
success: "Verifier 通过且 Merge 产物完整"
blocked: "依赖缺失、验证失败或预算耗尽"
nodes:
- id: research_docs
kind: worker
inputs: [approved_plan, source_catalog]
writes: [branch/docs/findings]
tools: [official_docs_search]
failure: docs_failed
- id: verify
kind: verifier
inputs:
- branch/docs/findings
- branch/code/findings
- branch/policy/findings
writes: [verification]
rubric: FindingRubric.v1
failure: verification_failed
- id: merge
kind: deterministic
requires: [verification.passed]
writes: [final_result]
state_ownership:
branch/docs/findings: research_docs
branch/code/findings: research_code
branch/policy/findings: research_policy
verification: verify
final_result: merge
评审时只问七个问题:
- 每条 Edge 对应哪个被消费的输出?
- 哪些节点真的可以独立运行?
- 两个并行节点是否可能写同一个状态键?
- Verifier 是否继承了生产者的偏见或写入权限?
- Merge 是否可能绕过验证?
- 预算耗尽、节点失败和人工拒绝分别进入什么终态?
- Trace 能否重建实际执行顺序和停止原因?
收藏清单
- Pipeline 管固定顺序,Loop 管反复反馈,Graph 管多个局部 Loop 的依赖和治理。
- 没有真实依赖、隔离状态和独立验证时,不要为了“多 Agent”画图。
- 并行分支写私有状态,Verifier 只读,Merge 独占最终写入。
- 静态外层控制预算和权限,动态内层处理开放探索,通常更稳。
- 所有架构比较都要有等预算单 Agent 基线。
- Graph Lab 的六个案例可以直接改造成自己项目的回归数据集。
下一篇进入 Human Control 与 Agent Security:把只读、可逆写入、外部发送和生产操作分到不同审批与权限路径中。