大多数人只用了 AI 能力的 5% 到 10%。真正拉开差距的,往往不是又学会一个提示词技巧,而是开始设计一整套工作如何被完成。
这也是大公司里一类真实岗位的分水岭:一个人负责做一项工作,另一个人负责设计一百项工作怎样同时推进、彼此交接、自动检查,并最终汇总成一个结果。
原文作者 Anatoli Kopadze 回忆,他在丹麦读大学时上过一门课,课程几乎只研究一件事:把流程画成图,再想办法让它尽可能高效。当时听起来很抽象。现在,这正是 AI 工程师在讨论的 Graph Engineering。
这篇文章会把它拆开讲清楚:什么是图,怎样识别拖慢系统的假依赖,哪种结构最值得复用,验证器为何必须使用独立上下文,图会在哪里悄悄失败,以及什么时候根本不该用图。
1. 从 Loop 到 Graph,这个概念到底从哪来
一个月前,大家还在谈 Loop。随后 Peter Steinberger 发了一句:
Are we still talking loops or did we shift to graphs yet?
我们还在聊循环,还是已经转向图了?
这句话好笑,是因为它说对了一半。
Loop 是让一个 Agent 反复改进同一件事:尝试、检查、调整,然后再来一轮。Graph 不是更高级的单循环,而是一组互相连接的循环。多个节点并行工作,一些循环监督或修正另一些循环,不再只有一个 Agent 追着一个指标跑。
工程师很快指出:这其实是一个用了几十年的老概念,只是换了新名字。没错。反而正因为它不新,才更值得认真看。关键系统已经用这类结构跑了很多年,我们现在只是把它应用到了 Agent 上。
2. 图到底是什么
图就是一张可执行的 AI 工作计划。它只回答两个问题:
- 哪些工作需要完成?
- 哪项工作必须等待另一项工作的结果?
图里只有两种基本元素。
**节点(Node)**是一项边界清楚的工作。比如研究一个竞争对手、写一份初稿、核查一条事实。一个输入,一个输出,一项任务。
**边(Edge)**表示依赖关系。如果任务 B 必须使用任务 A 的结果,A 和 B 之间就有一条边,B 必须等待 A。只有真正传递了数据,这条边才算存在。
节点负责思考,边负责传递结果。词汇就这么多。

一个节点要想真正接入图,还需要一份契约:任务边界、输入格式、输出格式都要明确。
如果节点返回一大段自由文本,它通常只能交给人读。若输出遵循固定结构,下一个节点便能直接消费,不必猜测字段含义,也不需要人站在中间转述。
text
▸ 节点契约
任务:研究一个竞争对手的定价,只做这一件事
输入:{ competitor: "name", url: "https://..." } ← 必须显式传入,不能自行假设
输出:{ price: number, plan: string, source: url, date: "YYYY-MM-DD" }
约束:强制校验 Schema;如果 Agent 返回自由文本,拒绝并重试
原因:只有明确的输出,后续节点才能在无人介入的情况下直接读取
3. 一个问题,找出所有假依赖
把你现在的 AI 工作流从头走一遍。每到一步,只问一句:
这一步真的需要上一步的结果吗?
如果需要,边是真的,保留顺序。如果不需要,这条边就是假的,等待时间纯属浪费,两项任务可以并行执行。
例如:"先检查文件 A 的 Bug,再检查文件 B 的 Bug。"读起来像一个顺序流程,但检查 B 根本不会使用 A 的结果。它们之所以串行,只是因为你按这个顺序写了出来。
让两项检查并行,总耗时就由较慢的那项决定,而不是两项时间相加。
大多数工作流里都能找到两三条这种假边。删掉它们,相当于白捡一段时间。

