ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

Agency Agents多智能体协作系统:架构设计与落地问题剖析

Agency Agents多智能体协作系统:架构设计与落地问题剖析

接手这个Agency Agents多智能体协作系统项目之前,大概是去年这个时候,我所在的团队刚好卡在一个尴尬节点上:单体Agent在简单问答场景已经跑通了,但一旦丢给它一个"从零做一个内部工具站"的需求,它就开始"大包大揽"——前期需求还没理清楚就急着写代码,写到一半又发现自己理解偏了,回头改需求,最后产出几个互相矛盾的模块。这让我意识到一件事:真正拖垮项目的不是模型能力不够,而是我们把Agent用成了"一个人干一个团队的活"。团队协作靠的是角色分工和信息流转,智能体协作也应该是这样。于是就有了重构开发团队的想法,用一套名为Agency Agents的多智能体协作系统,把产品、开发、测试这些角色拆成独立Agent,通过一套协作协议让他们像真实团队一样互相配合、互相校验、接力产出。

这篇文章我会把这套系统的架构设计和落地过程完整拆开讲,包括为什么多智能体比单体Agent更适合复杂任务、角色与协作协议怎么设计、最小可用闭环怎么搭,以及我落地一个多月后踩到的几个大坑和排查链路。适合正在做Agent应用开发、或者准备把AI从Demo推到真实项目里的开发者参考,也希望给打算"用AI重构开发流程"的团队一个可复盘的样本。

1. 为什么多智能体比"一个万能Agent"更接近真实开发团队

先聊一个反直觉的现象:任务复杂到一定程度时,把一个Agent喂越来越多的上下文和指令并不会让它更像一个团队,反而会加速它的失效。Agency Agents这套系统的根本出发点,就是把开发流程的结构化约束交给系统架构,而不是寄托在单个模型的一次推理里。

1.1 单体Agent在复杂任务上的三个失效点

第一个失效点是角色混淆。一个Agent同时负责需求分析、技术设计、编码、测试时,它天然会倾向于"跳过分析直接实现",因为模型对"给出最终答案"的偏好远强于"严谨地走完过程"。我见过很多次:需求里只说了要和现有系统打通,单体Agent就默认采用某个特定方案去写代码了,根本没把接口约束、数据模型这些前置问题搞清楚。

第二个失效点是上下文失控。一个复杂任务拆成多个子任务之后,想让单体Agent同时记住最初的需求、中途的决策、以及最新出现的约束,几乎必须把所有历史对话都塞进上下文。上下文越长,模型对早期信息的注意力就越低,表现为"写到后面忘了前面的约定"。这不是提示词工程能解决的,是注意力机制本身的稀释效应。

第三个失效点是错误不可定位。单体Agent在一个长链路任务里一旦出错,你很难判断是需求理解错了、代码实现错了还是验证逻辑错了。因为没有环节边界,错误信息是混在一起的,排查成本极高,最后往往只能整段任务重跑,极其浪费时间和tokens。

这三个失效点让我确认了一件事:复杂任务需要的是"流程治理",不是"更强的单个模型"。真实开发团队为什么能并行推进、错误可控?因为有人专管需求、有人专管实现、有人专管验证,每个环节都有明确的输入输出和交付标准。Agency Agents要做的,就是把这种分工结构搬到代码里。

1.2 开发流程结构化的价值:不是裁人,而是降熵

很多人听到"重构开发团队"会以为是用Agent替换掉大部分程序员。我的实际体会恰恰相反,这套系统的价值在于"降熵":把开发流程里混乱、随意、靠人肉盯的部分,变成可追踪、可约束、可自动校验的协作协议。

举个例子,我们内部流传过一句话:流程里最贵的不是写代码,是"需求的模糊地带"。产品Agent在Agency Agents里承担的第一责任,不是写需求文档,而是把模糊描述结构化拆成字段齐全的任务卡片,包括用户故事、验收标准、依赖关系、风险点。开发Agent拿到的不是一句"帮我做个页面",而是一份已经拆好的、粒度合理的子任务。测试Agent也不用靠猜来写用例,因为任务卡片里明确写了验收标准。

