恒美微站
首页
关于我们
建站服务
主题模板
案例展示
资讯中心
联系我们
多步Agent设计:从串行到图编排的进化
首页
资讯中心
/
多步Agent设计:从串行到图编排的进化
多步Agent设计:从串行到图编排的进化
发布时间:2026/8/12 14:30:56
很多人第一次设计多步 Agent 时都会自然地写成一条串行流水线A → B → C → D。这样设计很符合直觉平时代码就是这样按序、一步接着一步地执行下去。串行结构很好但真实任务很少会完全符合这种结构。一次代码审查会同时检查安全问题、性能问题和代码规范一次资料调研会并行处理多个来源。这些流程放在一起比起串行的一条线它更像一张关联的图。Agent Graph EngineeringAgent 图编排要解决的问题就是找到真实的任务数据流并按照这个图结构去组织 Agent。本文将会介绍图编排背后的设计原则再结合代码示例说明具体实现方式。其中本文提及的 agent()、parallel() 等接口只是 Claude Code 示例不同版本的 Claude Code 或其他 Agent 框架可能存在差异。但 Graph Engineering 的核心思想不变它同具体函数名称无关。工作流的真实结构要理解 Agent 图编排先得了解一件事即图可以表示任何一个 / 多个任务尤其适合表示复杂任务。从 Agent Workflow 的角度看一张图主要包含两个核心元素节点Node一个独立的工作单元。它可以是一个 Agent也可以是一段确定性的代码逻辑。每个节点负责一类明确的任务并有着清晰的输入和输出。边Edge节点之间的数据依赖关系用来表示某个节点产生的结果会被另一个节点使用。而要构成一条边的必要条件是数据真的从一个节点流向另一个节点时它们之间才存在边。例如你让 Agent 完成这样一个任务“总结这个文件然后告诉我天气。”在自然语言层面这两个步骤是连续的但它们之间没有数据依赖。天气查询并不需要文件总结的结果因此两者两个任务节点实际不会存在关联边。如果按照线性脚本设计流程就会是这样的文件总结 Agent ↓ 天气查询 Agent按照这种串行的工作流天气查询就会被迫等待一个与自己无关的任务完成。这就是 Agent Workflow 设计的常见问题执行顺序被误认为数据依赖。代码里的先后顺序只代表“什么时候执行”而图里的边代表“谁需要谁的结果”。明白这点之后很多 Agent 设计问题都会变得清晰。并行、隔离、分层这些优化其实是在重新梳理任务之间的数据依赖哪些步骤必须等待前置结果哪些步骤彼此独立可以被拆开执行。所以一个线性的 Agent 流程其实也能被图结构来解释它其实是一张只有单一路径的有向图A → B → C → D 罢了。这种工作流结构的问题在于只要有一个节点成为瓶颈将会影响后续所有步骤。如果这里 C 阶段失败了D 任务将无法开始。同时节点之间的连接关系也被固定下来前面 A 节点产生的结果只能沿着预设路径传递难以支持动态调整。线性 Workflow 的限制很多时候不在于代码实现而是任务结构本身没有被正确表达出来。节点设计要想把一个节点放进 Agent 图中它得有清晰的职责边界。如果一个节点依赖当前上下文中的隐含信息就很难进行拆分、并行执行或者替换。这类问题通常来自未声明的输入依赖会让节点和当前 Workflow 强绑定。以“前面那个 Agent 已经分析过了所以这里应该知道背景。”为例这种隐含的依赖关系会让节点和当前 Workflow 强绑定。一旦调整执行顺序或是尝试将它复用到另一个 Workflow 中这个节点可能会无法正常工作。因此一个可自由组合的节点得定义清楚它的输入、输出约定输入是什么输出是什么格式下游如何使用这个结果。有了约定之后节点才更像一个标准的软件组件可以被移动、替换和复用而不是只能依赖某一次对话上下文才能运行的临时步骤。在工程实现中约定一般通过 schema 来定义const ITEM { type: object, additionalProperties: false, properties: { title: { type: string }, url: { type: string }, impact: { type: string, enum: [high, medium, low] }, }, required: [title, url, impact], }; const result await agent(source.prompt, { schema: ITEM });上面这段代码的重点并不在 API 调用方式而是说明节点约定应该在哪里建立。通过 schema子 Agent 的输出结构会被限制在预定义的数据结构中。如果返回结果不符合要求运行时可以进行校验和处理而不是把一段没有结构约束的自然语言输出交给下游节点重新解析。有没有 schema 的区别在于有 schema 的输出是下游可以直接消费的数据结构没有约束的文本更适合作为人类阅读的结果而不是节点之间稳定传递的数据格式。schema 的作用就是让 Agent 的输出具备明确的数据结构从而可以连接到 Workflow 中的下一节点。数据流设计上面提到边代表的是节点之间的数据依赖关系那么它应该按照传递的数据来定义而不是按照执行顺序来描述。这个变化看似很小但对 Workflow 的设计方式影响很大。首先它会让节点之间的依赖关系变得可验证。“A 之后执行 B”这句话说明的是执行顺序并不能说明 A 和 B 之间是否真的存在数据关系。而“A 产出 ListItemB 消费 ListItem”就明确描述了两者之间的数据流A 的输出是否会被 B 使用B 是否依赖 A 提供的数据如果答案是否定的那么这条边实际上就不存在。其次基于数据约定来定义边可以让节点替换变得容易。只要两个节点产生相同格式的数据它们就可以在 Workflow 中互相替换Source A ↓ ListItem ↓ Process Agent Source B ↓ ListItem ↓ Process Agent虽然两个节点的实现不同但只要它们满足相同的数据约定都能连接到后续处理节点。如果只按照执行顺序设计流程这些数据关系和节点替换空间都会被隐藏。除了提升可组合性重新理解“边”还有一个重要的工程价值减少不必要的 Agent 调用。很多 Agent 系统消耗的 token并不是用在复杂推理上而是花在了本可以由代码完成的数据处理上。以典型的 fan-out → reduce → synthesis 流程为例reduce 阶段经常只是完成合并结果去除重复项筛选字段调整数据结构。其实这些操作有明确规则一般不用额外调用 Agent。参考// 边处理纯代码完成不消耗 token const flat collected.flatMap((c) c.items); const top [...new Set(flat.map((x) x.url))];想这种“让模型帮忙整理一下前面几个 Agent 的结果。”需求如果这里的“整理”只是格式转换、列表合并或去重比较合适交给代码处理。Agent 更适合处理需要判断、分析和生成的任务而不是承担数据搬运和格式转换。如果 Workflow 中每条边都依赖 Agent 来连接那么就是在为原本可以由代码完成的数据流转支付额外成本。Workflow 决定 Agent 系统的成本与延迟识别出任务之间的独立关系之后下一步就是利用这种独立性。最直接的方式就是把可以独立执行的节点并行展开。这里的要点在于理解图编排和普通 Prompt 的区别。为什么多个子 Agent 可以扩展而一个不断增长的巨大 Prompt 只会越来越难维护原因在于Agent 编排发生在代码层。编排逻辑由程序负责执行创建子 Agent 是运行时操作并不会额外占用主 Agent 的上下文空间。假设系统要同时处理 10 个数据源。单个 Agent 需要将 10 个来源的信息全部放入自己的上下文 而图编排会让每个子 Agent 只处理自己的输入最后再汇总结果。因此即便后面任务规模扩大单个 Agent 的上下文压力不会随着节点数量同步增长。这也是图结构的重要价值所在通过拆分任务系统可以处理单个 Agent 难以承载的复杂工作。const raw await parallel( SOURCES.map((s) () agent(s.prompt, { schema: ITEM })), ); const collected raw.filter(Boolean);上面代码中的 filter(Boolean) 对应了另一个重要设计故障隔离。在并行执行中不是所有节点都能够成功返回。如果某个子 Agent 执行失败稳妥的做法是把失败限制在当前节点而不是中断整个 WorkflowAgent A ✓ Agent B ✓ Agent C ✕ Agent D ✓ ↓ 继续处理 A / B / D 的结果因此fan-in 阶段要考虑部分结果缺失的情况而不是默认所有节点都会成功返回。这也是图结构相比串行流程的一个优势在线性 Workflow 中一个节点失败可能导致整个链路停止在图结构中失败可以被隔离在单个节点范围内。除了并行之外另一个影响 Workflow 性能的设计选择是parallel() 还是 pipeline()并行减少等待这种方式比较适合彼此独立的多个任务参考Agent A ↗ Input → Agent B ↘ Agent C所有节点可以同时开始整体等待时间接近最慢节点的耗时。以上图为例A 要 5sB 要 20sC 要 10s 的话整体将会接近 20s。流水线让任务持续流动这种方式适合经过相同处理流程的多个输入参考Input A → Stage 1 → Stage 2 → Stage 3 Input B → Stage 1 → Stage 2 → Stage 3当 A 进入 Stage 3 时B 已经可以开始 Stage 1。系统不需要等待所有输入都完成当前阶段再进入下一轮处理。设计 Workflow 时需要避免一个常见误区把逻辑上的阶段划分误认为执行上的同步等待。只有当某一步依赖完整集合时才要等待所有结果汇总跨来源去重根据整体数量提前终止比较所有候选结果。除此之外让所有任务等待同一时刻继续执行只会增加额外延迟。验证机制图结构不只是让系统可以运行更多 Agent还为 Agent 之间增加了一些单个模型难以可靠完成的环节比如验证、复查和反向质疑。一个 Agent 生成的结果一般只代表一次推理过程。如果让同一个 Agent 检查自己的答案它基于的信息还是之前那份依赖的推理路径也是同一个这会导致难以发现原始判断中的问题。因此提高系统可靠性的关键不是让生成者“再想一遍”而是引入一个独立的验证节点。简单来说Agent A 负责提出答案的话就让另一个 Agent B 来负责尝试推翻答案。参考// 对抗式验证多个独立 verifier 尝试反驳发现 const passed ( await parallel( findings.map((f) () parallel( [0, 1, 2].map(() () agent( 尝试验证这个发现是否成立${f.desc}, { schema: VERDICT } ) ).then((v) ({ f, keep: v.filter(Boolean) .filter((x) x.real) .length 2, })), ) ) ) .filter((x) x.keep) .map((x) x.f);这里的 verifier 并不是简单重复生成结果而是在结果交付前承担一个明确职责主动寻找当前结论可能存在的问题。只有经过验证仍然成立的结果才会进入下一阶段。这种设计利用的是 Agent 节点之间的独立性不同节点拥有不同的判断路径可以降低单次推理带来的错误风险。常见的验证方式主要有三类对抗式验证所谓对抗式验证就是让多个 verifier 从不同角度尝试推翻同一个结果。例如这个结论是否有足够证据支持是否存在反例推理过程中是否存在漏洞多个独立 verifier 的判断结果汇总后可以降低单次判断出错的概率。多视角验证多视角验证会让不同验证节点关注结果的不同维度。比如 Agent A 关注正确性、Agent B 关注安全性可复现性由 Agent C 来负责而 Agent D 则关注工程影响。相比让多个 Agent 重复执行相同检查多视角验证更容易发现不同类型的问题。评审团模式评审团模式让多个 Agent 分别生成或评价候选方案再由综合节点汇总结果。它类似人工评审流程多个候选方案 ↓ 多个独立评审 ↓ 综合最终结果这种方式适合需要比较、筛选和权衡的任务。动态 Workflow 设计原则有些 Agent 任务天然不是一次执行就能完成的。像持续发现新的资料、 不断定位新的 Bug、 反复尝试修复直到满足目标条件…这类任务一般是一个循环结构搜索 → 发现 → 验证 → 继续搜索 ↑ ↓ └──────────┘但循环有个最大的问题如果没有明确的停止条件它就会变成一个持续消耗 token 的流程。所以在设计 Agent Loop 时我们得先定义什么叫“一轮有效进展”。一种常见的定义方式是loop-until-dry连续 K 轮没有发现新结果后停止。参考下面代码const seen new Set(); let dry 0; while (dry 2) { const found /* 并行运行 finders收集结果 */; const fresh found.filter( (b) !seen.has(key(b)) ); if (!fresh.length) { dry; continue; } dry 0; fresh.forEach((b) { seen.add(key(b)); }); // 对 fresh 进行验证并加入 confirmed }这里有一个容易被忽略的细节去重应该针对所有已经发现的结果而不是只记录最终确认通过的结果。例如发现 A ↓ 验证失败 ↓ 下一轮重新发现 A ↓ 再次验证失败如果只记录“确认通过”的结果失败项会不断重新进入循环。Workflow 表面上仍然在探索新内容实际上只是在重复处理已经验证过的路径。而记录所有已经见过的结果seen { A, B, C }可以保证每轮探索都会排除已有结果让循环逐渐收敛。这不是一个简单的代码细节而是 Agent Loop 对“什么算是进展”的定义。图结构带来的另一个价值是让系统能够更精细地控制模型成本。当节点拥有清晰的输入输出边界后每个节点承担的判断任务也会更加明确。有些节点主要负责确定性工作分类信息抽取格式转换。上面这些任务一般不需要复杂推理。而另一些节点需要权衡不同方案综合多个结果做最终决策。这些节点才更需要强模型支持。所以模型成本不应该平均分配给所有 Agent而应该根据节点职责进行调整。例如agent(source.prompt, { model: smaller-model })可以让大量重复性的节点使用成本更低的模型而汇总节点验证节点决策节点这些节点继续使用能力更强的模型。图结构的价值就在这里它让不同职责的节点被拆分出来因此模型选择和成本控制也变得更加灵活。相比单体 Agent拆分后的 Workflow 更容易判断哪些环节值得投入更多计算资源。小结Agent 系统设计的关键不是让模型执行更多步骤而是找到任务真正的结构。线性 Workflow 是最容易开始的方式但复杂任务要进一步考虑任务之间的数据关系哪些任务可以拆开并行哪些结果需要验证哪些工作可以交给代码完成。通过节点和边的图结构来组织 WorkflowAgent 系统可以更好地利用任务之间的独立性降低成本提高稳定性。