4. 你现在的流程其实已经是一张图
当你把 Agent 写成"先做 A,再做 B,然后 C,最后 D"时,严格来说已经画出了一张图。只不过这是最无聊的一种:一条直线,每个节点只有一条入边和一条出边。
它能运行,但很慢,也很脆弱。C 卡住,D 就永远不会发生,A 和 B 已经完成的工作也堵在上游。
图工程的第一项实用技能,就是重画这条直线。检查每一条边,删除没有数据传递的假依赖。原本细长的链会变宽:几项互不依赖的工作同时开始,最后汇入真正需要它们的节点。
这不是为了让图好看。一个 40 步的线性流程,延迟是 40 步相加,也有 40 个顺序故障点。同样 40 项工作改成图后,关键路径也许只有三到五层。它的耗时取决于最慢的一层,而不是所有任务的总和。
同样的工作量,可能从五分钟降到十五秒。
模型未必是瓶颈。你画出的那条线才是。
5. 最值得记住的结构:菱形
你不需要背一百种拓扑。观察成熟的 Agent 系统,会反复看到同一幅图:任务先拆开,多个 Worker 并行调查;结果经过检查与压缩;最后重新汇入一个综合节点。
这个结构叫菱形。正式说法是:Fan out、Reduce、Synthesize。
- Fan out:拆开并行,获得广度。
- Reduce:用普通代码去重、过滤和压缩。
- Synthesize:由最终 Agent 合成答案。

