Agent 工程化(三):编排的本质——从单 Agent 到多 Agent 协作
编排不是让 Agent 变得更聪明,是让它们各自做对的事。
你的 Agent 已经会查数据库了,会写代码了,会发通知了。单看每一项都还不错。但当你让它同时做这三件事——先从数据库查出问题,写一段修复代码,再发邮件通知相关人员——事情就开始失控了。上下文窗口塞满了数据库 schema、代码规范、邮件模板,Agent 开始用写代码的逻辑去组织邮件内容,或者在应该发通知的时候跑回去重新查一遍数据库。你不禁怀疑:这东西单独用的时候明明挺靠谱的?
单 Agent 的瓶颈不是能力不够,是上下文和角色冲突。一个人再强,也没法同时当产品经理、工程师和测试——你脑子里同时装三套完全不同的指令体系、工具描述和历史记录,最后的结果一定是互相干扰。Agent 也一样。
那什么时候该拆成多 Agent?怎么编排?拆完之后怎么保证不翻车?
这三篇系列走到这里,第一篇搭了骨架,第二篇注入了知识,这一篇想聊的是——当骨架和知识都不够用的时候,怎么通过编排让多个 Agent 分工协作。
先说清楚一件事:为什么要拆。
答案不是”多个 Agent 更厉害”。说实话,如果你的任务足够聚焦、上下文足够干净,单个 Agent 完全够用,拆了反而增加复杂度。需要拆是因为三个实实在在的天花板。
上下文污染是第一个。不同任务的指令、工具描述和历史记录混在同一个 context 里,会互相干扰。这不是理论上的担忧,是实际会发生的——你在 system prompt 里同时塞了”写代码要遵循 PEP8”和”邮件要简洁得体”,Agent 就可能在代码注释里用邮件的口吻写东西,或者在邮件正文里冒出变量名。原因很简单:注意力机制在所有的 token 之间做加权平均,任务边界模糊的时候,不同领域的模式会串。
然后是角色漂移。Agent 在执行过程中会逐渐偏离原本的职责。一个挺典型的例子:让 Agent 写代码,它写着写着开始觉得项目结构”不够优雅”,自作主张地去重构目录、删掉它认为”不必要”的文件。你说它错了吗?它的推理链看起来挺有道理的。但问题在于——你只让它写一个函数,它给自己加了活。单 Agent 没有角色边界,所有任务共享同一个”人格”,一旦中间某个环节触发了 Agent 的”优化欲”,后面的任务就跟着跑偏了。
还有一个容易被忽略的问题——单点故障。一个环节出错,整条链路崩溃,没有隔离。比如你的 Agent 在调用一个外部 API 时超时了,陷入重试死循环,而同一个 Agent 负责的其他任务也跟着卡住——因为它在同一轮对话里,重试占用了推理路径,后面的任务排不上队。没有隔离带,一个着火的房间会把整栋楼烧掉。
说白了,多 Agent 的本质是三件事:分工、隔离、并行。不是为了”更厉害”,是为了让每个 Agent 的上下文干净、角色明确、故障不扩散。一个人干三件事会顾此失彼,三个人各干一件事就不会。这不是个人能力的问题,是认知负载和协作效率的问题。
那怎么拆?拆完之后怎么把它们组织起来?
我用同一个场景来对比:自动化代码审查系统。一个 PR 提交上来,你需要多个 Agent 协作完成审查。这个场景足够复杂,能暴露出不同编排模式的差异。
最直觉的做法是 Pipeline——Agent A 做完交给 Agent B,Agent B 做完交给 Agent C,严格串行。代码审查里就是:代码提交 → 静态分析 Agent 扫一遍 → 安全审查 Agent 再查一遍 → 最后报告 Agent 生成总结。每个 Agent 的输入是上一个的输出,职责清晰,谁先谁后一目了然。可预测、好调试,出了问题顺着链路找就行。但慢也是真的慢——静态分析跑了五分钟,安全审查就在那干等。一个环节卡住,全线等待。对于有严格依赖关系的任务(B 的输入确实需要 A 的结果),Pipeline 是合理的。但如果任务之间没有先后依赖,你就是在浪费时间。
如果你发现多个审查任务其实可以同时进行,那就该考虑 Fan-out / Fan-in 了。一个调度器把代码同时分发给三个 Agent:Bug 检测看逻辑错误,性能分析看热点,安全审查查注入和权限。各自独立跑,最后汇总成一份报告。并行带来的速度提升是实打实的。但汇总那个环节会变成头疼的事——三个 Agent 的输出格式可能不一样,严重程度的定义可能不统一,甚至结论可能互相矛盾。你得在汇聚阶段做大量的格式对齐和冲突消解工作。
再往上走一层,如果你不仅需要并行,还需要一个”大脑”来统筹全局,就用 Hierarchical 编排。一个主 Agent 负责拆解任务、分配给子 Agent、汇总决策。在代码审查场景里,主 Agent 接到”审查这个 PR”的指令后,先分析变更的范围和涉及的模块,然后把不同文件分给最合适的子 Agent——后端代码给 Bug Detector,API 变更给 Security Reviewer,样式变更给 Style Checker。子 Agent 各自做完,主 Agent 再汇总、去重、按严重程度排序。主 Agent 保持了全局视野,能做更智能的任务分配和结果整合。代价是它本身成了瓶颈——上下文窗口要装下所有子 Agent 的摘要,它挂了整个系统就停了。
还有一种比较特殊的模式——黑板模式(Blackboard)。没有严格的调度关系,多个 Agent 独立读写一个共享的状态空间(就是”黑板”),通过黑板间接协作。代码审查场景下,多个 Agent 同时分析一份代码变更,各自把发现写在黑板上,互相能看到对方的结论,甚至可以补充和修正。加一个新 Agent 不需要改任何调度逻辑,直接往黑板上写就行。听起来很灵活,但灵活性的另一面是协调成本——谁先写?写冲突了怎么办?两个 Agent 对同一段代码给出相反的评价怎么处理?这些都需要额外的机制来管理。
怎么选?从 Pipeline 开始。 它最简单,够用就不要加复杂度。当你发现等待时间成了瓶颈,升级到 Fan-out。当你需要更智能的任务分配和全局决策,用 Hierarchical。至于 Blackboard,留给你真的需要高度灵活、松耦合协作的场景——说实话,大多数生产系统暂时还用不到。
编排不只是画个流程图。
很多人一开始接触多 Agent 编排,会把它想象成画一个 DAG(有向无环图),定义好节点和边,任务就自己跑起来了。在 Demo 里确实是这样。但到了生产环境,你会发现 DAG 只解决了”谁先谁后”的问题,真正的工程挑战在别处。
最头疼的是状态管理。Agent A 产出了 5000 token 的分析结果,Agent B 不需要全部信息,你怎么做截断或者摘要?这些问题看似琐碎,在生产里经常变成最大的坑。常见的做法是定义一个标准化的消息格式(比如 JSON schema),用中间存储(Redis、文件系统、数据库)做传递,而不是直接在 prompt 里塞全部内容。
状态管理如果没做好,错误来了就更难办。子 Agent 挂了怎么办?重试几次?还是失败的话是降级还是跳过?需不需要通知人类介入?不同环节的策略可能完全不一样——安全审查失败了不能跳过,但风格检查失败了可以先跳过,后续补上就行。你需要在编排层为每个 Agent 定义失败策略。
还有几个容易被忽略但真会出事的细节。超时控制——Agent 不是每次都能在合理时间内完成。有时候是 LLM API 响应慢,有时候是 Agent 陷入了循环推理。你需要为每个环节设置超时阈值,超时之后有明确的 fallback 逻辑。不然一个 Agent 卡住了,下游全卡住,排查的时候你会发现日志里什么有用的信息都没有。并发控制——同时跑太多 Agent,API rate limit 打满,成本飙升;跑太少,并行的优势出不来。这本质上是个资源调度问题。
然后是可观测性和成本控制,这两个其实是一体两面。多 Agent 系统出了问题怎么排查?你需要 trace——每个 Agent 的输入是什么、输出了什么、花了多长时间、消耗了多少 token。像分布式链路追踪一样,给每个任务一个 trace ID,串联起所有 Agent 的执行记录。每个 Agent 的 token 消耗都要追踪,尤其是层级式编排里,主 Agent 的上下文会随着子 Agent 的增加而膨胀,成本不是线性增长而是指数增长。不然一个月跑下来账单能吓你一跳。
这些是编排的”运维层”。就像 K8s 不只是调度容器,还管健康检查、自动扩缩容、滚动更新、资源限额。编排也是一样——DAG 只是表面,底下是一整套系统工程。
说完了模式,用一个完整的案例把前面的东西串起来。
假设我们要构建一个多 Agent 代码审查系统。采用 Hierarchical 编排,结构如下:
- Orchestrator Agent:接收代码变更,分析变更范围,拆分审查任务
- Bug Detector Agent:专注逻辑错误和边界条件
- Security Reviewer Agent:专注注入、权限、敏感数据
- Style Checker Agent:专注代码风格和一致性
- Report Agent:汇总所有审查结果,去重,按严重程度排序,生成报告
有几个设计决策值得聊一下。
为什么选 Hierarchical 而不是 Fan-out?Fan-out 更简单,直接把代码同时扔给三个 Reviewer 就行。但问题在于——代码审查不是无脑并行。一个 PR 改了 50 个文件,其中 30 个是样式变更,15 个是后端逻辑,5 个涉及 API 权限。Fan-out 模式下三个 Agent 都要读全部文件,浪费且容易产生重复发现。Hierarchical 模式下 Orchestrator 先做一轮分析,把文件按类型分给最合适的 Reviewer,既省 token 又减少重复。
错误处理策略上,如果某个 Reviewer Agent 超时了,我选了降级方案——跳过那个 Reviewer 的结果,用已有的部分报告继续推进,同时在报告里标注”安全审查未完成,建议人工复核”。这比整体阻塞要好得多。宁可给一份不完整的报告,也不要让用户等到超时然后什么都没拿到。
成本控制方面,大文件不全量分析,只审查 diff 部分。一个 5000 行的文件,这次只改了 30 行,那只有这 30 行及其上下文会进入 Reviewer 的 context。看起来是个小优化,但在大型项目里能省掉 80% 以上的 token 消耗。
简化的编排流程大致是这样的:
1 | 输入:代码变更(PR diff) |
每个箭头背后都有一组工程细节——状态传递的格式、超时阈值、重试策略、token 预算。这些细节才是系统真正能跑起来的关键。
三篇写完了,回头看看这个系列。
第一篇搭了 Agent 的骨架——LLM + Tools + Memory,加上 Harness 让它可靠地跑起来。第二篇聊了知识——RAG 让 Agent 能”懂”你的私有数据,核心是检索质量和注入策略。这一篇讲的是协作——当单个 Agent 的上下文和角色边界撑不住的时候,通过编排让多个 Agent 分工协作,核心不是 DAG 画得漂亮,而是状态管理、错误处理、可观测性这些工程细节。
拼在一起就是:骨架让它能动,知识让它能懂,编排让它能协作。 三者缺一,Agent 就还停留在 Demo 阶段。
最后说一点务实的感受。多 Agent 编排听起来很酷,也是现在 Agent 框架宣传的重点。但我觉得对于大多数场景,先让单 Agent 跑稳比急着拆成多 Agent 重要得多。很多问题看起来是多 Agent 的需求,实际上只是单 Agent 的 system prompt 没写好、上下文没管理干净。等你真的碰到了上下文污染、角色漂移这些天花板,再拆也不迟。拆是手段,不是目的。


