Graph Engineering 完全指南:什么时候该用图,什么时候别用

大多数人只用了 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 工作计划。它只回答两个问题:

  1. 哪些工作需要完成?
  2. 哪项工作必须等待另一项工作的结果?

图里只有两种基本元素。

**节点(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

相关推荐
李剑一1 小时前
AI到底是在降本增效还是「降本增笑」?员工成本降下去了,AI反而成为最大的成本来源了!
aigc·ai编程
手写码匠2 小时前
华为云Flexus+DeepSeek征文|Dify 构建企业级联网搜索 Agent:查询改写、多源检索与引用溯源实战
人工智能·深度学习·算法·aigc
后端小肥肠17 小时前
我做了个能一键搭建个人工作台的 Skill,已开源
人工智能·aigc·agent
扯蛋43819 小时前
1.x 时代的记忆系统 (一)
langchain·llm·aigc
奈斯先生Vector20 小时前
把 Midjourney 二次编辑做成生产系统:customId 能力令牌、Action Graph 与 WebUI 精修工作台
数据库·人工智能·架构·aigc·音视频·midjourney
林澈在路上21 小时前
2026 主流 AI 写歌 APP 全面测评|零基础原创歌曲工具选购指南
大数据·人工智能·aigc·音视频·音频
ServBay21 小时前
Qwen3.8-Max 开源超大杯正式发布,如何让 AI 无感切换新模型
aigc·ai编程
奈斯先生Vector1 天前
解码 Midjourney V6 的精准控制力:从 customId 状态机制到 Web 端 Action 二次编辑工程全解析
前端·gpt·prompt·aigc·midjourney·ai编程·agi