1. 为什么开发团队会需要一套多智能体协作系统先说个背景。我所在的团队去年接了一个内部研发支撑项目业务方提的需求很直接把过去需要5个人手工维护的日常流程比如需求解析、任务拆解、代码审查、文档同步压缩到一个人加一套自动化系统就能跑完。最开始我们尝试的是一套“单Agent全流程”方案也就是一个智能体从接收需求到输出最终结果所有事都管。结果跑了一个多月问题集中爆发。典型场景是这样的用户提一句“帮忙看一下支付模块最近报错变多的原因”单Agent拿到问题后先去日志系统里捞数据再翻代码仓库还要查监控面板最后写一段分析报告。看起来没什么问题但真实执行时这个Agent经常会在“捞日志”这一步反复重试因为日志系统返回的数据格式特别乱即便它成功拿到数据下一步翻代码时又因为仓库结构太复杂把路径搞错。最要命的是一次长任务跑下来上下文里混着日志原文、代码片段、监控指标它自己都分不清哪部分对应哪个系统最后给出的结论经常是“看起来有异常建议进一步排查”等于什么都没说。后来我们复盘发现这不仅是模型能力问题而是架构问题。单个智能体同时承担调度、分析、搜索、汇报多个角色它的上下文窗口、工具调用习惯、决策路径全挤在一块就像一个人又要看门又要写代码还要开会哪怕这个人再聪明产出质量也会快速下降。这个阶段基本让我们确信要解决真实工程问题靠一个万能Agent是不现实的得把不同职责拆出去让多个智能体各管一段再通过一套协作机制串起来。这就是我们决定做一套多智能体协作系统的直接原因。这个方向放到现在的技术圈里并不冷门也就是大家经常提到的 Agent Architectures 或者 Multi-Agent Collaboration。但市面上的资料大多停留在概念层讲“什么是多智能体”“有什么框架可以用”真正讲“团队里怎么把这件事落地、中间踩了什么坑、代码层面怎么组织”的内容很少。所以我这篇文章想用我们实际搭建一套多智能体系统的过程作为主线把架构设计、关键模块实现、调度与通信机制、以及上线后遇到的典型问题都梳理一遍。适合正在做 Agent 工程化、或者准备把 LLM 接入生产流程的开发者尤其是那种“已经跑通 Demo但一上真实业务就各种翻车”的场景参考价值会比较大。2. 多智能体系统的整体设计思路先定角色再定流程2.1 单智能体与多智能体到底差在哪先放一张对比表这个表基本代表了我对两种模式的理解。维度单智能体多智能体协作系统上下文空间全部任务信息挤在一个上下文里容易互相干扰每个智能体只保留自己职责范围内的上下文信息隔离更清晰工具调用一个智能体持有全部工具选择路径极长容易选错每个智能体只挂载少量工具选择范围小准确率更高扩展性新需求到来时只能给同一个智能体加指令指令会越来越臃肿新需求到来时新增一个智能体即可老智能体不受影响故障影响面一次工具调用失败可能导致整个任务失败某个智能体失败可以被其他智能体感知并补偿开发成本起步快几条提示词就能跑起步慢需要设计通信协议、编排逻辑、状态管理从表里能看出来多智能体的核心价值不是“人多力量大”而是把复杂任务切成多个简单任务让每个智能体只面对一个小而清晰的问题域。模型处理小问题的能力远好于处理大问题所以整体质量反而会提升。但这里也要泼一盆冷水多智能体并非银弹。如果你的任务本身很小比如“把这段文本翻译成英文”“总结一下这份PDF”单智能体完全足够上多智能体只会增加延迟和复杂度。我们内部定过一个门槛任务拆解后如果每个子任务涉及的领域、工具、上下文彼此差异不大那就用单智能体只有当下游步骤需要不同专长、不同知识背景、不同工具集时才值得拆成多智能体。2.2 “重构开发团队”到底重构了什么我们的系统名字里带“开发团队”是因为整个架构刻意模仿了真实研发团队的分工方式。系统内设了四类固定角色路由智能体相当于项目经理接收原始需求先做一轮意图识别判断该交给下游哪个执行者或者是否需要并行拆分研发智能体相当于一线开发持有代码仓库、编译环境、测试执行等工具负责具体编码和本地验证审查智能体相当于代码审查人只拿差异文件和规范配置不持有任何写操作类工具从代码风格、安全问题、性能隐患三个维度做检查文档智能体负责输出变更说明、接口文档、用户可见的总结它不碰代码只消费前面环节产生的结构化输出。角色定了协作关系也就清楚了。绝大多数情况下路由智能体负责启动任务并分发研发智能体执行完后把产物交给审查智能体审查通过后再交给文档智能体出总结。这个过程不是简单的链式调用因为每一步都可能产生回退。比如审查不通过系统会把审查意见原样返回给研发智能体让它修改后重新提交最多重试两轮两轮后仍不通过就直接转人工。这个“回退”机制是纯链式结构很难实现的也是多智能体系统能比单Agent链更接近真实团队协作的关键。我自己的体会是设计这个系统最先想清楚的不是用什么框架、用什么模型而是角色边界。哪个智能体负责哪些事、不负责哪些事、把哪类信息传给下游必须提前定义得非常死。角色边界模糊的系统跑起来很快就会变成两个智能体互相扯皮或者都在做同一件事。3. 核心落地细节消息协议、任务编排与工具挂载3.1 智能体之间用什么通信协议通信协议是整个多智能体系统的基础设施。我们最开始用最简单的方式下游智能体直接把自己需要的信息塞进Prompt里传给上游也就是所谓的“动态提示词拼接”。这种方式写起来确实方便但上线后立刻暴露两个痛点一是需要传的信息一多Prompt就变得特别长模型理解成本陡增二是没有结构后续想针对某个字段做重试或者日志追踪都要靠字符串匹配去捞非常痛苦。后来我们改成了结构化消息协议。每条消息统一使用下面的数据格式{ event_id: evt_20250601_001, source_agent: routing_agent, target_agent: dev_agent, intent: task_delegation, payload: { task_id: T-20250601-8, requirement: 修复支付回调重复通知的问题, constraints: [不允许修改数据库表结构, 需要补充单元测试], callback_address: result_bus }, timestamp: 2025-06-01T10:30:00Z, trace_id: trace_8f3a1 }这套协议里最重要的两个字段是 intent 和 trace_id。intent 决定了下游智能体拿到消息后该走哪条处理逻辑而不是让模型每轮都从 Prompt 里“猜测”自己被要求做什么trace_id 则把所有子任务串成一条调用链排查问题的时候顺着一个 trace 就能看完整条路径这可比人肉翻日志高效多了。通信方式上我们对比过三种方案分别是直接传递、黑板模式和事件总线模式通信方案优点缺点适用场景直接传递实现最简单调用关系直观耦合度高上游变更会波及下游任务流程非常固定变动很少黑板模式信息集中存放多个智能体可异步读写状态同步容易冲突排错较困难多个智能体同时协作、产出逐步累积的场景事件总线松耦合新增智能体不需要改动老代码中间件维护成本高调试链路变长智能体数量多、后期扩展频繁最后我们选了事件总线作为主通信方式但做了个折中核心链路上的路由、研发、审查、文档仍然保留明确的上下游关系只有扩展性的旁路能力比如消息通知、外部日志同步、辅助分析才走总线。这样既避免了无序的总线风暴又保留了扩展空间。3.2 任务编排谁来拆任务怎么拆谁来决定要不要并行任务编排是实现多智能体系统时最容易被低估的部分。一开始我们天真地以为让路由智能体拿到需求后自己想一想怎么拆、拆完直接分发就行但实际跑起来发现LLM 拆任务这件事的稳定性远没有想象中那么高。它经常拆出一些不存在的依赖关系比如明明两个子任务互不影响它非要给排成串行或者拆的时候给每个子任务写了极其宏大的描述但下游智能体根本不知道第一步该做什么。后来我们引入了一套“规则优先模型兜底”的混合拆解策略。流程是这样的需求进来后系统先跑一轮规则匹配看看能不能命中我们预先配置好的任务模板。比如“修复某个功能模块的Bug”这个类型模板里已经规定好要拆几大步、每步对应哪个智能体、需要调用什么工具、验收标准是什么。只有在模板匹配不上、需求确实属于新类型时才允许路由智能体自由拆解。并且它拆解后的结果不是直接下发而是要经过一个校验器检查每个子任务是否有明确目标、是否包含验收标准、是否出现模糊依赖。校验不通过就直接打回重拆最多打回两次。关于并行执行我的原则就是“能并行尽量并行但必须显式声明依赖”。比如一个需求里同时涉及前端页面调整和后端接口改造这两部分没有强依赖路由智能体就会拆成两个子任务并发投递但后端接口没出来之前前端对接文档是不能生成的这个依赖关系必须在子任务的 payload 里写死。下游执行时事件总线的调度模块会先看依赖状态只有所有前置任务都处于完成态当前任务才会被真正投递给对应智能体。这套机制跑起来之后明显感觉到系统整体吞吐量上去了原来一个需求平均要6分钟跑完现在如果可并行度高3分钟左右就能完成。3.3 非核心智能体的挂载方式与工具权限隔离多智能体系统的“敌人”是让一个智能体拿着太多工具。表面看智能体手里工具越多越强大但模型做工具选择时的和系列模型就不太一样候选工具数量一旦超过十几个选择准确率就会明显下降误调失败率也跟着涨。我们在系统里对每个智能体做了严格的工具白名单。路由智能体只挂需求解析工具和任务模板查询工具研发智能体挂代码读取、代码提交、构建触发、测试执行四个工具审查智能体只挂差异读取和规则配置读取连写权限都没有文档智能体更简单只有一个读取结构化结果然后渲染成文档的工具。这个隔离既是稳定性考虑也是安全考虑。审查智能体如果有了写代码的能力万一模型在某次生成中动了不该动的业务逻辑后果不堪设想。所以工具权限一定要从架构层面卡死不能依赖“模型应该能管住自己”这种理想主义假设。我们的做法是在 Agent 启动配置里通过参数限制可用工具集合而不是让智能体自己去枚举全部工具。4. 从零搭建系统时的实操过程记录4.1 基础框架注册中心与智能体生命周期说回代码侧。我们基于一个内部轻量框架搭了整个系统没有用现成的多智能体框架原因有两方面一是我们内部已经有了稳定的模型调用基座和工具网关直接复用更省事二是现成框架往往封装太多“约定”出问题时排查链路太长我们更希望自己掌握每一层的结构。整个系统的核心模块是智能体注册中心。每个智能体在启动时都会往注册中心上报一条元数据记录内容包括智能体名称、职责描述、支持的 intent 列表、挂载工具列表、依赖模型版本、回调地址。调度模块在决定把消息投递给谁时先查注册中心看目标智能体是否在线、是否支持当前 intent、工具状态是否正常。这套注册机制让我们后续新增智能体非常方便新业务场景到来时只要注册一个带新 intent 和工具列表的智能体就能接入完全不用改动已有环节。关键代码长这样已脱敏用伪代码描述class AgentMeta: name: str intent_supported: list[str] tool_uris: list[str] callback_uri: str model_id: str status: str # idle / busy / offline / error注册中心会定时做健康检查如果一个智能体连续三次无响应就把它标记为 offline路由模块之后不会再投新任务进来。这个设计在早期帮了我们大忙因为 LLM 服务偶尔会超时如果不用注册中心做状态管理经常会出现任务被投递给一个已经卡死的智能体然后整个链路就一直悬着直到最终超时报错。4.2 数字员工间的任务交接从定义到实现任务交接是整个系统运行时的“血管”。我举个例子某次一个子需求被路由智能体拆下来交给研发智能体研发智能体处理完产出一个结构化的补丁描述里面包含改动文件列表、变更类型、测试结果摘要。这个补丁描述不会直接发给审查智能体而是先被存入消息中间件里一个叫“产物仓库”的地方同时向下游推送一条通知消息通知里只带产物ID和摘要。审查智能体接收通知后再按产物ID去拉取详细内容。这套“先存后推、引用代替全文”的交接方式是我们反复调整后确定的。最初我们直接在通知消息里塞全量产物比如把整个补丁 diff 都放进消息体结果下游智能体在处理时经常把注意力放在庞大的文本上反而忽略了审查重点。改成引用方式之后消息变得更轻下游可以自己决定要加载哪些信息、加载多少同时也能避免上游一次性把太多信息塞给下游导致上下文过载。如果你也在做类似系统我强烈建议交接信息保持精简尤其不要在大消息里混入冗余的中间推理步骤。4.3 人工介入与断点恢复多智能体系统再强也不能保证100%自动完成全部需求尤其涉及生产变更时人工审批必须预留入口。我们在调度模块里放了一个审批节点配置某些类型的子任务执行完成后不会直接进入下一步而是进入“待审批”状态消息推给项目负责人由负责人在界面上点击通过或驳回。这个人工介入不是全流程卡点而是按任务类型触发的比如只读分析类任务完全自动跑不打扰任何人但只要任务包含“写操作”比如修改代码并提交就必须过一道人工确认。断点恢复这块我们刚开始做得不好系统重启后所有进行中的任务都丢失了得靠人工重新发起。后来我们给消息中间件加了持久化所有任务状态和消息都会写入本地存储系统重启后会根据存储中的状态做一次恢复扫描未完成的任务重新投递已经完成但还没被下游消费的继续投递。这一步让系统的可用性提升了一个量级毕竟真实团队不可能因为某个成员掉线就从头再来一遍系统也一样。5. 上线后最常见的几个问题与排查方法5.1 死循环和重复消费多智能体系统跑久了最容易遇到的一个问题就是死循环。典型场景是审查智能体反馈“代码有问题”研发智能体改了一版审查智能体又发现新问题再打回研发再改如此往复。虽然我们设置了重试次数上限但有一个隐蔽的情况容易漏掉就是“看似不同、实际相同”的修改。比如研发连续两次提交了几乎一样的代码只是因为修复说明措辞不同审查智能体以为有进展实际上代码根本没变。最后我们加了“变更指纹”机制每次提交核心 diff 时生成一个哈希指纹如果指纹和上一版本相同系统直接判定本轮修改无效提前终止该分支并转人工。消息重复消费也踩过坑。事件总线模式下消费端挂了之后消息被重新入队消费者恢复后可能把同一条消息再处理一遍。我们的解决方案是让每个智能体在处理消息前先查一条消费记录表同一消息ID在表里有记录就直接跳过用幂等消费把重复问题消化掉。5.2 上下文污染一个智能体被另一个智能体的任务带偏这个问题的典型表现是研发智能体在处理需求B时会因为消息队列里还残留着需求A的部分上下文导致输出内容里混入需求A的术语和信息。一开始我们以为是Prompt设计问题反复加提示词试图约束但效果一般。后来才意识到问题出在“会话复用”上。我们把智能体设计成常驻服务内部长期持有同一个会话窗口前一个任务的上下文残渣自然留到了下一个任务。修正方案很简单每个任务开始前强制清空会话状态加载新的系统提示词和任务上下文并把版本号写清楚。这个改动上线后上下文污染问题基本消失输出稳定性明显上了个台阶。如果你也在做类似常驻智能体服务建议直接把“任务会话隔离”作为基本约定不要在同一个会话里处理两个不同任务。5.3 可观测性没有全链路追踪的多智能体系统就是盲盒调试多智能体系统比调试普通服务痛苦得多因为消息在多个智能体间跳动出现问题你根本不知道在哪一跳丢了。这个感受是在一次线上事故里被“教育”出来的某需求跑到一半审查智能体那边一直没动作但我们无法判断是消息没发出、发出没收到、收到了但处理卡住、还是处理了但没回调。后来我们花了半天时间人工核对日志才定位到是回调地址配置错了。这种排障效率完全不可接受。之后我们把全链路追踪补了起来。每个需求进来时生成一个全局 trace_id所有消息、所有智能体执行单元都会记录这个 trace_id并上报执行日志和执行耗时。排查问题时只要输入一个 trace_id就能看到完整的消息流转链路和每一步的执行情况。这么做之后系统的透明度和可维护性完全不一样了。另一个实用的可观测性配套是每个智能体执行结束时都会上报一段“执行摘要”内容包括本轮任务目标、实际产出、存在的问题而不是只上报一个“成功”状态。靠着这些摘要整个系统就像每个成员在写日报一样出了事随时能追溯当时的判断逻辑。把这些问题都解决之后这套多智能体协作系统算是真正稳下来了。也正是这些经验让我意识到多智能体系统本身不神秘真正决定它能不能落地的是下面这些“工程问题”。通信、状态、工具权限、可观测性每一层都要做得足够扎实缺一个环节整个系统就会在最意想不到的地方给你上一课。6. 这些经验沉淀下来之后我建议的搭建路径如果现在让我重新在另外一个场景里搭一套多智能体协作系统我会按下面这个顺序推进而不是一上来就写代码先画角色网络图。把业务场景里涉及的职责列出来明确每个角色的输入、输出、持有工具、决策权限画清楚谁依赖谁。这一步产出的是架构文档不是代码。再定义最小消息集。找出核心链路上必须传递的所有信息类型为每种类型定义消息格式只保留刚够下游完成工作的字段多一个都不要加。消息结构越精简后期加字段越容易这比一开始设计一个“万能消息结构”要实际得多。接着写一个可以手动触发的线框流程。先用硬编码方式让路由智能体固定把某类需求发给研发智能体研发完成后固定发给审查智能体不接真实工具只打印结果。这一步的作用是验证消息协议和角色边界是否合理跑通了再接入真实模型和真实工具。然后接模型和工具但要控制范围。每次只给一个智能体增加一个工具观察它在真实任务中的表现确认稳定后再挂下一个。不要一次性把所有工具全挂上去否则出问题的时候你没能力判断到底是哪个环节导致的。最后上可观测性和审批节点。在接入真实业务之前必须先把 trace 链路、状态持久化、人工审批全部铺好。这些不是锦上添花的功能而是系统能不能长期跑下去的地基。再多说一点团队内部对“多智能体”的态度容易走向两个极端一种是过度神话觉得上了多智能体所有难题都解决了一种是过度抵触觉得这只是 LLM 花活不可靠。我的实际体验是它的确解决了单Agent在处理复杂长链路任务时的很多问题但它把复杂性转移出了模型层面转移到系统层面的通信、状态协调、工具管理上了。你用系统架构换来了更好的模型纪律和更清晰的职责边界这才是它真正的价值。最后分享一个小技巧如果你不想一上来就重造轮子又不想被某个框架绑死可以先跑一个“容量最小但闭环完整”的验证一个路由带两个执行智能体处理一个你们团队真实存在的业务场景把消息协议、产物交接、回退机制、日志追踪完整跑起来。这个过程会让你快速感知到真正的难点分布在哪里。等这版稳定了再往里面加角色、加工具、加并行分支系统会越跑越顺局面也就彻底打开了。