Claude 的 Research 功能就采用了类似结构:Lead Agent 规划研究角度,多个 Worker 同时收集材料,发现先被核查,最后才汇总成一份报告。
一旦看懂菱形,你问的问题会变。以前是"怎样让 Agent 多做几步",现在是"在哪里分叉,在哪里合并"。后一个问题才真正能扩展。
下面是一张市场扫描图的代码骨架:
javascript
// 一个市场扫描图:输入 workflow 后,由 Claude 写出的菱形结构
const angles = [
"与前三名竞争对手比较价格",
"买家在评论里抱怨什么",
"这个品类缺少哪些功能",
"未来 12 个月市场会往哪里走",
];
// FAN OUT:每个角度一个研究员,同时运行
const raw = await parallel(
angles.map(a => () => agent({
task: `研究:${a}。每项结论必须带来源 URL 和日期。`,
schema: Finding, // 结构化输出,不接受自由文本
model: "cheap", // 普通节点用便宜模型
}))
);
// REDUCE:普通代码完成,不调用模型,不消耗 Token
const findings = dedupeBySource(raw.flat().filter(Boolean));
// VERIFY:每项发现交给一个全新上下文的怀疑者,尝试推翻它
const survivors = await parallel(
findings.map(f => () => agent({
task: "尝试推翻这项发现,返回 keep | drop,并说明原因。",
input: f,
freshContext: true, // 绝不复用研究员的对话
model: "strong", // 判断节点使用强模型
}))
).then(v => findings.filter((_, i) => v[i].verdict === "keep"));
// SYNTHESIZE:一个 Agent 用幸存材料写最终报告
return agent({
task: "写成一份报告,按置信度排序并附上来源。",
input: survivors,
model: "strong"
});
整套方法都在这段骨架里:独立工作并行;去重交给零 Token 成本的代码;验证使用全新上下文;无聊节点用便宜模型;真正需要判断力的节点使用强模型;最后只综合一次。
市场扫描、代码审查、研究报告都能套用这副骨架,只要替换研究角度和节点提示词。
6. 独立检查器才是整张图的关键
这是最常被跳过的一步,也是真正的图与昂贵玩具之间的分界线。
模型很容易漏掉自己犯的错误。让同一个模型检查自己的结果,它通常会过于宽容。所以,完成工作的 Agent 不能同时负责验收这项工作。
在结果流向下游前,放一个独立验证节点。它的任务不是润色,而是尽力杀死这条发现。经得住攻击才放行,否则就地丢弃。
还有一个更容易被忽略的条件:检查器必须使用干净上下文。
如果验证器读过 Worker 的完整对话,它很可能只是顺着已有论证点头。多个 Agent 共用一个上下文,看上去是图,本质上仍是同一个循环换了几套字体,错误模式并没有改变,只是成本更高、暴露更晚。
真正的验证器只接收待检查的结果,不接收生产过程。它也不检查"Agent 是否声称完成",而是检查真实信号:测试到底有没有通过,链接是否真的支持结论,数据是不是最新的。
text
▸ 验证节点
输入:Worker 的一项发现,仅限发现本身,不包含 Worker 的对话
上下文:全新、空白,从未看过它正在评判的工作
检查:三个怀疑者并行执行,分别回答不同问题
1. 结论正确吗? → 这项主张能否经得住核查
2. 信息仍然有效吗? → 来源是否够新,而不是过期材料
3. 来源真实吗? → 链接是否存在,是否真的支持被引用的主张
通过:多数检查者允许后才保留
失败:在结果进入最终答案前丢弃
记住这条规则:Worker 和它的 Verifier 不应共享上下文。共享之后,就又变成了一个循环给自己的作业打分,只是账单更大。
7. 图会在哪里悄悄坏掉
7.1 上下文坍塌
你可以并行启动一千个节点。但如果最终把一千份原始输出一次性塞进综合节点,合成还没开始,上下文窗口就已经爆了。
解决方式是分层 Fan-in:先分批汇总,再合并摘要,不要把原始结果堆给最后一个 Agent。
javascript
// 分层汇入:不要把 1,000 份原始输出直接倒进一个节点
const batches = chunk(results, 40);
const summaries = await parallel(
batches.map(b => () => agent({ task: "总结这一批结果", input: b }))
);
return agent({ task: "基于这些摘要写最终答案", input: summaries });
// 最终节点读取大约 25 份摘要,而不是 1,000 份原始输出
7.2 虚假的独立性
两个节点的提示词互不引用,看起来可以并行,但它们可能同时写同一个文件,或者抢同一个有限速的 API。这也是依赖,只不过藏在共享资源里。
Bun 团队第一次把大型任务分发给多个 Agent 时,Worker 共用同一工作区,结果相互覆盖文件。
每个 Worker 应该有自己的隔离空间。检查依赖时,不能只看数据,还要审计文件、数据库、锁、API 配额等共享资源。
javascript
// 隔离 Worker:不共享文件,也不共享工作区
await parallel(files.map(f => () => agent({
task: `重构 ${f}`,
worktree: true,
})));
// 两个节点只要会写同一个文件,就应该建立依赖,而不是并行
7.3 节点静默失败
在线性流程里,一个节点失败会让整条链停下。很烦,但很明显。
图里有两百个节点时,其中一个悄悄死亡,最终报告看起来仍然可能很完整。合并节点必须核对"实际收到的输入数"和"预计输入数",发现缺口就报警,不能拿半套数据照常综合。
javascript
// Fan-in 守卫:捕获悄悄死亡的节点
const results = (await parallel(jobs)).filter(Boolean);
if (results.length < jobs.length) {
flag(`WARNING: ${jobs.length - results.length} of ${jobs.length} nodes returned nothing`);
}
// 永远不要基于残缺输入生成报告,再把它叫作完整结果
8. 你真的需要图吗
图带来的是广度,不是更好的判断力。
它适合大量互相独立的工作同时展开。如果任务本身并不"宽",线性结构就不是瓶颈。以下情况可以跳过图:
- 任务很小或彼此孤立。 加一个函数、修一个 Bug,协调成本比执行成本更高,一个 Agent 更快也更便宜。
- 你需要逐步批准。 图的价值是让大量工作在没有持续人工介入时并行推进;每步都要审批,会抵消它的优势。
- 你还不知道自己在找什么。 探索阶段更适合一个随时可调整方向的 Agent,而不是提前锁定计划的一支舰队。
- 步骤确实严格依赖。 强行把顺序工作画成图,只会增加成本,不会提升速度。
- 找不到假边。 如果任意两项工作之间都有真实数据依赖,那就没有可并行的图。它是一个 Loop,而 Loop 完全没问题。
9. 最不讨喜、却最重要的东西:锚点
这里还有一个更深的陷阱。
你建出一张复杂的图:Worker 配检查器,审计节点监督业务节点,Meta 节点继续调优其他节点。每个节点都在监督另一个节点,但它们读到的全是系统内部生成的报告。
审计器拿财务报告核对数字,而财务报告本身就来自同一套系统。
结果处处一致,却没有任何东西得到真正验证。这样的图会像单循环一样失败,只是失败得更晚、更贵,而且一路亮着更多绿灯。

