1. 从"一问一答"到"任务闭环":多 Agent 编排到底解决了什么
用 Claude Code 写代码的人,大概都经历过这个阶段:打开终端,敲一句需求,等它吐出一段代码,看一眼,不满意,再补一句"这里改一下",再等,再改。整个过程像在跟一个反应很快但记性一般的实习生对话——单步、线性、上下文越聊越乱,聊到第十轮的时候,它已经忘了你第一轮说的约束条件。
这就是单步聊天模式的天花板。它不是模型能力不够,而是交互范式的问题:所有信息都挤在一条对话流里,规划、执行、验证、修正四个动作互相污染,谁也没法专心干好自己的事。
多 Agent 编排要解决的就是这个。它的核心思路很朴素:把一件复杂的事拆成几个角色,每个角色只对自己那一段负责,角色之间通过明确的输入输出契约传递信息。规划的人不写代码,写代码的人不做验收,验收的人不负责修 bug——听起来像把一个人的活分给一个小组,但关键在于,这个"小组"的协作规则是你用脚本和配置写死的,不依赖临场发挥。
我自己的体感是,单步聊天适合"我知道要什么,只是懒得敲"的场景;而多 Agent 编排适合"我知道目标,但路径需要探索、需要反复验证"的场景。前者是打字加速器,后者才是真正的工程化助手。
1.1 单步模式的三个隐性成本
很多人觉得单步聊天"够用了",是因为没算清楚它的隐性成本。我把它拆成三块:
上下文污染成本。一条对话流里,你前面随口说的一句"用 Python 写",会一直挂在上下文里。等到后面你想让它生成一段 shell 脚本时,它可能还在纠结 Python 的语法。上下文越长,这种"历史包袱"越重,模型的注意力被稀释,输出质量肉眼可见地下降。
验证缺失成本。单步模式下,模型说"改好了",你只能自己去看、自己去跑。它没有动力去验证自己的输出,因为对话范式里根本没有"验证"这个环节。结果就是你要反复当人肉编译器,一遍遍把它的代码贴到终端里跑。
状态丢失成本。聊到一半关掉终端,或者上下文超限被截断,之前积累的所有决策就没了。下次打开,你得重新交代一遍背景。这在长任务里是致命的。
1.2 多 Agent 编排的本质:把"对话"变成"流水线"
多 Agent 编排的本质,是把非结构化的对话,转换成结构化的流水线。每个 Agent 是一个处理节点,节点之间有明确的输入输出格式,整个流程可以被脚本驱动、被日志记录、被重复执行。
这里有个关键认知:Agent 之间的"通信"不应该是自然语言闲聊,而应该是结构化的数据。比如规划 Agent 输出的不是一段"我觉得你应该先做 A 再做 B"的散文,而是一个 JSON 数组,里面每个元素包含任务描述、依赖关系、验收标准。执行 Agent 拿到这个数组,逐条处理,输出带状态标记的结果。验证 Agent 再拿着验收标准去核对。
这种设计的好处是,任何一环出问题,你都能定位到具体的节点和具体的字段,而不是在一大段对话里大海捞针。
1.3 什么任务值得上多 Agent
不是所有任务都值得。我的判断标准是三条:
- 任务能被拆成有依赖关系的子任务。如果一件事就是"生成一段正则",那单步就够了,拆了反而增加开销。
- 子任务之间有明确的验收标准。比如"这个函数要能通过这三个测试用例",标准越硬,验证 Agent 越有用。
- 任务需要多轮迭代。一次成型的事不需要闭环,需要反复试错的事才需要。
举个我实际用过的例子:给一个老项目补单元测试。这件事天然可以拆成"扫描代码找未覆盖函数 → 为每个函数生成测试 → 跑测试看是否通过 → 失败的重新生成"。四个阶段,每个阶段都有明确产出,这就是典型的多 Agent 场景。
2. 拆解 Claude Code 的多 Agent 编排骨架
Claude Code 本身是一个终端里的编码助手,它的多 Agent 能力不是靠一个"多 Agent 按钮"实现的,而是靠子 Agent 调用 + 任务分解 + 结果聚合这套机制拼出来的。理解这套骨架,比记住某个命令重要得多。
2.1 主 Agent 与子 Agent 的职责边界
在 Claude Code 的体系里,主 Agent 扮演的是"调度者"角色,子 Agent 扮演"执行者"角色。这个分工不是随便定的,背后有明确的工程考量。
主 Agent 掌握全局上下文,知道整个任务的目标、约束、当前进度。它的职责是分解任务、分配任务、汇总结果。子 Agent 则被赋予一个相对封闭的子任务,它不需要知道全局,只需要把手头这件事做到位,然后返回结构化结果。
为什么要这样切?因为上下文窗口是稀缺资源。如果所有子任务都在主 Agent 的上下文里执行,那主 Agent 的上下文会被大量中间过程塞满,很快就超限了。把子任务下放给子 Agent,相当于给每个子任务开了一个"临时工作区",用完即弃,主 Agent 的上下文始终保持清爽。
我踩过的一个坑是:一开始我让主 Agent 事无巨细地记录每个子 Agent 的完整输出,结果跑了三个子任务上下文就爆了。后来改成子 Agent 只返回"结论 + 关键证据",主 Agent 只保留结论,上下文压力立刻降下来。
2.2 任务分解的粒度控制
任务分解的粒度是个技术活。分得太粗,子 Agent 还是得做一堆事,等于没拆;分得太细,调度开销和通信开销会吃掉所有收益。
我的经验法则是:一个子任务应该能在一次独立的模型调用里完成,且产出物可以用一两句话描述清楚。比如"为 user_service.py 里的三个函数生成 pytest 测试"就是一个合适的粒度;"重构整个后端"就太粗,"给 login 函数加一行注释"就太细。
还有一个容易被忽略的点:子任务之间尽量保持无状态。也就是说,子 Agent A 的输出不应该依赖子 Agent B 的中间状态,而应该依赖主 Agent 明确传给它的输入。这样做的好处是子任务可以并行、可以重试、可以替换,整个流程的鲁棒性会高很多。
2.3 结果聚合与冲突消解
多个子 Agent 跑完之后,结果汇总到主 Agent 这里,经常会出现冲突。比如两个子 Agent 都改了同一个文件,或者一个子 Agent 说"这个函数没问题",另一个说"这个函数有 bug"。
这时候主 Agent 需要一套冲突消解规则。我常用的策略是优先级 + 证据权重:验证类 Agent 的结论优先于生成类 Agent 的结论,因为验证是后置的、更接近事实的;带具体证据(比如测试输出、报错信息)的结论优先于纯主观判断。
如果冲突实在无法自动消解,主 Agent 应该把冲突点明确抛出来,交给人来判断,而不是自己瞎猜一个。这一点很重要——自动化流程最怕的不是报错,而是悄悄做了一个错误的决定。
3. 闭环自愈:让 Agent 自己发现并修复问题
多 Agent 编排如果只做到"分解任务、并行执行、汇总结果",那它只是一个批处理系统。真正让它从"批处理"升级到"智能体"的,是闭环自愈——Agent 能自己发现执行结果不符合预期,并主动发起修复。
3.1 自愈闭环的四个环节
一个完整的自愈闭环包含四个环节:执行 → 检测 → 诊断 → 修复。
执行环节产出结果,检测环节判断结果是否达标,诊断环节分析为什么不达标,修复环节针对原因做修正。四个环节首尾相接,形成一个循环,直到检测通过或者达到重试上限。
这里的关键是检测环节必须有客观标准。如果检测靠模型自己"感觉对不对",那闭环就是假的,因为模型很容易自我感觉良好。客观标准可以是:测试用例是否通过、编译是否成功、lint 是否报错、输出是否符合预定义的 schema。
我见过不少人搭的"自愈"流程,检测环节就是让模型自己说一句"看起来没问题",然后流程就结束了。这种自愈等于没有,因为模型根本不会主动承认自己错了。
3.2 检测信号的来源设计
检测信号从哪来?我总结了几个可靠的来源,按可靠性从高到低排:
| 信号来源 | 可靠性 | 适用场景 | 注意事项 |
|---|---|---|---|
| 测试用例执行结果 | 极高 | 有测试覆盖的代码 | 测试本身要可信 |
| 编译/构建结果 | 极高 | 强类型语言 | 只验证语法不验证逻辑 |
| Lint/静态检查 | 高 | 代码规范 | 规则要配置合理 |
| Schema 校验 | 高 | 结构化输出 | schema 要覆盖关键字段 |
| 模型自评 | 低 | 无客观标准的场景 | 只能作为辅助信号 |
实际用的时候,我一般会组合两到三种信号。比如代码生成任务,先用编译结果做第一道筛,再用测试用例做第二道筛,最后用 lint 做风格检查。三道都过了才算通过。
3.3 诊断环节:从"报错"到"根因"
检测环节告诉你"失败了",诊断环节要告诉你"为什么失败"。这两件事的难度差了一个数量级。
诊断的核心方法是把失败信息结构化。比如测试失败,不要只把"AssertionError"丢给模型,而要把失败的测试名、期望值、实际值、相关代码片段一起打包。信息越完整,诊断越准确。
我常用的一个技巧是让诊断 Agent 先复述问题,再给假设。具体做法是:要求它先用自己的话描述"发生了什么",然后列出"可能的原因",最后给出"最可能的原因及验证方法"。这个"复述"步骤能显著降低它瞎猜的概率,因为复述过程本身就是在强迫它理解问题。
3.4 修复策略:重试、回退还是换路
诊断出原因之后,修复策略有三种:
重试:原因明确且是偶发的,比如网络超时、临时资源占用,直接重跑就行。
回退:当前路径走不通,退回到上一个稳定状态,换一种方式再试。比如某个函数怎么改都过不了测试,那就回退到原始版本,换一个实现思路。
换路:整个方案有问题,需要重新规划。这时候要把控制权交回主 Agent,让它重新分解任务。
这三种策略的选择,我一般设一个简单的规则:同一原因连续失败两次就升级策略。第一次失败重试,第二次失败回退,第三次失败换路。这样能避免在死胡同里无限重试。
提示:重试次数一定要设上限。我见过有人设了 10 次重试,结果一个死循环跑了半小时,烧了一堆 token 什么也没产出。我的习惯是单点重试不超过 3 次,整体循环不超过 5 轮。
4. Routine 脚本化:把一次性编排变成可复用资产
多 Agent 编排搭好之后,如果每次都要手动敲一遍流程,那它的价值就大打折扣。Routine 脚本化的意义,是把编排流程固化成可重复执行的脚本,让它从"一次性操作"变成"可复用资产"。
4.1 为什么需要脚本化而不是"记住流程"
有人会说,流程我都记住了,每次照着做不就行了。问题在于,人记住的流程是模糊的,脚本记录的流程是精确的。
模糊的流程里,"检查一下代码质量"这句话,今天你可能检查了 lint,明天可能只看了两眼。而脚本里写死的检查步骤,每次执行都一模一样。这种一致性,是自动化流程可靠性的基础。
另外,脚本化之后,流程可以被版本管理。你今天优化了一个检测步骤,明天可以对比优化前后的效果。这种可迭代性,是手动流程给不了的。
4.2 Routine 的结构:触发、步骤、退出条件
一个 Routine 脚本,我一般拆成三部分:
触发条件:什么情况下启动这个 Routine。可以是手动触发,也可以是某个事件触发,比如"检测到新提交的代码"。
步骤序列:Routine 的主体,一系列有序的 Agent 调用和数据处理步骤。每一步都要明确输入、输出、失败处理。
退出条件:什么情况下结束。正常结束是"所有步骤成功完成",异常结束是"达到重试上限"或"遇到不可恢复错误"。
这三部分里,退出条件最容易被忽略,但最重要。没有明确退出条件的 Routine,很容易变成僵尸流程,卡在那里既不出结果也不报错。
4.3 参数化:让一个 Routine 适配多种场景
写死的 Routine 只能干一件事,参数化的 Routine 能干一类事。参数化的关键是把变化的部分抽出来,把不变的部分固化。
比如一个"代码审查"Routine,不变的是"扫描 → 分析 → 生成报告"这个流程,变化的是"扫描哪些文件""用什么规则分析""报告输出到哪"。把这些变化的部分做成参数,一个 Routine 就能适配不同的项目。
参数设计有个原则:参数要少而精。参数太多,调用的时候容易搞混;参数太少,灵活性不够。我的经验是,一个 Routine 的核心参数控制在 3 到 5 个比较合适。
4.4 一个可落地的 Routine 骨架示例
下面是我常用的一个 Routine 骨架,用伪代码表示,你可以根据自己的工具链替换具体实现:
def code_review_routine(target_path, rules, max_retry=3): # 步骤1:扫描目标文件 files = scan_files(target_path) if not files: return {"status": "empty", "message": "没有找到待审查文件"} # 步骤2:逐文件分析 results = [] for f in files: retry = 0 while retry < max_retry: analysis = analyze_agent(f, rules) if validate_schema(analysis): results.append(analysis) break retry += 1 else: results.append({"file": f, "status": "failed", "reason": "分析结果不符合schema"}) # 步骤3:汇总报告 report = aggregate_agent(results) return {"status": "done", "report": report}这个骨架里,validate_schema就是检测环节,while retry就是自愈闭环,aggregate_agent就是结果聚合。三个机制串起来,就是一个最小可用的 Routine。
5. 实战中的坑:编排、自愈、脚本化各自的雷区
理论讲完了,说说实战。这三块我都在真实项目里踩过坑,有些坑还挺隐蔽的。
5.1 编排的坑:子 Agent 越权与信息泄漏
子 Agent 越权,指的是子 Agent 做了超出它职责范围的事。比如一个"生成测试"的子 Agent,顺手把被测函数也改了。这种越权在单步模式下不明显,但在多 Agent 模式下会引发连锁反应——下游的验证 Agent 拿到的是被改过的代码,验证结果就不可信了。
防范方法是给子 Agent 明确的权限边界。在 prompt 里写清楚"你只能修改测试文件,不能修改被测代码",并且在流程层面做检查——如果发现被测代码被改了,直接判定这一步失败。
信息泄漏是另一个坑。子 Agent 之间如果共享了不该共享的信息,会导致结果互相污染。比如两个子 Agent 都在做代码审查,如果它们能看到对方的中间结论,就可能产生"从众效应",第二个 Agent 倾向于附和第一个。解决办法是子 Agent 之间不直接通信,所有信息通过主 Agent 中转。
5.2 自愈的坑:无限循环与假性通过
无限循环前面提过,这里说个更隐蔽的:假性通过。
假性通过是指,检测环节显示"通过",但实际上问题没解决。常见原因有三种:检测标准太松、检测覆盖不全、检测本身有 bug。
我遇到过一次,测试用例全过了,但上线后立刻出问题。排查发现,测试用例是我让模型生成的,而模型生成的测试用例恰好避开了有 bug 的分支。这就是典型的"检测覆盖不全"——测试通过了,但没测到关键路径。
防范假性通过的方法是引入独立的验证源。比如模型生成的测试用例,再用另一套规则做交叉验证;或者关键路径上强制要求人工确认。自动化流程里,任何单一检测源都不应该被完全信任。
5.3 脚本化的坑:硬编码与版本漂移
脚本化最常见的坑是硬编码。路径写死、模型名写死、参数写死,换个环境就跑不起来。
比硬编码更隐蔽的是版本漂移。脚本里调用的某个工具升级了,接口变了,脚本没跟着改,跑起来就报错。或者模型的行为变了,之前能通过的检测现在过不了。
应对版本漂移,我的做法是在脚本里加版本检查。启动时先检查依赖工具的版本,不符合要求就明确报错,而不是跑到一半才崩。另外,关键流程的脚本要定期回归测试,确保它还能正常工作。
6. 从单步到编排:我的迁移路径与取舍
最后说说迁移。从单步聊天迁移到多 Agent 编排,不是一蹴而就的,我自己的路径大概分了三步。
6.1 第一步:把重复的单步操作脚本化
最开始不要急着上多 Agent。先观察自己哪些单步操作是重复的,把它们脚本化。比如"生成测试 → 跑测试 → 报告结果"这个流程,如果每天都要做,就值得写成一个脚本。
这一步的收益是立竿见影的,而且风险低——脚本化只是把手动操作自动化,不涉及多 Agent 的复杂性。
6.2 第二步:引入验证环节,建立最小闭环
脚本化之后,下一步是引入验证。在脚本的关键节点加上检测,让流程能自己判断"这一步做对了没有"。
这一步的难点在于设计检测标准。我的建议是从最硬的检测开始,比如编译结果、测试结果,这些不需要模型判断,直接看退出码就行。等硬检测跑通了,再逐步引入软检测。
6.3 第三步:拆分角色,引入多 Agent
有了闭环之后,再考虑拆分角色。把"执行"和"验证"拆成两个 Agent,让它们各司其职。这一步的收益是质量提升,但成本也上来了——多一个 Agent 就多一份调用开销和协调复杂度。
我的取舍标准是:只有当单 Agent 的上下文压力明显影响质量时,才拆多 Agent。如果单 Agent 能搞定,就别拆。多 Agent 不是目的,是手段。
6.4 什么情况下应该退回单步
最后说个反直觉的:有些场景应该从多 Agent 退回单步。
比如任务很简单、一次就能做对的事,上多 Agent 纯属浪费。或者任务需要高度创造性的探索,多 Agent 的流程约束反而会限制发挥。再或者调试阶段,你需要快速试错,多 Agent 的流程太重,不如单步来得灵活。
我自己的判断是:多 Agent 适合"流程明确、需要反复验证"的任务,单步适合"探索性强、一次成型"的任务。两者不是替代关系,是互补关系。工具箱里两把工具都有,看菜下饭才是正道。
这套东西我陆陆续续搭了小半年,最大的体会是:编排的价值不在于 Agent 多,而在于每个环节的职责清晰、接口明确、验证可靠。一个设计良好的三 Agent 流程,比一个混乱的十 Agent 流程有用得多。如果你刚开始尝试,建议从一个最小的闭环做起——哪怕只有"执行 + 检测"两个环节,只要检测是硬的、闭环是通的,它就已经比单步聊天强了。