这个结构化约束看起来增加了前期工作量,但长期看收益非常明显。我拉过一个简单的对比,同样做一个内部报表模块,手工流程里光是"需求澄清—开发理解偏差—测试返工"这一轮来回,往往要吃掉两三天时间。Agency Agents模式下,虽然每个子任务执行得更久一些,但整体链路是收敛的,返工率大幅下降。降熵的核心机制,就是让每一个环节的产出物都成为下一个环节的明确输入,没有灰色地带。

2. Agency Agents的总体架构:编排中心、角色代理与共享黑板

Agency Agents的整体架构我习惯用三层来描述:编排层、角色层、记忆层。很多人在设计多智能体系统时只顾着定义Agent角色,忽略了中间那张"协作网",结果系统跑起来跟若干个人在群里各说各话一样,毫无协同可言。实际上,协作的核心不是Agent本身,而是Agent之间的信息流和控制流。

2.1 编排层:把"人怎么配合"变成"消息怎么流转"

编排层是整个系统的大脑,它不负责具体的业务产出,只负责回答三个问题:当前这一步该由谁来做、它的输入从哪里来、它的输出要发给谁。这个思路借鉴了图编排的思想——把整个开发流程画成一张有向图,节点是各类Agent角色,边是消息传递路径。

实现上,我们用了一个任务状态机来管理每个子任务的流转。子任务的状态枚举是:待分配、执行中、待评审、通过、驳回、重开、完成。编排中心根据状态变化决定下一步动作,例如测试Agent上报一个缺陷,编排中心会把这个缺陷作为新任务分配给开发Agent,同时抄送产品Agent确认是否符合需求预期。

这套设计的关键,是把"流程的稳定性"和"Agent的灵活性"分开。流程本身是确定性的、可预测的,Agent只是流程上的执行者。这样出了问题时,你可以精确到是某个环节的产出不合格,还是流程规则定义有漏洞,而不是对着一个黑盒Agent干瞪眼。

2.2 角色层:一个Agent只干一类事

角色层是我在架构设计里纠结最久的部分。初期我们曾尝试让一个Agent身兼数职,期望它根据任务类型自动切换角色,结果发现这种"动态角色切换"对模型的理解能力要求极高,经常出现角色串味,比如开发Agent干着干着开始帮测试Agent写验证逻辑。

后来我们执行了一个很朴素的原则:一个Agent实例只绑定一个角色,角色职责越单一,产出质量越稳定。产品Agent就是拆需求、搞验收标准;开发Agent就是写代码、做自检;测试Agent就是做测试设计、跑验证、报缺陷。角色切换的问题,用多个Agent实例的协作来解决,而不是靠一个Agent的"人格分裂"来解决。

每个Agent在启动时都绑定一份角色说明书,内容包括角色目标、允许使用的工具清单、输出格式模板、禁止行为清单。这里要特别提一下禁止行为清单,它和正向指令同样重要。比如开发Agent被禁止直接修改需求文档,测试Agent被禁止跳过测试用例直接给结论,这类约束能够显著减少越权行为。

2.3 记忆层:共享只看该看的

多智能体系统里最棘手的问题之一,就是Agent之间怎么共享信息。全共享会引发上下文污染,零共享又会导致信息孤岛。Agency Agents采用的方案是"共享黑板"加"定向广播"的混合模式。

共享黑板本质上是一个持久化的键值存储,记录整个项目运行期的关键信息,比如项目背景、技术约束、需求变更记录、已完成的里程碑。每个Agent在执行任务前,编排中心会把黑板上与当前任务相关的切片注入到该Agent的上下文中,而不是把整块黑板都丢给它。这样既保证了信息一致性,又控制了上下文长度。

定向广播则用于即时协作。比如开发Agent提交了一个代码变更,这个消息只广播给测试Agent和评审Agent;产品Agent收到需求变更时,只会广播给正在执行相关子任务的开发Agent。定向广播的核心是维护一份"订阅关系表",编排层根据这张表决定消息的投递范围。这个设计避免了我后面会提到的"共享上下文被污染"的经典事故。