拓扑结构本身不会带来真相。图需要锚点,也就是无法靠语言争辩的外部信号:
- 测试不是"理论上应该通过",而是真的运行并通过。
- 收入不是报表里的预测,而是真的进入银行账户。
- 留存不是 Agent 给出的乐观解释,而是客户确实留下来了。
还有一些规则必须被冻结,禁止优化器修改。因为最容易被优化器削弱的约束,往往正是防止它作弊的约束。
一张图有多诚实,取决于里面有多少东西拒绝随它移动。让它面对无法反驳的数据,它才会脚踏实地;让它只给自己的报告打分,它迟早会自信地犯错。
10. 在 Claude Code 里搭一张真实的图
理论讲完,开始动手。
Claude Code 提供了一类动态工作流能力。关键提示词是 workflow。把这个词放进任务描述后,Claude 会把工作组织为一段协调脚本,再启动多个子 Agent,而不是沿着一条聊天链逐项执行。
重点在于:协调发生在代码里,不是靠多个对话互相转述。中间结果在脚本内部流动,不会每次交接都重新占用主会话上下文。
打开一个你熟悉的真实代码仓库,粘贴下面这段:
text
▸ GRAPH SPEC
GOAL: 审计 src/routes/ 下所有路由文件,检查是否缺少鉴权
FAN OUT: 每个文件一个 Agent,全部并行运行
VERIFY: 每项发现交给独立检查器,并使用全新上下文
CAP: 第一次运行最多处理 20 个文件
ON FAIL: 标记所有没有返回结果的文件,绝不能静默跳过
REPORT: 合并为一份缺少鉴权的路由清单
(提示词以 "workflow" 开头,让 Claude 构建图)
运行后,Claude 会先说明它正在构建 Workflow,而不是直接聊天回答,并展示计划供你批准。随后多个 Agent 同时处理各自文件,主会话保持空闲。
最后你不会收到二十段需要自己拼接的对话,而是一份合并报告。中间结果一直留在脚本内部,没有淹没上下文。

