多代理协作架构实战:从单代理瓶颈到AI智能体机构落地
我第一次看到agency-agents这个项目标题时第一反应是这不就是把一个 Agent 折腾成一个机构吗后来我把这个概念来回啃了几遍发现这个标题背后藏的东西还真不简单——它说的不是单个智能体而是一整套多代理协作架构的设计思路。这篇文章想把这个方向上的理解、踩过的坑以及一套能落地的实现思路全摊开来讲清楚。如果你也在搞 AI 智能体、自动化工作流或者想把自己的工具链升级成“一群 Agent 帮你干活”的模式那这篇内容值得你花几分钟读完。agency-agents要解决的核心问题不是“怎么让一个模型更聪明”而是“怎么让一堆相对简单的 Agent 组合起来完成一个单一大模型干不漂亮的复杂任务”。这就跟公司里一个刚毕业的实习生和一支成熟团队的区别一样——单兵再猛遇到跨领域、多步骤、需要反复校验的活儿还是团队分工更稳。1. 为什么需要把一个 Agent 拆成一个“机构”1.1 单代理的瓶颈在哪里单代理是目前大多数人接触大模型的第一站给它一个提示词它给你一个回答。这种模式处理写文案、改代码、翻译文本等一次性任务确实没问题但一旦任务变得复杂问题马上就来了。首先是上下文长度撑不住。一个任务拆成步骤后每一步都要把历史信息、中间结果、约束条件带进对话里。你让一个代理写一份市场调研报告它既要记住用户需求又要记住行业数据还要记住写作风格、格式要求、引用来源对话轮数一多上下文窗口很快就被撑爆。而且长上下文里相互矛盾的信息也容易让模型“精神分裂”。其次是角色冲突。同一个代理既要做需求分析又要写代码还要检查测试结果所有角色混在一个大脑里模型很难真正切换好状态。让它严谨分析时它开始放飞让它写代码时它又频繁停下来“讨论需求”效率低不说输出质量也不稳定。第三是缺乏校验机制。单代理从头干到尾错了就错了没人给它把关。它自己写的代码自己测自己写的文案自己审模型天然有“自我强化”倾向——很难发现自己刚生成的回答里有漏洞因为它的评判标准和生成标准来自同一套参数。而agency-agents这个思路的变化就好像把一个全能员工拆成几个专职岗位有人负责拆解需求有人负责具体执行有人负责检查验收。每个代理只干自己最擅长的事上下文被切分成相对独立的小段各角色之间有清晰的接口和校验关系问题就被大幅度压下去了。1.2 多代理适合做什么样的任务不是所有任务都应该上多代理架构。根据我实际测试下来的经验下面这几类场景收益最大。第一类是流程长、环节多的任务。比如一个研究报告的生成先做选题分析再收集资料再定框架逐章撰写最后统一格式和校对。每一个环节都需要不同的能力侧重而且中间产物需要被下一步消费。这种时候把流程切给不同专长的代理每个代理处理自己的一段结果比单代理从头干到尾稳定得多。第二类是需要交叉验证的任务。比如代码审查、数据验证、风险审核。让一个代理负责生成另一个代理专门负责挑毛病这种“红队蓝队”机制能极大减少单一模型自以为是造成的错误。多个代理从不同角度审视同一个结果相当于引入了一个廉价的“多重背调”。第三类是需要长期状态管理的任务。比如一个持续运行的调研助手要定期跟踪某个行业动态。单代理在长时间运行中很容易遗忘早期的语境设定而多代理架构可以通过一个 supervisor 统一管理全局记忆把每次任务的上下文固化下来避免状态漂移。1.3 什么时候不应该用多代理我也得泼盆冷水。多代理不是银弹以下情况你强行上多代理纯粹是给自己找麻烦。任务简单、只需要一步回答直接调大模型就够了。硬拆成多代理把任务描述从一个 prompt 拆成三段再引入调度开销纯属脱裤子放屁。多代理意味着多次模型调用成本和延迟都会成倍上升。我还见过有人为了一个“帮我写个朋友圈文案”的任务搞了三个代理结果文案没写好三兄弟自己先吵起来了。其次你对每个子任务的质量标准还不清楚的时候也别硬上。多代理需要你提前定义每个代理的输入输出格式、验收标准、失败处理策略。如果你连“什么算做得好”都没想明白那代理之间的交接就会混乱最后出来的东西只会更糟。2. 代理“机构”里都有哪些角色2.1 主管型代理任务分解与调度在一个多代理系统里主管代理通常叫 supervisor 或 orchestrator 是整个体系的大脑。它不负责具体干活只负责理解用户需求、拆解任务、决定派哪个代理去执行、收集结果、判断是否达标、决定下一步动作。主管代理的设计里最关键的一点是任务分解的粒度。你把一个任务拆得太粗子代理面对的还是一个大问题跟单代理没区别拆太细子代理之间的通信和调度成本又高得离谱而且每个子代理拿到的上下文很有限反而做不好有全局视角的工作。我自己的经验是拆到“每个子代理只需要一次核心决策”的粒度比较合适。举个例子做一个营销文案主管先分解产生“目标受众分析”“产品卖点提炼”“文案草稿”“合规性审查”这四个步骤。每一步的产出都是一个明确的具体文档或数据块不是一个需要二次分解的大目标。主管代理还需要维护一份能力清单知道自己手下有哪些代理、各能干什么。这份清单可以是静态配置文件也可以让主管在运行时去发现代理的能力。早期阶段我建议用静态清单简单可靠等规模大了再考虑动态发现避免一上来就陷入复杂系统。2.2 执行型代理独立干活执行型代理是真正动手的人。它们通常配备特定的工具集、提示词模板和约束条件。比如代码生成代理的提示词里会强调用 Python 3 语法、文档字符串规范、遵循 PEP 8数据分析代理则被绑定到只能调用 SQL 查询和图表生成工具。执行代理的核心价值在于专业化。当你把一个代理限定到很窄的职能范围时它的系统提示词可以写得极其详细把边界、偏好、禁忌都讲清楚。模型在这个受限语境下的表现往往会比它在“全能但模糊”的语境下好得多。这也是为什么“用多个专用代理”经常胜过“一个通用大模型人设”。执行代理之间的能力边界要画清楚。如果两个代理都能“写代码”那主管分配任务时就会出现混乱。我通常会让每个代理在能力列表里标注自己的擅长领域和工具集同时在提示词里强调“不属于你职责范围的事直接拒绝并告知主管”避免越权操作。2.3 质检/协作型代理校验与兜底质检代理是我在所有项目里都不舍得省掉的一个角色。这个代理不产出新内容专门对交付物做检查格式是否符合预期、有没有缺漏项、逻辑有没有明显矛盾、内容有没有违反用户约束。质检代理的存在相当于给整个系统加了一条反馈回路。它检查完发现不合格就把它打回给执行代理重做同时附上具体的修改意见。这个循环可以跑一到三轮超过阈值就上报主管由主管决定是换一种处理策略还是降级处理。有一个细节值得注意质检代理的检查标准要尽量显式化。不要只写“请检查这段代码质量”而是写“这段代码需要通过静态检查、无语法错误、核心逻辑与需求文档一致、边界条件有处理”。标准越具体质检结果越稳定系统才不会在主管和工人之间反复拉锯。3. 设计一套可用体系需要考虑的几件事3.1 通信协议与消息模型代理之间怎么说话决定了整个系统的可扩展性和调试难度。很多人在初期不太重视通信协议结果代理越多越乱。我的习惯是给所有代理间的消息定义一个统一的数据结构大致长这样class AgentMessage: def __init__(self, msg_id, sender, receiver, msg_type, content, metadataNone): self.id msg_id # 全局唯一消息ID self.sender sender # 发送者代理名 self.receiver receiver # 接收者代理名或 all self.msg_type msg_type # task_assign, result_return, review_feedback, ... self.content content # 实际传输的数据 self.metadata metadata # 附加信息如超时策略、重试次数、令牌消耗等消息 ID 必须有而且要保持唯一。排查问题的时候你靠它可以追踪一整条任务链路。我刚做第一版的时候把消息 ID 省了结果出了问题完全没法定位是哪个环节丢的后来花了整整半天加回去。3.2 上下文管理与记忆策略多代理系统里最常见的死法之一是上下文越滚越大。每个代理都有自己的记忆再加上主管的全局记忆如果完全不清理几个小时后整个系统的 token 消耗和响应延迟都能飙到不可用。我的做法是给记忆分层短期记忆当前任务的中间状态、长期记忆跨任务的偏好和约束、工作记忆主管全局梳理后的精简摘要。每次子代理完成一个步骤短期记忆里与它无关的内容就会被“归档压缩”成摘要只保留必要的信息传递给下一步。这个压缩操作可以由主管代理在收到结果后立即执行避免历史包袱越堆越重。具体到代码实现上我强烈建议对每条存入上下文的消息设置token_cost字段并在每次上下文更新时做一次总消耗统计。超过预算时自动触发“摘要化”流程——把前面的对话记录用模型压成一段话再把原文从上下文里移除而不是粗暴地截断。3.3 状态同步与任务追踪多代理系统最大的风险是状态不透明你只知道有个任务发出去了但不知道它执行到哪了、卡在谁手里、失败在哪里。做工程的人都明白不可观测的系统是不可维护的。我在实践里做了一个很简单的状态管理器用文件持久化一个任务看板# 简单状态看板示例伪代码 task_status { task_001: { status: in_progress, # pending / in_progress / completed / failed / needs_review current_agent: writer, history: [ {agent: planner, time: 2025-... , result_summary: ...}, {agent: writer, time: 2025-..., result_summary: ...} ], next_agents: [reviewer], retry_count: 0 } }每次代理交接时强制更新这个看板。这样一旦出了问题你可以精确地知道任务在哪一步、哪个代理负责、已经重试几次。别小看这个细节它决定了你调试系统时是花十分钟还是花一整天。4. 从零搭一套最小可用的多代理系统4.1 整体结构与工具选型先说思路这套系统的核心是“一个主管 两个执行代理 一个质检代理”通过一个简单的消息队列进行通信。技术栈不用刻意追求复杂我用 Python 加上一个简单的任务队列和 JSON 文件做持久化就够了。这里用到了几个模块模型调用层统一封装 LLM API 的输入输出、代理定义层主管、执行、质检各自独立、消息队列层负责在代理之间传递消息、状态管理模块看板 日志。代码结构上我自己比较习惯用agents/放代理定义messages/放消息模型和队列逻辑workflow/放任务状态机和调度逻辑方便长时间维护。一个最小系统的流程是用户输入 → 主管解析并拆解任务 → 主管给执行代理发消息 → 执行代理调用 LLM 完成子任务 → 代理把结果返回给主管 → 主管交给质检代理 → 质检通过则汇总输出不通过则退回重做。4.2 核心代码骨架下面这段是一个最小示例重点是看整体结构不是直接复制就能跑的。import json from dataclasses import dataclass, field from typing import List, Optional dataclass class Task: id: str description: str status: str pending # pending / in_progress / completed / failed assigned_agent: Optional[str] None result: Optional[str] None retry_count: int 0 dataclass class AgentMessage: msg_id: str sender: str receiver: str msg_type: str content: dict class BaseAgent: def __init__(self, name: str, model_client): self.name name self.client model_client def call_llm(self, system_prompt: str, user_content: str) - str: # 这里应该接你的模型API底座填入实际请求逻辑 response self.client.chat(system_prompt, user_content) return response class SupervisorAgent(BaseAgent): def plan_task(self, task_description: str) - List[str]: prompt 你是一个任务经理。请把下面的任务拆解成不超过3个子步骤每个步骤输出一个JSON数组。 result self.call_llm(prompt, task_description) sub_tasks json.loads(result) # 解析返回结果得到子任务列表 return sub_tasks class WorkerAgent(BaseAgent): def execute(self, task: Task, context: str) - str: prompt 你是一名专业执行者。请根据任务描述和上下文产出最终结果。 return self.call_llm(prompt, f任务{task.description}\n上下文{context}) class ReviewerAgent(BaseAgent): def review(self, task_description: str, result: str) - dict: prompt 你是质检员。检查结果是否符合任务要求返回JSON{pass: bool, feedback: str} raw self.call_llm(prompt, f任务{task_description}\n结果{result}) return json.loads(raw)这类代码的核心不在语法而在于让它跑起来的业务逻辑主管拿到子任务列表后要按顺序把它们放进队列工人执行完把结果回传质检拿到结果给出 pass 或 feedback。下面这段是一个简单的主循环。def run_workflow(user_request: str): supervisor SupervisorAgent(supervisor, client) worker WorkerAgent(worker, client) reviewer ReviewerAgent(reviewer, client) # 1. 主管拆解任务 sub_task_descs supervisor.plan_task(user_request) final_results [] for i, desc in enumerate(sub_task_descs): task Task(idftask_{i}, descriptiondesc) # 2. 执行代理干活 worker_result worker.execute(task, contextuser_request) # 3. 质检代理把关 review_result reviewer.review(desc, worker_result) # 4. 不通过就重跑一次再不过就标记失败 if not review_result[pass]: worker_result worker.execute(task, contextreview_result[feedback]) review_result reviewer.review(desc, worker_result) task.status completed if review_result[pass] else failed final_results.append({task: desc, result: worker_result, review: review_result}) # 5. 主管汇总 summary supervisor.aggregate(final_results) return summary这段代码看起来简单实际上已经隐含了几个重要机制任务拆解、上下文传递、质检与重试、结果汇总。我建议你把重点放在“消息传递的字段设计”和“重试策略的边界”上这两个地方是今后扩展时最容易出问题的部分。4.3 运行流程与关键调优跑起来之后你会发现真实世界的需求比上面这段复杂得多。第一个要调的是子任务的粒度。主管拆得太粗工人代理干得累拆得太细调度开销大。我的经验是先根据真实任务跑几遍观察每轮调用的 token 消耗和延迟再主观调一次拆解粒度。一般拆到每步产出一个明确文件或明确 JSON 对象粒度就基本对了。第二个要调的是质检的通过阈值。质检代理如果太严格整个系统就会在重试循环里空转太宽松又失去了兜底的意义。我建议第一次运行时把质检的 feedback 打印出来人工看几轮再根据“否定率”去调整质检代理的提示词——把标准写得可操作、可验证而不是抽象的“合理”。第三个要调的是重试上限。一个子任务重试超过三轮基本可以判定要改方案了。继续重试除了消耗 token大概率不会有突破。这时候应该把失败信息反馈给主管让主管重新拆解子任务或者降级处理。5. 实战中的坑与我的排查心得5.1 循环调用刹不住车多代理系统最常见的故障就是代理互相发送消息形成一个死循环。尤其是主管让工人执行工人觉得缺信息又发给主管主管以为新任务又发给工人两兄弟就这么聊到天荒地老token 烧到破产。排查方法其实不复杂必须监控消息队列的长度和每条消息的msg_id。如果队列在短时间内爆发式增长就说明有人在循环往来。我的防护手段有两个一个是给每条消息加一个max_hops字段任何链路深度超过 5 就强制终止并上报二是在代理的提示词里明确说“你只能收到明确任务消息时才能继续处理消息只发给直接对接代理”切断无意义的转模特环。5.2 子代理“跑偏”了目标执行代理跑着跑着忘记了最初的目标这是多代理系统里非常隐蔽的问题。比如写代码的代理收到了一个“重构函数 A”的任务干着干着忘了自己最初是在处理整个项目管理系统的重构只盯着局部优化最后产出的结果单独看不差但合到一起完全不能融进整体方案。我解决这个问题的办法是在给每个子代理的消息里带上一个全局任务摘要字段每次调用 prompt 都把这个摘要附带上。摘要不用长核心是把“用户最终想要什么”和“当前这步在整个链路中的位置”写清楚。这相当于给每个干活的人发了一张全景地图让他们别迷失方向。5.3 上下文越滚越大很多人在做多代理时犯的一个错误是“每一步都把历史记录全传给下一个代理”。结果系统运行几小时后每条消息都带着一个越来越大的历史尾巴最后要么被模型截断要么成本直线上升。我的处理方式是“上下文压缩”。在代理交接时主管把对话历史中的非核心部分用模型压缩成一段不超过 200 字的摘要只保留当前步骤需要的关键信息。这个压缩操作本身会有少量 token 成本但远小于保持全量历史的长期成本。而且摘要化之后后续代理的响应质量通常不降反升——因为噪声被剔掉了。5.4 幻觉被层层放大单代理的幻觉可能只影响一个点但多代理系统里一个子代理的输出会成为另一个代理的输入幻觉会被层层传递、放大。前一步编了个不存在的文献引用后一步基于这个引用生成了更细节的内容最后交付的文档里全是子虚乌有的东西。这个问题的应对思路是数据溯源。每个子代理在输出结果时必须标注哪些部分来自输入原文、哪些部分是自己的推断。质检代理在审核时专门针对“推断内容”做比对发现来源不明的内容直接打回。这个机制不能保证 100% 消灭幻觉但至少能把跑偏的范围圈住。另外再提醒一点如果你引入了外部工具比如搜索引擎、数据库查询一定要把工具返回的原始结果原样保留在上下文里不要只让模型转述。模型转述等于加了滤镜失真风险很高。6. 从单代理到多代理的演进路径6.1 先跑通单代理再拆结构如果你目前还是用单代理处理所有任务不要一上来就搭多代理系统。我的建议是先把现在的单代理流程完整跑起来记录下它表现最差的地方是长流程容易丢信息还是输出缺乏校验还是上下文窗口不够。多代理架构应该针对具体的痛点去设计而不是为了“用多代理”而用。比如你发现单代理写长篇文章时经常前后风格不一致那就先拆“撰稿”和“统一校对”两个角色你发现单代理改代码经常改坏原有功能那就拆“代码实现”和“回归审查”。先小步试错验证了收益后再慢慢加角色。6.2 补充工具与知识库多代理系统与外部工具的结合是另一个大方向。执行代理只有接到工具调用能力才能真正“动手干活”。比如数据分析代理可以接一个 SQL 查询工具文档代理可以接一个 PDF 分割工具信息搜集代理可以接一个搜索工具接口。我建议把工具按代理的职能边界做绑定一个代理最多挂两到三个工具太多会让模型在调用时犹豫不决。工具描述字段要写清楚输入参数、输出格式和典型使用场景这样模型才知道什么时候该调用、什么时候不该调用。另一个经验是给工具调用加一个“最小置信度”逻辑模型只有在你给它的任务描述足够明确时才允许调工具否则先向主管澄清避免乱调。6.3 后续扩展方向扩展方向我觉得有两个比较值得关注一个是把静态代理升级成动态的“可插拔”插件体系让新的代理注册进来后自动被主管发现另一个是把代理间通信从简单的消息传递升级成带语义路由的总线——不只是按名字发给谁而是根据内容语义决定该转发给哪个代理。我自己的阶段规划是第一步先把单代理流程跑熟并建立日志基线第二步做两个代理的最小协作实现 审查第三步在协作稳定的基础上加主管调度第四步才考虑动态扩展和复杂路由。每一步的验证标准都很简单——看看这轮改动有没有让交付质量、系统稳定性或调试效率至少其中一项明显变好。说实话多代理系统做久了你会发现很多坑跟技术关系不大更多是组织方式和沟通协议的问题。这跟现实中的团队管理出奇地像分工清不清晰信息传不传得到位有没有人对最终质量负责。如果你把这几个问题想明白了哪怕代码再简陋系统也能跑得像模像样。