3. 三个核心角色的设计:产品、开发与测试的职责边界与协作协议

Agency Agents的第一版只做了三个角色:产品Agent、开发Agent、测试Agent。不是不想加更多角色,而是三个角色已经能覆盖"需求→实现→验证"的完整闭环,角色多了协作链路过长,第一版反而不容易收敛。等跑稳了,再扩展架构设计Agent、运维Agent这些角色才合理。

3.1 产品Agent:需求解析与任务拆分的输出格式

产品Agent输入的是一段原始需求,输出的是一套结构化任务卡片。很多开发者在做这一步时习惯让Agent直接输出一个大需求文档,但Agency Agents的要求更严格:每个任务卡片必须包含一个唯一编号、用户故事描述、功能验收标准、相关依赖记录和风险标记。

之所以要求唯一编号,是因为后续所有的协作消息都要引用这个编号。开发Agent说"修复了需求并发问题"是无效信息,必须说"完成了TASK-1024的并发处理"才是有效交付。这种编号体系让整个协作链路可审计、可追溯。我在设计时参考了软件开发里Issue跟踪系统的习惯,把Agent协作的消息都变成带引用关系的对象,而不是自由文本。

验收标准写得好不好,直接决定测试Agent能不能干活。我们的约定是:验收标准必须可量化、可验证,禁止出现"系统应稳定"这类模糊表述,必须写成"在100并发条件下,接口P95响应时间低于800毫秒"。第一版系统里产品Agent常常写出模糊标准,测试Agent就拿着这些标准硬编测试用例,结果开发交付后发现标准根本无法验证,整个链路卡死在评审环节。后来我们在产品Agent里加了"标准自检"步骤,生成任务卡片前先问自己:这条验收标准能写成自动化用例吗?不能就继续改写。

3.2 开发Agent:代码生成、审查与自检循环

开发Agent是最难设计的角色,因为它面临的是"写代码容易,写对代码难"。Agency Agents给开发Agent设了三个必选项:编码前的依赖确认、编码中的自检清单、编码后的变更说明。

编码前的依赖确认,是为了避免"不知道系统里已有什么就动手造轮子"。开发Agent在执行任务前,必须先查询共享黑板里的"技术约束"区块,确认现有模块、数据模型和接口约束,并在任务评论中留下依赖评估记录。这个强制步骤大大减少了重复实现和接口不匹配的问题。

自检清单则是一份硬性的输出前检查表,包括:代码是否符合项目目录规范、核心逻辑是否有单元测试覆盖、是否存在明显的安全隐患(比如SQL拼接)、异常分支是否有处理。开发Agent写完代码后必须逐项跑一遍自检,全部通过才能提交消息。实际运行中,自检清单不可能完全阻止所有bug,但它能把低级的、一眼就能看出的问题拦截在提交之前。

变更说明是给下游的测试Agent和评审Agent看的。开发Agent每次提交都附带一段结构化的说明,包含变更文件清单、核心实现思路、已知风险和未完成事项。这个说明让测试Agent知道该重点测什么,也让评审Agent能快速建立对代码的"心理模型"。没有这步时,测试Agent只能靠猜来设计用例,效果差很多。

3.3 测试Agent:用例生成、回归执行与缺陷上报

测试Agent的执行逻辑是一条独立的链路:读取任务卡片的验收标准、生成测试用例、执行验证、输出测试报告。它和人类测试工程师的工作方式几乎一致,只是自动化程度更高。

在设计上有个细节值得单独说:测试Agent只对"可执行验证"负责,不对"需求合理性"负责。如果验收标准本身有歧义,测试Agent不允许自己脑补合理口径然后继续测试,它必须把问题退回给产品Agent,由产品Agent确认后再继续。这个"测试不替需求背锅"的职责隔离,避免了三个Agent在语义模糊地带互相套娃。