提示词里的"最多 20 个文件"很重要。它让第一次实验保持便宜,也提醒我们注意所有演示最容易回避的东西:账单。
11. 五张可以直接复制的图
下面五个模板本质上都是菱形,只是把它指向不同工作。打开 Claude Code,进入真实目录,把方括号内容换成自己的任务,然后粘贴。保留人工为发布前的最后一道门。
11.1 决策级研究台
它把问题拆成多个角度,研究员同时调查;怀疑者攻击每项发现;只有经得住核查的内容进入报告。
text
▸ GRAPH SPEC
GOAL: 围绕 [你的问题] 产出可用于决策的研究
FAN OUT: 拆成 5 个不同角度,每个角度一个研究员,并行执行
RULE: 每项发现必须带来源链接和日期
VERIFY: 怀疑者攻击每项发现并尝试推翻,失败的内容丢弃
MERGE: 将幸存材料合成报告,按置信度排序
SAVE: 保存为 research-report.md,然后展示最重要的发现
HUMAN GATE: 未经我确认,不得继续修改或发布
(提示词以 "workflow" 开头)
11.2 SEO 内容机器
每次生成一篇具备排名基础的草稿,但绝不自动发布。
text
▸ GRAPH SPEC
GOAL: 为 [主题] 写一篇具备搜索排名基础的草稿
PARALLEL JOBS:
1. 当前排名靠前的页面覆盖了什么
2. 用户围绕该主题真正提出的问题
3. 排名靠前的页面漏掉了什么
MERGE: 合成大纲,再写完整草稿
VERIFY: 事实核查器标记所有没有来源的主张
SAVE: 保存到 drafts/,并在顶部列出被标记的主张
HUMAN GATE: 永远不要自动发布
(提示词以 "workflow" 开头)
11.3 Go-to-market 套件
一次生成完整发布物料,但每个阶段仍由人批准。
text
▸ GRAPH SPEC
GOAL: 为 [产品] 制作完整发布套件,目标用户是 [受众]
PARALLEL JOBS(研究阶段):
1. 描绘买家画像,以及他们真实使用的表达
2. 找到这些买家在线上聚集的位置
3. 收集竞争对手如何向他们推销
MERGE: 合成一页定位文档
HUMAN GATE: 写作前暂停,先把定位文档交给我确认
PARALLEL JOBS(写作阶段):
1. 落地页文案
2. 一周的发布内容
3. 一组外联消息
VERIFY: 检查器将所有资产与定位文档对照,标记偏离内容
SAVE: 保存到 launch-kit/,未经确认不得再改
(提示词以 "workflow" 开头)
11.4 全仓库重构扫描
它可以覆盖单一上下文装不下的代码广度。
text
▸ GRAPH SPEC
GOAL: 找出所有超过 100 行的函数,并逐一提出重构方案
FAN OUT: 每个文件一个 Agent,并行执行
VERIFY: 每项重构方案交给使用全新上下文的独立检查器
DEDUPE: 所有方案彼此去重
CAP: 第一次最多处理 50 个文件
REPORT: 汇报实际返回的文件数,防止静默失败
(提示词以 "workflow" 开头)
11.5 大小未知的发现循环
适合那些开始前无法判断工作量的任务,例如找到一个 Bug 后,又暴露出三个新 Bug。
text
▸ GRAPH SPEC
GOAL: 在仓库中寻找 [安全问题 / 错误处理缺陷 / 死代码]
FAN OUT: 多个 Finder 并行运行
DEDUPE: 每项新发现与已有发现去重
VERIFY: 对幸存发现执行独立核查
LOOP: 连续两轮没有新发现时停止
CAP: 设置 Agent 总数硬上限,防止失控
REPORT: 按严重程度输出最终清单
(提示词以 "workflow" 开头)
第一次先缩小范围,观察实际成本,再逐步扩大。某次运行稳定后,把它保存下来,以后就能按名字重复调用。
12. 成本与监督:舰队不会免费工作
图比普通聊天贵得多。降低的是协调成本,不是实际工作量。每个 Agent 仍在消耗 Token,一整支舰队会消耗很多。
有一个公开案例很能说明问题:工程师用类似结构重写 Bun Runtime,在大约 11 天里,把约 53.5 万行一种语言的代码转换成超过 100 万行另一种语言的代码。人工完成接近一年。
这项任务运行了大约 50 个 Workflow,最高同时启动 64 个 Agent。总使用成本约 16.5 万美元,而且仍需要人类设计和持续监督。外界也提出了一个合理质疑:这么多 AI 生成的代码,是否可能得到安全、充分的审查?
这就是图的真实形状。它可以展开上千个 Agent,吃掉单一上下文根本装不下的任务;如果选错场景、没有成本上限、缺少锚点,它也能在后台悄悄烧钱。
重型 Graph 适合有预算、有硬限制、有监控能力的团队。其他人没有落后。先从一个很小的图开始,观察一次运行多少钱,只有当它证明自己划算时,才继续加宽。
13. 最后该记住什么
Graph Engineering 的强项是广度:把真正独立的工作同时展开。它的弱点也很明确:图不会自动带来判断力,而且用错场景会迅速抬高成本。
所以,不要把所有工作都改成图。先判断任务是否足够宽,是否存在可删除的假边,是否真的需要多个独立上下文。找不到两项互不依赖的工作,就继续使用 Loop。
最值得今天就做的练习,不是安装新工具,而是画出你当前的工作流。逐条检查边,凡是没有传递真实数据的依赖,都删掉。
多数人会继续把任务排成一条长队。少数人会开始画图,然后调度一支舰队。
原文作者:Anatoli Kopadze
原文:Graph Engineering explained: what it is, when to use it and when not to