
做多智能体协作系统绕不开的话题就是“agent-agents”这个名字。我当初第一次看到这个词第一反应是“代理的代理”以为是某种多层中转服务直到真正动手把一个多Agent项目命名为 agency-agents 之后才意识到这个名字本身就精准描述了这类系统的本质一组各司其职的数字员工以类似代理机构agency的协作方式被统一编排起来共同完成一个复杂任务。如果你也在折腾AI Agent开发大概率已经遇到过单Agent聊着聊着就忘了前文、任务一复杂就开始胡乱调用工具、明明什么都想做却什么都做不精细的问题。与其在一个巨型Agent里不断增加Prompt和工具数量不如换一种思路把不同能力拆成多个专职Agent用一套调度机制让它们像一个小型团队一样协同工作。这就是“agency-agents”这类多Agent协作系统的价值所在。无论你是AI应用开发者、技术团队负责人还是只是对Agent开发感兴趣这篇文章梳理的架构思路、编排逻辑和那些只有实际跑了才知道的坑都值得过一遍。1. 项目定位agency-agents 到底解决什么问题1.1 先说清楚“多Agent”和“单Agent”的区别很多人在刚开始接触Agent开发时都会陷入一个思维定式只要把所有指令、工具、背景知识塞进同一个系统Prompt就能得到一个“超级Agent”。这个思路在小规模Demo里确实管用但到了真实业务里单Agent的瓶颈会非常明显。第一是上下文窗口的占用。你在Prompt里既定义行业知识库又规定工具调用格式还要塞进几十条历史对话Token消耗很快。第二是工具选择的冲突。一个Agent同时挂十几个工具模型在决策时就容易犯迷糊经常调用错工具或者在高风险操作上表现得很“大胆”。第三是角色不一致。要求同一个模型既严谨审核又自由发散既精通代码又擅长文案它会经常在两种状态间横跳输出不稳定。“agency-agents”的核心思路正好对着这个问题来打不再试图造一个全能单体而是把能力拆解成多个专职Agent——有的负责规划拆解有的负责具体执行有的负责审核质量有的负责资料检索。每个Agent只专注于一件事Prompt短了工具少了模型决策自然更准整体输出的可控性也大幅提升。1.2 它和“智能体框架”是什么关系现在市面上已经有AutoGen、MetaGPT、CrewAI这类多Agent框架经常有人问那我自己搞一个“agency-agents”是不是重复造轮子我的看法是框架给的是通用底座但真实业务里的角色定义、协作协议、任务分配策略仍然需要根据项目场景去定制。尤其当你做的是垂直领域产品比如自动化内容生产、研发辅助、客服工单分发拿通用框架硬套往往会在流程控制上碰壁。所以“agency-agents”更像是一个可参考的自研多Agent协作方案核心价值在于它的角色划分思想和编排协议而不在于某个具体库。你可以用LangChain实现也可以裸调大模型API实现甚至可以在某个通用框架基础上改造。关键是先想清楚你的系统里有哪些角色它们之间如何传递信息出了问题由谁来兜底。注意很多教程喜欢把多Agent系统吹成“零代码搭建”实际开发中你会遇到大量需要手工控制的细节比如消息格式、错误重试、任务优先级。想清楚这一点能少踩很多坑。1.3 适合谁来参考如果你属于下面任一情况读这篇文章会比较有收获想做自动化研究工作流让多个模型分别负责收集资料、写摘要、做交叉验证想给研发团队做一个代码辅助系统让不同Agent分别做需求分析、技术方案设计、代码生成、Review或者你已经玩过单Agent开发发现系统越来越臃肿想找一个结构化的升级方案。2. 整体设计与架构思路2.1 为什么“代理机构”式的结构管用“agency-agents”这个名字里的agency不是随便起的。真实代理机构的工作方式是这样的客户提出需求项目经理PM拆解任务分派给不同执行者执行者各自完成自己的部分再由质量人员把关最后统一交付。这个模式放到Agent系统里好处非常直接。第一是职责隔离。每个Agent只维护自己的上下文和工作状态不会被其他角色的历史消息干扰。比如说负责代码实现的Agent不需要关心用户原始的模糊需求长什么样它只需要拿到已经结构化好的、包含验收条件的任务单。这样上下文长度可控模型注意力更集中。第二是质量防线。执行Agent很容易“自信犯错”但如果后面站着一个专门的审核Agent去检查它产出方案的正确性、代码是否符合规范、文案是否有事实错误很多严重问题能在交付前被拦截下来。第三是并发可能性。分工明确之后如果一个任务可以拆成多个互不依赖的子任务多个执行Agent可以并行处理。比如一个品牌推广需求文案、视觉方案、渠道策略这三个子任务之间没有强依赖并行跑能省下大量时间。2.2 系统里最基本的角色配置一个最小可用的“agency-agents”系统我习惯用三类角色起步协调者Coordinator负责接收用户的原始需求拆解成可执行的任务单元执行者Worker根据协调者下发的任务单调用相应工具和模型能力完成具体工作审核者Reviewer检查执行者的产出判定是接受、打回重做还是反馈修改意见。在这个基础上如果业务需要再扩展出研究员Researcher专门做信息检索和资料整理或者安全员GuardAgent检查输出内容有没有风险和不合规信息。这些角色可以共用同一个模型也可以配置不同的模型视成本与效果要求而定。2.3 任务调度流程消息路由是这套系统的心脏。如果设计得太宽松所有Agent随便发言系统会变成一锅粥如果太严格协调者就变成了一个只能顺序执行的串行管道又失去了多Agent的意义。我实践下来相对好用的方式是“阶段化路由”任务按阶段划分每个阶段只有一个主导Agent其他Agent只能通过结构化消息与主导者交互。一个典型流程长这样协调者先输出一份任务分解清单包含子任务、依赖关系、验收标准然后执行者按优先级领取任务完成任务后提交产出物与自检说明审核者收到产出物后对照验收标准做检查返回“通过”或“打回修改建议”如果打回执行者根据建议修改后再次提交直到审核通过最后协调者汇总所有子任务产出生成最终交付结果。这个流程看起来简单但真正落地时需要非常注意消息协议的设计。提示把流程画出来的时候不要只画“成功路径”。多Agent系统里最需要提前设计的是异常路径——某个Agent返回了不可解析的内容怎么办多次打回仍然不通过怎么办这些情况在真实运行中一定会发生。3. 核心细节解析与实操要点3.1 消息协议的设计别让Agent之间“鸡同鸭讲”Agent之间传递的不应该是自然语言的碎片而应该是结构化的消息对象。我一开始犯的错就是让两个Agent直接用字符串对话看起来灵活实际调试起来非常痛苦——你根本不知道某条消息是谁发的、属于哪个任务、有没有被处理过。后来我把消息统一成一个包含固定字段的数据结构发送者、接收者、任务ID、消息类型、内容负载、时间戳。任务ID尤其重要它用来关联同一个任务的所有反馈、修改、重试记录。有了这个ID排查问题时可以很快拉出整个生命周期。为了让不同Agent能各取所需内容负载在必要时还会拆成“执行信息”和“审核信息”两部分执行信息给下游执行者看审核信息给审核者看。一个容易被忽视的字段是版本号。同一个子任务如果被多次打回修改它会产生多个版本的产出物。如果没有版本管理后续审核者和汇总阶段的协调者很可能会用错版本导致改对了又被打回陷入死循环。3.2 各角色Prompt设计的几个关键原则Prompt策略上我的总体原则是“每个Agent的人设越窄越好”。协调者的Prompt重点放在拆解逻辑和验收标准制定上不用关心用什么工具实现执行者的Prompt重点放在工具调用和完成交付上不需要关心最终用户的情感审核者的Prompt重点放在“挑错”上要明确要求它从完整性、正确性、一致性三个维度去审查。这里有个实操技巧审核者Prompt里不给“正面模板”只给“否定清单”。比如如果答案存在事实性错误打回如果代码无法通过语法检查打回如果产出物缺少必要的说明文档打回。否定清单比任务描述更能约束模型行为。我实测下来这种方式比“请认真检查质量”这类模糊指令有效得多。还有一个容易踩的坑系统Prompt里的角色名称要和消息协议里的Agent名称保持一致。听起来很基础但自动生成Prompt或者批量修改角色时经常会出现不一致轻则日志混乱重则消息路由失效Agent根本收不到指派给它的任务。3.3 工具注册与权限控制多Agent系统里每个执行Agent挂载的工具集合不同如果所有人都能调用所有工具轻则资源浪费重则出现严重操作事故。我见过一个例子负责写文案的Agent因为工具列表里包含数据库删除接口在生成示例代码时真的执行了删除操作幸好数据有备份。这种问题的根源不是模型有恶意而是权限边界没有设清楚。所以工具注册模块务必要带权限控制。每个工具定义里除了名称、功能描述、调用参数还得有“允许调用该工具的Agent白名单”。更稳妥的做法是再加上“人工确认开关”对于加解密、数据删除、资金操作这类高风险工具强制设置二次确认。注意工具描述文本对被调用的准确性影响巨大。同一个工具描述写“删除数据库中的记录”和写“按用户ID精确删除指定表的一行数据操作不可恢复”的区别很大。显式标注副作用和适用条件机器调用的准确率会明显更高。4. 从零实现一个最小可用的系统4.1 选型与基础代码结构我用Python实现了一个基于共享“任务黑板”机制的最小版本不依赖任何重量级框架。核心组件只有消息队列内存版、任务管理器负责状态流转、模型调用封装可切换不同服务商、工具注册中心。这套结构的好处是足够轻方便逐行理解多Agent协作的逻辑。如果业务规模大了再考虑引入消息中间件和数据库持久化。以消息对象为例定义如下from dataclasses import dataclass, field from typing import Any from enum import Enum import time import uuid class MessageType(Enum): TASK_CREATED task_created # 协调者拆解出新任务 STATUS_UPDATE status_update # 执行者更新任务状态 SUBMISSION submission # 执行者提交产出 REVIEW_REQUEST review_request # 申请审核 REVIEW_RESULT review_result # 审核结果 FINAL_REPORT final_report # 最终汇总 dataclass class AgentMessage: sender: str receiver: str task_id: str msg_type: MessageType content: Any version: int 1 timestamp: float field(default_factorytime.time) id: str field(default_factorylambda: str(uuid.uuid4()))这个结构包含了之前说的所有关键字段。接收者字段可以是具体的Agent名称也可以是“any”表示广播给所有能处理该类型消息的Agent。但实践中我尽量避免广播因为广播把路由复杂度从中心转移到了每个Agent的理解能力上模型在这种场景下很容易误接收。4.2 协调者的任务拆解逻辑协调者不直接干活它只负责拆解。我给协调者的输入端是用户原始需求文本输出端是一个JSON结构包含任务清单和任务间的依赖关系以及每项子任务的验收标准。在实际开发中从自然语言到JSON结构这一步最容易出错所以我采用了两段式实现先让模型输出自由文本的计划再用一个“格式化器”把自由文本转成结构化JSON。很多Agent框架是让模型直接输出JSON看起来高效但模型偶尔会漏掉字段或者输出非法JSON。两段式虽然多了一次调用但稳定性大幅提升。格式化器可以设计成只重试非法部分而不是让整个规划Agent重新推理一遍。dataclass class SubTask: task_id: str title: str content: str acceptance_criteria: list dependencies: list status: str pending output_data: Any None retries: int 0依赖关系我要特别提一句它是协调者最容易生成的“豆腐渣工程”字段。很多模型倾向于把任务拆成全部串行即每个任务都依赖前一个这在实现上最安全但效率极低。另一种错误是拆出大量并行任务但没有考虑它们之间的事实依赖。我的处理办法是在协调者Prompt里明确要求“仅在两个任务存在实际输入输出依赖时才填写依赖字段”并在任务分发阶段增加一个检查器对依赖关系做自动校验。4.3 执行者的工作循环执行者拿到一个子任务之后自己做一个小循环理解任务单、规划内部步骤、调用工具、验证结果、更新任务状态并提交。关键一步是“自检”。我没有让执行者在提交后再等审核者发现问题而是要求它在提交时自带一份自检说明内容包含完成了哪些步骤、引用了哪些资料或工具、有哪几个已知的局限。这个自检说明的价值一是让审核者能把注意力放在真正重要的风险点上而不是从头到尾重新推理二是如果系统调试时发现大量打回集中在某个环节通过统计自检说明能很快定位问题出在哪种任务类型上这个信息是宏观观测系统健康度的关键数据。自检的Prompt示例不复杂我会要求执行者在回答末尾固定输出“自检完成项、局限项、不确定项”三个小节。不管任务类型是什么这三节都必须填。缺失的字段会被格式化器校验拦下要求补全。4.4 审核者的兜底策略审核者进行到最后一步要把评审判定为一个结构化结论APPROVED通过或者CHANGES_REQUESTED需要修改。如果是后者必须额外输出一个“修改要点列表”并且每条修改要点要引用具体位置而不是泛泛地说“内容深度不足”。这个严格要求对执行者的第二轮修改效果提升非常明显因为模型在修改时如果只知道问题类型不知道具体位置很容易改错地方。在审核这层我还加了一个“争议升级”机制如果同一任务被连续打回超过两次协调者会收到一条特殊消息要求介入处理。协调者在第三次派发时会附带历史修改记录并可以选择调整验收标准或者让另一个执行者重新接手。这种制度在真实企业里很常见搬到Agent系统里也完全适用——它避免了因为某个Agent状态不佳或验收标准不合理导致的无限往返循环。核心代码类似这样class ReviewAgent: def __init__(self, llm): self.llm llm def review(self, subtask: SubTask, submission: dict) - dict: prompt build_review_prompt( task_titlesubtask.title, criteria\n.join(subtask.acceptance_criteria), submissionsubmission[content], self_checksubmission[self_check], deny_listsubtask.deny_list, ) result self.llm.chat(prompt) if result.verdict APPROVED: return {verdict: APPROVED, comments: []} return {verdict: CHANGES_REQUESTED, comments: result.modification_points}4.5 运行一个端到端示例我拿一个比较经典的内容生产任务跑过一次完整流程“生成一篇关于_某跨平台系统_的上手指南包含环境准备、核心概念、代码示例和遇到的问题排查”。协调者拆出了四个子任务环境准备说明、核心概念讲解、代码示例编写、常见问题整理。前三个任务之间是弱依赖最后一个任务依赖前三个的内容事实因此形成了并行与串行混合的拓扑。执行者先并行完成了前三个之后审核者逐个审核。概念讲解在第一次审核时被打回原因是“部分概念定义与官方文档表述不一致”修改要点里写明了具体是哪两个概念。执行者第二次提交后通过最后一个任务才启动。全程耗时大约不到一次串行流程的一半而且最终产出的质量明显比单个Agent直接写出来的要高特别是在事实一致性上。5. 编排策略与任务分解细节5.1 拆任务的颗粒度和边界任务拆得太大每个Agent仍然要处理复杂问题多Agent的优势就体现不出来拆得太小Agent之间频繁通信通信开销和模型调用成本反而会盖过收益。我实践下来的经验是一个子任务最好满足两个条件独立可验收以及执行时长在可接受范围内。独立可验收的意思是一个子任务结束时要有明确的产出物比如一份文档、一段代码、一组数据而不是“完成了一半的调研”。明确产出物是审核者能有效工作的前提。执行时长因任务类型而异但一个子任务如果让模型连续工作反而会开始出现长输出截断就需要考虑是不是拆得还不够细。我经常使用一个自查问题这个子任务如果交给一个人去做需要开多少次会如果需要反复跟别人确认需求说明边界还是不清。Agent任务同理边界清晰的子任务执行者单看任务单就能开工不需要回头找协调者问。5.2 依赖关系的三种类型实际项目里依赖关系不是只有“前一个完成后下一个才能开始”这一种。我梳理下来至少有三类强依赖表示下一个任务必须使用前一个任务的产出作为输入比如“写代码示例”强依赖于“确定演示项目结构”弱依赖表示下一个任务参考前一个任务的产出能提高质量但不参考也能开展比如“常见问题整理”可以弱依赖“环境准备说明”中提到的版本号约束依赖表示为避免冲突需要等前一个任务结束才能启动典型场景是两个Agent同时写同一个文件的多个部分后面的任务必须等前面的写完后才能合并修改。在消息协议里我会给依赖字段增加一个“类型”子字段协调者在生成依赖时不只写“A依赖B”还写明依赖类型。执行调度器再根据依赖类型决定是阻塞等待还是允许并行但需要稍后合并。这一步对吞吐量的影响非常大值得多花一点Prompt设计和校验成本。5.3 上下文管理什么时候共享什么时候隔离上下文策略上我的经验是“隔离为主共享为辅”。每个执行者的工作内存里只有当前任务单、自检说明、相关历史版本不看其他Agent的中间状态。审核者只看到提交的产出物和自检说明不自动获取执行者的内部推理过程。这样做的好处是第一隔离了无关信息造成的注意力分散第二也大幅降低了Token消耗。需要共享的是“事实池”。比如代码项目的接口定义、文档产品的术语表、业务指标的计算口径这些东西在系统启动时由协调者统一写入共享存储各Agent在执行过程中可以主动读取。读这种共享数据我建议用函数调用的形式让Agent自己决定“现在是否需要查术语表”而不是在每次任务里把术语表内容都贴进去。这样既兼顾了准确性又控制了上下文体积。提示如果你发现某些Agent经常在共享数据里检索到过期内容或者同一个事实在不同Agent的理解里有偏差不要只改Prompt。优先检查事实池的更新机制确定谁负责写入、什么时机写入、是否带版本号这三件事不解决改Prompt只能暂时掩盖问题。6. 常见问题与排查技巧实录6.1 Agent陷入无限循环多Agent系统最常见的故障就是“打回-修改-再打回”死循环。我遇到过最夸张的一次一个子任务来回三次内容越改越偏最后协调者介入才发现是审核者的否定清单和验收标准本身有冲突验收标准要求覆盖完整流程但否定清单要求拒绝任何冗长输出两个要求叠加导致模型怎么改都会被拒。排查思路是这样的先看修改评论的相似度如果连续两次打回理由高度重合大概率不是执行者改不动而是审核标准出现了自相矛盾再看是否执行者在每次修改后都大幅重写而不是针对修改点精准修正这会导致新错误不断引入。解决问题的手段包括让审核者在打回时额外判断“是否由于标准冲突导致”或者给协调者设置一个最大重试次数超过后强制重新拆解任务或者更换执行者。6.2 任务执行结果质量不稳定同一套系统有时跑出来效果很好有时很糟糕。这种波动很多时候不是模型“时灵时不灵”而是输入上下文的差异。典型场景是某个任务比较复杂执行者看到的历史消息比它训练时习惯的要长模型会在长上下文里“迷失注意力中心”被无关的历史消息带偏。我的排查顺序先对比成功案例和失败案例中实际送入模型的Token序列找出差异再检查是否有过长的历史消息被一并送入有的话改成“只送任务单最新修改建议”的短上下文策略最后看模型温度参数多Agent协作场景里我倾向于把执行者温度调到相对偏保守的区间特别是代码生成、数据整理这类需要确定性的任务。6.3 Agent之间消息丢失或错乱消息丢失最常见的原因是消息路由层的通配符用得太多。比如你让协调者把消息广播给所有Worker但恰好有两个Worker处理相同类型的消息任务就被执行了两次。这类问题在初期的Demo里不明显任务多了之后会造成严重的资源浪费和产出冲突。我采用的补救措施主要有两个第一个是在消息层增加“消费确认”——每条消息被Agent接收并开始处理后必须回一条STATUS_UPDATE协调者侧维护一个超时重发逻辑第二个是在路由规则里采用“精确优先”策略只有无法匹配精确接收者时才启用模糊匹配。配合任务ID就能在日志系统里完整追踪一条消息经历了哪些Agent、是否被重复消费、最后任务落在哪个状态。6.4 大模型调用成本失控多Agent系统的Token消耗是单Agent的若干倍很多人上线第一个月看到账单会吓一跳。省成本的方法不是不用多Agent而是减少无效调用。我这里有两个比较下功夫的优化方向一是重复内容合并如果多个子任务需要查询同一份参考文档不要每个子任务都让对应Agent去调大模型检索而是把查询结果写入事实池统一读取模型调用次数直接减少二是轻量路由前置在把任务交给大模型之前先用规则或小模型判断任务类型简单任务可以直接走模板化处理不必动用完整链路。注意成本监控一定要按任务ID维度做不要只按Agent维度做。只有把每次呼叫关联到一个具体子任务你才能定位是哪些类型的问题消耗了最多的Token。纯按Agent维度看你只知道哪个Agent最贵但不知道是不是某个异常流程导致的重复调用。6.5 排查工具速查表症状优先排查方向常用手段系统卡住不动消息是否有消费确认是否等待了不存在的接收者查看消息队列积压检查依赖关系是否有环产出内容脱离用户原始需求协调者拆解时是否准确传递了原始需求检查任务清单与原始需求的对应关系必要时让协调者先复述需求再做拆分Agent频繁调用错误工具工具列表是否太长、描述是否有歧义精简工具列表调整描述添加适用条件增加白名单同一问题反复打回验收标准或否定清单是否自相矛盾调出历史打回评论比对标准文本让协调者介入重派Token消耗异常高是否有循环调用、是否有过长上下文按任务ID统计调用链开启缓存压缩上下文7. 后续可以怎么扩展如果这个最小系统跑通了我个人建议下一步按“深度扩展”而不是“广度扩展”来走。深度扩展是指把单个环节做扎实比如加强事实池的版本管理和冲突解决机制让多个Agent对同一事实的认知始终保持一致加入人工审批节点在关键交付物上支持人工介入修改而不是完全依赖自动审核引入可插拔的工具执行沙箱让代码类Agent在隔离环境里真正执行并验证它写的代码。广度的扩展也要做但要克制。每增加一个Agent角色消息协议、路由规则、权限配置和审核链路的复杂度都会同步上升。加角色之前先问自己这个角色的能力边界是否和现有角色真的不重叠如果只是“让它做同一件事但换个Prompt”不如直接给现有Agent加工具。真正值得加的角色是那些拥有独特技能、需要独立上下文或独立安全边界的单元。如果你打算把它对接现有业务系统我的建议是从“辅助决策”而不是“自动执行”开始。先让多Agent系统产出建议方案由人工确认后再执行。经历过几次真实任务的筛选你对系统能力边界会有更准确的判断之后再逐步放开自动执行权限。这样既能控制风险也能让团队更平滑地接受一个多Agent协作系统进入工作流。我个人在这些项目的实操中最大的感受是多Agent系统的价值不在论文里的架构图而在那些边界划分和执行细节里。消息协议里多一个字段审核标准里少一条歧义工具描述里多一句副作用提示最终呈现出来的稳定性差异是数量级的。希望这篇文章里记录的踩坑和思路能让你在搭自己的“agency-agents”时少走几段弯路。