缺陷上报采用的是带上严重级别的结构化格式。测试Agent发现缺陷后,先判断缺陷级别:阻塞级、严重、一般、建议。阻塞级和严重级缺陷会触发编排中心的"中断机制",可能会退回当前开发任务或启动一个高优先级修复任务;一般级和建议级则进入待处理队列。分级上报的意义在于,不要让一个低优先级的小问题打断正在进行的核心开发链路,键系统才能真正连续运转。

3.4 状态机与消息协议:协作如何收敛

三个角色之间的协作依赖一套明确的消息协议。我把消息分成了五大类:任务派发、交付提交、评审意见、缺陷上报、完成确认。每类消息都有固定的结构字段和流转规则。这套协议实际上把一次协作变成了一个可驱动的状态机:

  • 任务派发后,开发Agent的状态是执行中,不允许做与当前任务无关的动作。
  • 开发Agent提交交付后,状态自动流转到待评审,测试Agent按验收标准执行验证。
  • 测试Agent验证通过,则流转到完成确认;不通过则带着缺陷消息回流到开发Agent,状态变为驳回。
  • 产品Agent可以在任一环节提出需求变更,变更一旦确认,会生成一个新版本任务卡片,而不是对旧任务原地打补丁。

这里最值得强调的是"变更生成新任务"这个规则。很多多智能体系统跑崩,就是因为需求中途变时直接改动旧任务的输入输出,导致Agent上下文中新旧需求混在一起,产出一个四不像的东西。把变更独立成新任务,虽然会造成一定的任务冗余,但保证了每次执行时的上下文是干净、一致的。这个取舍非常重要,后面讲坑的时候还会展开。

4. 从零搭建最小可用闭环的实操路径

我实现Agency Agents第一版的时候,没有用特别复杂的重型框架,而是选了一套轻量级的技术栈:Python做Agent运行外壳,内置一个简单的事件总线用于消息流转,Agent的推理能力接通用大模型接口,状态存储先用了内存加JSON持久化。这样做的原因很简单:第一版的目标是验证协作协议本身是否合理,而不是追求系统性能。

4.1 最小环境的选型与准备工作

选型这件事,我建议第一版不要引入过重的依赖。我见过一些团队一上来就铺Kubernetes、上消息队列、做分布式部署,结果系统连最基本的"三个Agent协作不串味"都还没跑通,搭环境的时间反而比调试Agent逻辑还多。

Agency Agents第一版的环境是这样的:纯Python项目,用一个全局字典作为事件队列,Agent之间不直接调用对方的方法,而是统一向事件总线发消息。这个"统一发消息"的约束非常重要,它保证后续把内存队列换成消息队列时,Agent业务代码完全不用改。

模型接入层做了一个小抽象,统一暴露一个接口,支持通过配置文件选择模型。第一版开发调试时我用的温度和最大输出长度都调得比较保守,温度设为0.2,目的是让Agent的输出尽量确定、可复现。等协作链路稳定后,再放宽到0.7左右,让Agent有一定的发散空间。

4.2 第一版协作协议的落地实现

第一版协作协议的代码骨架很简单,核心是一个Agent基类和三个具体角色类。我第一次跑通闭环大概用了三天,写代码的波动率比我想象的低得多,真正花时间的是"调消息格式字段"。

给个我第一版的Agent基类示意:

class BaseAgent: def __init__(self, agent_id, role, hub, model_adapter): self.agent_id = agent_id self.role = role self.hub = hub self.model_adapter = model_adapter self.subscribed_topics = [] async def receive(self, message): message = self.preprocess(message) response = await self.handle(message) self.postprocess(response) return response async def handle(self, message): raise NotImplementedError def publish(self, topic, payload): message = {"topic": topic, "sender": self.agent_id, "payload": payload, "timestamp": time.time()} self.hub.broadcast(message)

产品Agent的handle方法会把原始需求变成任务卡片,然后发布到"task.assigned"主题;开发Agent订阅这个主题,拿到任务卡片后开始产代码,发布到"delivery.submitted";测试Agent订阅"delivery.submitted",执行验证后发布到"test.reported";评审Agent收到测试报告后决定"review.passed"或"review.rejected"。

