Claude 图工程:从 0 到图架构师的 14 步路线图(完整课程)
- 将线性代理重画为图,切断无数据依赖的箭头,独立节点可并行运行
- 给每个节点定义有界输入输出契约,通过 JSON 模式强制验证结构化数据
- 使用 parallel()扇出独立任务,屏障处扇入归约,构建钻石拓扑
- 在边上放置验证器节点,用对抗性验证和多样化视角确保结果可靠性
- 根据节点复杂度分层模型,用 pipeline()替代屏障减少延迟,让 Claude 自路由
大多数尝试构建多步代理的人最终都得到一条直线。第一步、第二步、第三步,每一步都在礼貌地等待上一步完成后才开始。
十分之九的人会注意到,这些步骤中有一半根本不需要等待。
它们不需要路由,不需要分支,不需要并行化。它们只是排队,一个头、一个上下文、一次做一件事,直到窗口填满,代理忘记了自己在做什么。
关注我的 Substack 获取最新 AI 前沿内容:movez.substack.com
这是一份 14 步路线图,它能把那条单一的直线变成一张图:一张能够横跨整个舰队展开工作、自我验证发现、并聚合出单个代理永远无法承载的结果的图。

这里有一个没有人明说的转变。提示是一个句子。循环是一个周期。框架是代理立足的地板。
但工作本身的形状,什么在什么之前运行,什么可以同时运行,什么必须等待其他所有事情完成,这个形状就是一张图。节点进行思考。边传递结果。
Claude Code 已经发布了直接构建这些图的工具:动态工作流。
Claude 编写一个简单的 JavaScript 编排脚本,然后生成一个协调的子代理舰队来执行它,而协调本身消耗零模型令牌,因为它是代码,不是对话。

01. 节点是任务,边是流动的内容
一张图只有两样东西,把它们搞清楚就能解决大部分困惑。节点是一个工作单元,一个代理、一个有边界的任务、一个输入一个输出。
边是一种依赖关系:它表示这个节点的输出会送入那个节点的输入,仅此而已。

人们常犯的错误是把「然后」当作边。「总结文件,然后告诉我天气」这句话中,两者之间没有边,因为天气不需要消耗摘要。
这是两个被线性脚本无谓串联起来的断开节点。只有当数据真正穿过时,边才存在。
学会对你代理中的每一个「然后」提出这个问题:下一步是否读取了上一步的输出?如果没有,就没有边,等待就是浪费。


02. 你的线性脚本是一张退化的图
当你把一个代理写成「做 A,然后 B,然后 C,然后 D」时,你已经画了一张图:一条没有分支的单链。每个节点恰好有一条边入和一条边出。
它能正确运行,但也运行得缓慢且脆弱。因为链条没有冗余:如果 C 停滞,D 永远不会发生,A 的工作被困在上游,无处可去。

图工程的第一个真正技能是重画这条链。拿出你的线性代理,对每个箭头,问自己第一步的问题。
大多数链条有两三个不携带数据的箭头,它们只是你碰巧输入的顺序。
切断那些箭头,链条就会展开成更宽的形状:几个独立节点可以同时运行,供给一个需要它们全部的单个节点。

03. 给每个节点一份契约
一个你无法推理的节点,是一个你无法并行化的节点。解决方案是一份契约:有界的输入、有界的输出、恰好一项工作。
输入就是节点读取的任何内容,必须明确传入,永远不能假设来自共享窗口。输出是一个定义好的形状,最好经过验证,这样下一个节点可以直接使用而不需要猜测。

在工作流中,这份契约通过模式来强制执行。当你给 Claude 一个带有 JSON 模式的 Agent() 调用时,Claude 生成的子代理会被强制返回经过验证的结构化数据。验证发生在工具调用层,所以 Claude 会在不匹配时重试,而不是给你需要解析和祈祷的自由文本。
这就是 Claude 能够连接到图中的节点,与只有人类读取输出时才能工作的节点之间的区别。


04. 把边当作数据契约
边不只是「B 在 A 之后」。它是一个关于什么穿过的承诺:A 产生这个形状,B 被构建来消费这个形状。当你用数据而不是顺序来命名边时,两件事会变得更容易。

你可以立即看到边是否真实(数据真的在移动吗?),而且只要形状保持不变,你可以交换两端的节点而不会破坏图。
实际上,边存在于普通的 JavaScript 中。扇出和合成之间的归约步骤包括扁平化、去重、过滤,这只是对节点返回的形状进行操作的代码。
不需要代理。图思维的一个安静胜利是:人们大量消耗模型令牌的工作中有很大一部分实际上是边,而边是免费的。


05. 用 parallel() 进行扇出
这是为一切买单的一步。当你有 N 个独立节点时,N 个要检查的来源、N 个要审查的文件、N 个要审计的路由,你不需要串联它们。
你告诉 Claude 把它们扇出并立即运行。在工作流中,这就是 parallel():Claude 接受一个 thunk 数组,每个 thunk 生成一个子代理,全部并发执行,然后把结果数组交还给你。

