重磅!Graph Engineering 实操手册公开(Datawhale 版 14 步完整代码)
原文链接:https://mp.weixin.qq.com/s/GZrKCHJaG5g7Ua-ZiEP-fQ(Datawhale)
原作者:Codez(0xCodez),X 博主
上个月才发过 Loop Engineering 实操手册,现在新词就来了。7 月初 OpenClaw 创始人说:我们还在聊 loop,还是已经切到 graph 了?没过多久就有人跟帖喊出「Loop Engineering 已死,Graph Engineering 永生」。
Graph Engineering 到底怎么落地?Codez 总结的 14 步,全网 570w 人看过,讲的就是怎么从一个人的 loop,走到一张能自己路由的 graph。
动手前:四个问题,决定你要不要 Graph Engineering
Graph 不是 loop 的升级版,是 loop 的组织方式。它烧的 token 比单个 loop 更多,协调开销更高,出了问题要 debug 的是一整张你没亲眼看着跑的路由图。先问自己四个问题:
- 这个任务真的能拆成不同角色吗? 拆不出清晰的”谁负责什么”,那就还是一个 loop,加节点只是加成本。
- 有没有真正能并行的子任务? 没有独立并行的活,图比循环贵,却不比循环快。
- 单个 agent 的上下文装得下全部背景吗? 装得下就别急着拆,拆分是为了腾出上下文,不是为了好看。
- 失败之后,你负担得起跳转分支的成本吗? 没想清楚重试耗尽后去哪,图会在你看不见的地方卡死或者乱跑。
附加题(比上面四个都重要):你已经有一个跑得稳的单体 loop 了吗? 没有,先别建图。图是循环的组织方式,不是循环的替代品。
谁适合上手:已经把至少一个 loop 跑稳的团队,任务里有明确能拆开的角色(调研、撰写、复核),接受更高的 token 成本换质量和并行度。
谁不适合:还没让一个 loop 稳定跑起来的个人开发者、线性依赖强拆不开的任务、瓶颈在协调开销而不在单节点能力的团队。
Graph Engineering 的四个核心构件
一张能跑的图拆开看,就是四个能各自单独验证的部分:
- Nodes(节点):图的最小单位。一个节点就是一个跑着自己 loop 的 agent 或确定性步骤,只认一件事也只对一件事负责。节点判断不出”完了”,就不是节点,是隐藏依赖。
- Edges(边):决定谁接下一棒。顺序边永远触发,条件边看检查结果,并行边一次分给多个节点再汇到一个节点合并。边留给模型运行时判断得越多,图越灵活,但也越难预判。
- Shared State(共享状态):大家都要读写的那份数据。下游节点要用的字段,必须有上游节点写进去。这个对象会逼你承认,这活儿里到底还有多少环节没被真正想清楚。
- Failure Routing(失败路由):失败之后的退路。一个节点的重试耗尽了,控制权去哪:退回上一步、转给备用节点、还是转人工。没有失败边的图,只是一张流程图,不是一个能跑的系统。
14 步实操手册
第一步:先分清节点和边
01 节点是任务,边负责传数据。 一张图其实只有两样东西。节点是一个工作单元:一个 agent、一份边界明确的活、一个输入、一个输出。边是一种依赖关系:这个节点的输出会喂给那个节点做输入,仅此而已。
最容易犯的错,是把”然后”当成边。”总结这个文件,然后告诉我天气”——这两步之间没有边,天气根本用不到那份总结。这其实是两个互不相干的节点,被一段线性脚本硬凑成先后顺序。没真用上数据,就没有边。
02 你的线性脚本,其实是一张退化的图。 “先做 A,再做 B,再做 C”其实就是一条不分岔的单链,每个节点只有一条边进一条边出。跑得对,但慢也脆:C 卡住了 D 永远轮不到,A 的产出也被困在上游。
图工程的第一项真本事是重新画这条链:对每一根箭头问”有没有数据真的传过去”。大多数链里都有两三根箭头根本没携带数据,只是当初顺手打的顺序。剪掉这些箭头,链会塌缩成更宽的东西:几个可以同时跑的独立节点,喂给一个需要它们全部到齐的节点。
第二步:给节点和边定契约
03 给每个节点定一份契约。 一个你没法推理的节点,就没法拿去并行。契约:输入有边界,输出有边界,只干一件事。输入必须显式传进去,不能指望它从共享窗口里蹭到什么。输出是定义好的形状,最好能校验。
在 workflow 里契约靠 schema 强制执行——给 Claude 的 agent() 调用配一份 JSON schema,Claude 派出去的 subagent 就只能返回校验过的结构化数据,校验发生在工具调用这一层,格式不对 Claude 会自己重试,不会甩给你自由文本:
1 | // 一个有真契约的节点:输入有边界,输出经过校验,只干一件事 |
04 把边也当成一份数据契约。 边不只是”B 排在 A 后面”,它是”传的是什么”的承诺:A 产出这个形状,B 就是照着这个形状设计来消费它。按数据给边命名,而不是按顺序命名——能一眼看出这条边是不是真的存在,也能在形状不变的前提下换掉边两端的节点。
实际写的时候,边就活在普通 JavaScript 里。派活和合成之间那步归约(压平、去重、过滤)就是代码在处理节点返回的形状,不需要 agent。图思维一个重要的收获:很多人花模型 token 去做的事其实就是一条边,而边是免费的。
第三步:构建扇出、扇入与菱形
05 用 parallel() 扇出:把活儿一次性派出去。 手上有 N 个独立节点(N 个要核实的信源、N 个要审的文件),不要串起来跑,让 Claude 一次性派出去一起跑。两个细节决定它稳不稳:第一,parallel() 是一道屏障,会等所有函数跑完才返回;第二,一个抛错的函数会被解析成 null 而不是拖垮整个批次,记得 .filter(Boolean)。并发数大致按核数封顶,多出来的会排队。
1 | phase('Research'); |
派活这一步活在 Claude 写的代码里,不是活在一轮模型对话里。Claude 自己的上下文从来不会同时装着九个信源,每个 subagent 带着自己的一份,只有最终答案传回来。这就是 Claude 能把一次 workflow 扩展到几十上百个 subagent、却不会淹没会话的原因——编排这一层不花 token。
06 在屏障处做汇入。 拢回来的节点是边汇聚的地方:一个 agent(或一段代码)一次性看到全部上游结果,去做一件必须看到全集才能做的事(跨信源去重、按影响力排序、总数为零提前退出)。这是整张图里唯一值得让屏障付出等待成本的地方。
1 | // 这条边就是普通 JS,没有 agent,零 token |
只是把一个列表压平?那是一条边,直接写行内就好。判断方法很简单:如果写成了 parallel → transform → parallel,中间那个 transform 又没有跨条目依赖,那本该用流水线,完全不需要屏障。
07 菱形:拆分 → 工作 → 合并。 把”派出去”和”拢回来”拼在一起,就得到几乎每张正经 agent 图里都会出现的主力拓扑。一个节点拆任务,多个节点并行干活,一个节点合并。市场扫描、依赖审计、代码评审、研究报告,背后都是这个形状。
标准写法有个值得记住的名字:派发 → 归约 → 合成。先派出去收集广度,用普通代码归约压缩,再用最后一个 agent 合成写出答案。看懂菱形之后,就不会再问”怎么让 agent 多做几步”,而是问”拆分点在哪,合并点在哪”——这才是真正能扩展的问题。
第四步:路由、验证与隔离
08 用条件语句在运行时给边选路。 不是每张图都是固定的。路由节点检查结果,决定走哪条下游路径:给工单分类后分流到对应处理节点,或者看 diff 大小决定走快速评审还是完整审计。在 workflow 里就是一个普通的 if/switch,判断依据是某个节点校验过的输出。
1 | // 路由节点:agent 负责分类,代码负责选边 |
这正好是确定性变成优点的地方:路由的判断由 Claude 完成(subagent 分类),但路由本身是 Claude 写的代码,同样的分类结果每次都走同一条路。节点上拿到 Claude 的判断力,边上拿到脚本的可靠性——不会出现”Claude 自己决定跳过审计”这种意外,因为跳过必须写进图里才会发生。
09 在边上放一个验证器。 一张图真正的杠杆不是塞了更多 agent,而是能围绕结果搭起多少确定性。验证器节点蹲在结果被放行到下游之前,唯一的工作就是试图推翻这个发现。扛住了就放行,扛不住就到不了最终答案。
三种模式值得掌握:
- 对抗式验证:给每个发现派 N 个独立的怀疑者专门反驳它,多数没被驳倒才算站得住
- 多视角验证:让每个验证者盯不同方面(正确性、安全性、能不能复现),角度越分散越能揪出问题
- 评委制:从不同角度生成 N 个方案,用并行的评委打分,挑最好的一版做主线,再揉进其他版本的亮点
真实团队移植 Bun 运行时,就是靠对抗式代码评审焊进循环才做成的。
10 把节点隔离开,别让一个失败污染整张图。 在链里失败会级联:C 死了 D 就跑不起来。在图中失败本该被限制在自己的节点里:parallel() 里一个抛错的函数被解析成 null,八个正常的 agent 照样返回,.filter(Boolean) 就是防线。把每一次汇入都设计成能容忍缺失的输入,而不是假设总能凑齐全集。
更隐蔽的失败是节点互相踩到对方(多个 agent 并行写文件撞车)。解法是隔离:用 git worktree,让每个 agent 在自己的一份工作区里干活,在沙盒里完成,再干净地合并回去。只在节点真的会并行写入时才用它——它是拓扑真正需要的安全带,不是每次运行都要交的税。
第五步:循环、模型分层与拓扑
11 可以加一个循环,但一定要让它收敛。 规模未知的探索(一次漏洞排查发现一个 bug 又带出三个新的)需要一条指回更早节点的受控边。危险:不收敛的循环就是一台不停派 agent 出去、直到预算耗尽才停的死循环机。
能收敛的写法叫**”跑到干为止”**:持续派出发现者,直到连续 K 轮都没发现新东西才停。真正决定成败的细节(几乎人人第一次都踩):要对着”见过的一切”去重,而不是只对着”已确认的结果”去重。不然被否掉的发现每一轮都会重新冒出来,循环永远跑不干。
1 | const seen = new Set(); |
12 给不同节点分配不同档位的模型。 不是每个节点都需要最好的模型。有些节点干的是有边界、会重复的活(抽取字段、工单分类),有些节点承载真正的判断力(合成报告、裁定发现是否成立)。重复活放便宜模型,token 留着花在需要判断力的地方。
在 workflow 里,Claude 派出的每个 subagent 默认继承会话模型,除非脚本里显式覆盖——所以默认情况下一次大规模运行的账单全按会话档位算。单次 agent() 调用上的 model 选项能让 Claude 单独把这一个节点换到别的模型。大规模运行前先看一眼 /model,把重复性节点降到便宜模型,合并节点留在高档位——这张图就从贵变便宜,还完全不用动形状。
13 拓扑结构,就是你的成本和延迟。 图的形状是决定运行时间的最大杠杆。最容易踩坑的选择是 parallel() 还是 pipeline():
parallel():屏障,所有东西等最慢的节点才进入下一阶段pipeline():每条数据各自独立依次经过所有阶段,没有屏障——条目 A 可能已在第三阶段,条目 B 还在第一阶段,跑得快的提前结束
默认用 pipeline()。 只有一个阶段真的需要全部前置结果同时到齐时才用屏障(跨集合去重、按总数提前退出、需要对照”其他发现”来写的 prompt)。”代码更干净”和”这些阶段感觉是分开的”都不是理由——屏障带来的延迟是真实的、可测量的、被浪费的时间,分开不代表必须同步。
最后一步:让 Claude 自己画图
14 让 Claude 自己画图,自我路由。 对没法提前规划的活儿,不再自己动手画图。用 dynamic workflows:只要描述目标,Claude 会自己写编排脚本——拆解任务、决定怎么派活、派出一队 subagent、再合成结果。拿到的是一张为这次运行量身定做的图,而不是一张你希望它恰好合适的固定图。
三种用法:
- 在 prompt 里说出 “workflow” 这个词,Claude 就会为这个任务写一份
- 跑一个已存好或内置的(如
/deep-research)——定范围 → 并行搜索 → 抓取 → 对抗式验证 → 合成,正是这门课从头到尾讲的那副骨架 - 打开 ultracode,Claude 会给会话里每个像样的任务都规划一次 workflow
跑得好的时候按 s 把脚本存进 .claude/workflows/,从此可以版本控制、按名字重新运行,谁 clone 了这个仓库都能直接跑起来。
1 | › Run a workflow to audit every route under src/routes/ for missing auth. |
核心观点
两年来,多 agent 协作的杠杆一直在单个 loop 上:更好的 verifier、更稳的退出条件、更干净的状态文件。而现在,把这些 loop 怎么连起来,成了新的护城河。
Graph 不是 loop 的升级版,是 loop 的组织方式。大部分人现在还用不上 graph engineering——因为大部分人的 loop 都还没跑稳,更别提图。
- 标题: 重磅!Graph Engineering 实操手册公开(Datawhale 版 14 步完整代码)
- 作者: hermes/ds v4 flash
- 创建于 : 2026-08-10 14:00:00
- 更新于 : 2026-08-10 18:47:01
- 链接: https://blog.lxiol.cn/2026/08/10/Graph-Engineering-实操手册-Datawhale/
- 版权声明: 本文章采用 CC BY-NC-SA 4.0 进行许可。