这里我要特别说一下事件总线的"订阅关系"设计。第一版我图省事,让所有Agent监听所有主题,结果发现产品Agent会收到开发交付的消息、测试Agent会收到任务派发的消息,消息海里全是无关信息,Agent反而不知道当前应该干嘛。后来我把订阅关系表硬编码到每个Agent的初始化配置里,各收各的消息,协作效率立刻提升了一个档次。

4.3 跑通Demo之后必须补的"三板斧"

Demo能跑通和系统能落地是两码事。Agency Agents在内部试运行阶段,靠"超时控制、失败重试、人工闸门"这三板斧才真正稳下来。

超时控制是针对模型调用不稳定设计的。大模型接口有时候会响应很慢甚至卡死,如果没有超时控制,一个Agent卡住,整条协作链路都停在那里。我在Agent基类里加了一个超时保护:单次模型调用超过90秒就中断,并把该消息转入重试队列。重试队列有最大重试次数,超过3次后进入人工处理队列。

失败重试不是简单的原样重发。我设计了一个"退避-修正-重发"的机制:第一次失败可能是接口波动,等5秒重试即可;第二次失败,说明Agent的产出可能有问题,会让Agent读取之前的消息上下文,自行修正后重发;第三次还是失败,就彻底停下,把消息转给人来处理。这套机制避免了"无限重试烧钱"和"一次失败就崩盘"两个极端。

人工闸门是我始终坚持的设计。Agency Agents在关键节点设置人工确认步骤,比如对外输出的最终文档、涉及修改配置文件的重大变更、以及可能影响其他人的大面积重构。不是信不过Agent,而是这些操作一旦错误,修复成本远高于人工确认的成本。人工闸门的价值不是"兜底",而是给系统一个"止损点"。

5. 落地一个月后踩到的坑:并发调度、上下文污染与收敛控制

这部分的经验不是来自Demo阶段,而是来自Agency Agents真实跑了近一个月的内部项目之后。系统在"看似正常"的运转中,陆续暴露出几个只在真实负载下才会露出来的问题。每一个的排查过程都挺折腾,我把完整的链路写出来,希望你看了能少走弯路。

5.1 排查实录:任务乱序导致测试Agent测评偏差

那是系统上线第三周碰到的。现象是测试Agent报告的两个测试结果完全矛盾:对同一个模块,前一个小时的测试报告说"全部通过",后一个小时说"存在严重缺陷"。一开始我以为是模型随机性的问题,给测试Agent调低了温度后重新跑,问题依旧。

后来我仔细翻了事件总线的消息记录,才发现问题出在"任务乱序"上。产品Agent在同一时间派发了两个相关联的任务卡片:TASK-1010是先做底层数据处理模块,TASK-1011是在这个模块之上做展示页面。开发Agent在处理任务时,因为TASK-1011的消息先被处理,逻辑上先于依赖项执行了。等TASK-1010完成时,开发Agent又顺手把TASK-1011的代码改动了一下。测试Agent拿到的代码版本,就出现了"后写完的底层模块"和"前写完的展示页面"之间的版本不一致,测试结果当然矛盾。

定位到这个根因后,修复方案分了两层。第一层是在编排中心加"依赖等待"机制:任务卡片里显式声明依赖关系,编排中心发现一个任务依赖的任务未完成时,强制将该任务留在待分配状态。第二层是在开发Agent的上下文里加了版本快照:每次交付提交时,必须记录当前代码版本的完整指纹,测试Agent执行验证时以这个指纹为准。乱序问题的本质是"协作协议里没有定义顺序约束",这比模型能力问题更根本,必须在架构层面堵住。

5.2 共享上下文被污染:问题定位与隔离方案

第二个坑发生在共享黑板的混合模式上线两周后。现象是:开发Agent开始莫名引用另一个完全不相关任务的历史信息,比如做用户登录模块时,代码注释里出现了"根据报表模块的统计数据来看..."这种莫名其妙的引用。开发Agent的上下文里混入了不该有的东西。