有两个细节让它变得鲁棒。首先,parallel() 是一个屏障,它会等待每个 thunk 完成后才返回,所以下一阶段会看到完整的集合。其次,一个抛出异常的 thunk 会解析为 null,而不是拒绝整个批次,所以一个不可靠的代理不会毁掉整个运行。
永远记得对结果使用 .filter(Boolean)。并发上限大约是你的核心数,超过的会排队,所以你可以传入一百个 thunk,它们都会完成,只是一次处理几个。
扇出存在于 Claude 编写的代码中,而不是模型对话中。Claude 自己的上下文永远不会同时容纳九个来源,每个子代理携带自己的上下文,只有最终答案回来。
这就是让 Claude 能够将工作流扩展到几十个或几百个子代理而不会淹没会话的原因。编排层消耗零令牌,因为它不是 Claude 思考的另一轮。

06. 在屏障处扇入
扇出只有在有东西收集它时才有用。扇入是边汇聚的节点,在这里一个代理或一段代码一次性看到所有上游结果,并做一些需要完整集合的事情:跨来源去重、按影响排名、如果总数为空则提前退出。这是屏障值得它的墙上时钟成本的唯一地方。
保持图快速的规则是:只有当一个阶段真正需要每个先前结果在一起时,才使用屏障。跨所有来源去重?屏障,正确。
只是扁平化一个列表?那是边,内联完成。这个嗅觉测试残酷而简单:如果你写了 parallel → transform → parallel,而中间的转换没有跨项依赖,你应该使用 pipeline 并完全跳过屏障。
07. 钻石拓扑:拆分 → 工作 → 合并
把扇出和扇入放在一起,你就得到了每个严肃代理图的主力拓扑:钻石。
一个节点拆分任务,许多节点并行工作,一个节点合并。这是市场扫描、依赖审计、代码审查、研究报告背后的形状,交换来源和提示,同一个骨架就能适应。
标准形式有一个值得记住的名字:扇出 → 归约 → 合成。扇出来获取广度,用普通代码归约来压缩它,用最终代理来合成并写出答案。
一旦你看到了钻石,你就不再问「我如何让我的代理做更多步骤」,而是开始问「哪里拆分,哪里合并」,这才是真正扩展的问题。
08. 运行时用条件路由边
不是每张图都是固定的。有时走哪条边取决于节点发现了什么。路由器节点检查结果并决定触发哪条下游路径,分类工单,然后分支到正确的处理程序;检查差异大小,然后要么进行快速审查,要么启动完整审计。
在工作流中,这只是对节点验证输出的 JavaScript if 或 switch,因为控制流存在于代码中。
这就是确定性成为特性而非限制的地方。路由器的决定可以由 Claude 驱动,一个子代理进行分类,但路由是 Claude 编写的代码,所以对于相同的分类,它每次都以相同的方式运行。
你在节点得到 Claude 的判断,在边得到脚本的可靠性。不会出现「Claude 决定跳过审计」这样的意外,因为跳过必须被写入图中,而它并没有。
09. 在边上放一个验证器
图的真正杠杆不是更多的代理,而是你可以围绕它们构建来产生信心的结构。
验证器节点位于结果被允许向下游传递之前的边上,它唯一的工作就是试图杀死这个发现。如果它存活下来,就通过;如果没有,它永远不会到达答案。
有三个模式值得你掌握。
对抗性验证:对于每个发现,生成 N 个独立的怀疑论者,提示他们反驳它;只有大多数存活时才保留它。
视角多样化验证:给每个验证者一个不同的视角,正确性、安全性、是否可复现,因为多样性会捕获 N 个相同检查永远不会发现的失败模式。
评判小组:从不同角度生成 N 次尝试,用并行评判者打分,从获胜者那里合成,同时嫁接亚军的最佳部分。
这正是让一个真实团队将 Bun 运行时移植成功的模式,他们在循环中内置了对抗性代码审查。
10. 隔离节点,让一个失败不会毒化整张图
在链条中,失败会级联,C 死亡,D 永远不会运行,整个事情停止。在图中,失败应该被限制在它的节点。
这已经部分成立了:parallel() 内部抛出的 thunk 会解析为 null,所以八个好的代理仍然返回,而一个坏的退出。你的 .filter(Boolean) 就是遏制。
设计每个扇入来容忍缺失的输入,而不是假设有完整的集合。
更微妙的失败是节点互相踩到。当代理并行写入文件时,它们可能会冲突。
解决方案是隔离:「worktree」,每个代理在自己的 git 工作树中运行,在沙箱中完成工作,并干净地合并。
只在节点实际并行写入时使用它。这是需要它的那个拓扑的安全带,而不是每次运行的默认税。
11. 添加循环,但让它收敛
有时你直到进入工作中才知道任务有多大:未知大小的发现,一个漏洞扫描,找到一个漏洞会揭示三个更多。这需要一个循环,一个受控的边回到更早的节点。
危险是显而易见的:一个不收敛的循环是一个无限循环,它会生成代理直到你的预算耗尽。
收敛的模式是循环直到干涸:持续生成发现者,直到连续 K 轮没有新发现,然后停止。决定成败的一个细节,也是几乎每个人第一次都会犯的错误,是你去重的依据。
根据所有看到的内容去重,而不仅仅是确认的结果。否则被拒绝的发现每轮都会重新出现,循环永远不会干涸,你就建造了一台机器,付钱让它永远重新发现同样的死胡同。
12. 在节点之间分层模型
不是每个节点都需要你最好的模型。图以单个代理永远不会的方式使这一点变得明显:一些节点是有界和重复的,提取这个字段、分类这个工单,一些节点承载真正的判断,合成报告、裁决发现。
在更便宜的模型上运行无聊的节点,把你昂贵的令牌花在真正需要判断力的地方。
在工作流中,Claude 生成的每个子代理都会继承你的会话模型,除非脚本覆盖它,所以默认情况下,一次大运行完全按照你的会话层级计费。单个 Agent() 调用上的模型选项告诉 Claude 只将该节点路由到别处。
在大运行前检查 /model,然后让 Claude 将扇出的重复节点路由到更便宜的模型,同时保持合并节点在高级模型上。这是一个杠杆,它在不触及形状的情况下,将消耗大量令牌的图从昂贵变成经济。
13. 拓扑就是你的成本和延迟
图的形状不是装饰性的,它是墙上时钟时间的最大单一杠杆。绊倒每个人的选择是:parallel() 与 pipeline()。parallel() 屏障让一切都等待最慢的节点,然后下一阶段才开始。
pipeline() 独立地将每个项目流式传输通过所有阶段,没有屏障,项目 A 可以在阶段 3,而项目 B 仍在阶段 1。快速项目提前完成,而不是在慢速项目后面空转。
默认使用 pipeline()。只有当一个阶段真正需要每个先前结果在一起时,才使用屏障,跨集合去重、对总数的提前退出、一个比较「其他发现」的提示。「代码更干净」和「阶段感觉分开」不是理由;屏障延迟是真实的、可测量的、浪费的时间。分开不等于同步。
14. 让 Claude 画图:自路由
最后一步是,对于你无法提前计划的工作,停止手工画图。
使用动态工作流,你描述目标,Claude 自己编写编排脚本,分解任务、选择扇出、生成一个协调的子代理舰队,并合成结果。你得到一张为这次运行量身定制的图,而不是你希望适合的固定图。
有三种进入方式。在你的提示中说「workflow」这个词,Claude 会为这个任务写一个。运行一个保存的或捆绑的工作流,/deep-research 是一个已经在生产中发布的真实图:范围界定 → 并行搜索 → 获取 → 对抗性验证 → 合成,正是本课程中的精确骨架。
或者打开 ultracode,Claude 会为会话中的每个实质性任务规划一个工作流。当运行良好时,按 s 将其脚本保存到 .claude/workflows/,版本控制、可按名称重新运行,任何克隆仓库的人都可以启动的图。
本周用 Claude 构建的六张图
- 每条路由的安全扫描。Claude 为每个路由文件生成一个子代理,每个都在寻找缺失的身份验证检查,然后验证器通过确认每个发现,然后才到达报告。这是单个上下文无法承载的广度。
- 使用 /deep-research 的引用报告。一个已经在 Claude Code 中发布的图。Claude 将你的问题分解成不同的角度,运行并行搜索,去重来源,然后在写作前用三票怀疑论者对抗性地验证每个主张。
- 逐个文件移植模块。Bun 的上限,扩展到你的仓库。Claude 在文件之间扇出翻译,在每个文件上运行测试套件作为门,并将失败循环回去,对抗性审查捕获单次通过会发送损坏的东西。
- 差异的对抗性审查。Claude 根据差异大小路由:小改动得到一次快速通过,大改动触发完整的并行审计,评审人员使用不同的视角,正确性、安全性、性能,然后一个评判小组进行合成。
- 按计划进行的生态系统扫描。保存一次,永远重新运行。Claude 并行检查许多来源,版本、博客、讨论,在屏障处按影响排名,并撰写摘要。版本控制在 .claude/workflows/ 中,可按名称启动。
- 未知大小的发现。你不知道那里有多少错误。Claude 并行运行发现者,对每个新发现与所有看到的内容进行去重,验证幸存者,并持续循环直到两轮没有新发现,然后停止。
结论
提示者提出问题。架构师画出一张图。
线性代理从来不是天花板,它只是第一个形状,是每个人都能到达的形状,因为它匹配我们打字的方式。一行,一个头,一次一件事。
一旦你能看到节点和边,你就不再要求代理做更多,而是开始要求图做得更宽:在工作独立的地方扇出,在信心重要的地方门控边,在不需要判断的地方分层模型。
大多数人会继续排队步骤。那些学会画图的人将运行一支舰队,并且永远不会注意到其他人被困在下面的天花板。
评论互动