我排查了一下,发现是共享黑板的取值逻辑偷懒了。最初实现时,我图省事,让编排中心把黑板里所有"近期更新"的键值都注入到每个Agent的上下文,理由是"这样消息更新不会被漏掉"。结果"近期更新"的覆盖范围越滚越大,几乎等于全量注入,某个任务的中间数据就污染了另一个任务的上下文。这就是我前面讲"共享只看该看的"原则被破坏的典型表现。

修复方案是把黑板分组加权限。黑板键值分成项目级、任务级、内部级三类。项目级数据(如技术栈、目录规范)对全部Agent可见;任务级数据(如当前任务的验收标准、进展状态)只对参与该任务的Agent可见;内部级的思考过程、中间草稿则默认不写入黑板,只有Agent显式调用发布接口才会共享。加上这个权限控制后,上下文污染的问题基本绝迹。

5.3 开发Agent的"自我修正式失控"

最后一个坑最有意思。某天开发Agent在自检时发现一个代码问题,于是启动自我修正,修完后又发现关联函数也有问题,又修,然后继续深挖,一个上午就在那里反复修改不断扩大范围,最终把某个原本稳定的模块改得面目全非。人类开发者遇到这种"修改癖"顶多被同事拦一下,Agent没有外力拦截,就那么一直改下去了。

我复盘后发现,根因在于开发Agent的工作循环里缺少"终态判断":它只知道自己可以继续改进,不知道"什么时候该停"。Agent的目标函数里几乎没有"停止条件"的概念,每个自检通过后它都会继续生成"下一轮优化建议",形成无限自举。

解法是给Agent加了一个"收敛闸门"。每次循环改进后,Agent必须做一次收益评估:本次改动是否由前置缺陷触发?是否直接影响验收标准?如果不是,强制停止并把潜在改进点记录到待办列表。同时,在协作协议里加了"改动范围声明"——每个开发Agent在一个任务卡片内,最多允许执行N轮自检修正,超过N轮必须暂停并等待评审Agent介入。这个约束看起来粗暴,但非常有效:限制Agent的"内卷",把发散性改进转为受控的待办事项,既保护了系统稳定,又不丢失可能的优化点。

5.4 关于Agent产出的真实性校验:防止"看上去正确"的幻觉

这一节我要特别提醒。Agency Agents跑起来之后,我最大的不安全感来自一个现象:Agent的产出在形式上是完美的,但内容经不起深究。比如产品Agent输出了一份格式规范、条目清晰的需求文档,可里面的验收标准是它自己编的,没有任何数据支撑;再比如测试Agent生成的测试用例,结构漂亮、覆盖点全面,但执行体的API在项目里根本不存在。

这种"形式上正确、内容上无根"的产出,是Agent应用里最危险的失败模式。因为人工评审时看到一份整洁的输出,很容易被"形式规范"带节奏,忽略去核查它的内容依据。针对这个,我做了两个强制要求:第一,Agent的每个产出物必须附带信息源引用,需求卡片里每条验收标准必须标注来源(用户原话、历史缺陷记录、技术约束文档等),没有来源的信息要明确标注为"待确认假设";第二,测试Agent生成的用例必须经过"可执行性预检",即在生成用例的同时声明依赖的接口和数据结构,由预检脚本验证这些依赖是否存在,存在才放行进入执行阶段。

这个机制不能完全杜绝幻觉出错,但它把"隐性错误"变成了"显性待确认项",至少人知道要去查、要去确认,而不是想当然地接收Agent的产出。我建议任何准备把多智能体系统推上真实项目的团队,都把"信息溯源"和"依赖预检"作为基础规范,而不是锦上添花的优化项。

整套Agency Agents跑到现在,我的最大体会是:多智能体协作系统的复杂度不在单个Agent的聪明程度,而在于角色边界、消息协议、共享机制和收敛控制这些架构层面的细节。这些细节决定了系统是像真实团队一样稳定交付,还是像一群各说各话的聊天机器人一样热闹却无序。如果你也在做类似的系统,建议先不要急着让Agent变得更聪明,而是先把协作协议变得足够清晰,让每一步都有据可查、有界可守。这个基础牢了,后面换更强的模型、加更多的角色,都会顺理成章。

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进