多Agent系统设计实战:从角色拆分到编排器落地 多Agent设计这件事最近几乎是每个做LLM应用的开发团队都在聊。我把一个内部的内容生成系统从单Agent迁移到多Agent结构之后最直观的感受是复杂任务的成功率确实上来了线上返工工单明显变少但架构复杂度也跟着上来了。很多人以为多Agent就是多写几个Prompt然后串起来真正落地的时候才会发现角色怎么拆、消息怎么传、状态放哪里、失败怎么恢复每一步都藏着坑。如果你正打算给项目引入多Agent或者已经试过但效果不如预期这篇笔记应该能帮你少走弯路。内容覆盖设计思路、拓扑选型、通信约定、编排器实现、问题排查和指数体系建设适合已经跑通单Agent、想进一步做协同能力的开发者阅读。1. 先别急着写代码第一件事是拆问题1.1 什么时候才真正需要多Agent我见过不少团队任务还没拆清楚就先上了三个Agent。结果代码写了一堆效果还不如原来一个Agent老老实实跑完。判断“要不要多Agent”可以从任务形态上找信号而不是凭感觉。第一个信号是任务本身超过了一个Agent的上下文和步骤上限。比如让一个Agent写一份完整的市场调研报告它要经历框架规划、资料检索、数据分析、文字撰写、格式排版、合规审查。单Agent硬扛时后面写出来的章节很容易忘掉前面定的结论或者重复引用同一个数据源这种“上下文漂移”随着任务长度增长几乎是必然的。第二个信号是任务需要多个专业视角交叉验证。写技术方案的人往往不会同时做安全评审做翻译的人不擅长做专业术语校对。专业视角分离之后每个Agent只负责一小块反而能把每块做深。第三个信号是任务里有明显的可并行子任务比如同时检索三个不同领域的信息源单Agent串行做会很慢并行Agent能显著缩短端到端耗时。也有明显不需要多Agent的反例。一次RAG检索加回答、一次简单的信息抽取、单轮客服回复这些任务本身步骤少、目标单一硬拆成多Agent只会增加延迟和token开销还可能引入不必要的传递误差。我通常跟团队说一句很直白的话如果一个人从头做到底不会累就不需要招一队人来接力。多Agent是协作工具不是装饰品。1.2 单Agent与多Agent的边界在哪里这里我列一个对比表方便你对照自己的任务形态做判断。这张表是我在一轮技术评审时整理出来的被不少组拿去当决策参考我觉得比纯讲概念要有用得多。维度单Agent多Agent任务复杂度中低线性步骤为主高需要多阶段或多视角协作上下文管理单一上下文易维护多上下文需要裁剪与隔离并行能力弱基本串行强可并行执行子任务失败恢复单点失败重跑成本高可以局部重试但链路更长端到端延迟较低可能偏高取决于编排方式token成本较低明显上升且会有重复消耗调试难度相对容易日志简单困难需要追踪跨Agent流转一个简单的判据是当任务里存在“多重专业视角”且“互相验证”的需求时多Agent的收益会明显大于成本。比如代码提交前的Code Review既有开发者视角也有审查者视角再比如医疗类的科普内容生成既要医学知识准确又要通俗表达还要合规审核。而如果任务只是“从一个较长的文档里抽取关键字段”单Agent加一段写得好的Prompt就够了。1.3 设计前的三个必答问题动手画架构之前先回答三个问题。这三个问题的答案直接决定拓扑和分工方式。第一个问题任务能不能被拆成独立可验证的子任务每个子任务需要有明确的“完成定义”。比如“检索相关资料并输出摘要”比“理解用户需求”要可验证得多。如果拆出来的子任务不可验证后面哪怕装一个独立的审查Agent它也不知道该拿什么标准去审。第二个问题子任务之间的依赖是串行还是并行以内容制作为例规划Agent必须先于写作Agent执行这是硬依赖而资料检索和风格分析之间没有强依赖可以并行。把依赖关系画成一张有向图图里入度为0的节点越多说明并行空间越大就越值得做多Agent。第三个问题最终质量由谁负责很多系统跑着跑着就失控核心原因是没有一个“守门员”。我习惯最后一个Agent一定是审查角色它对前面所有输出做最终判定并明确返回“通过”或“打回”。如果最后一个Agent是执行角色那整个链路就缺少质量闸门问题会直接漏到用户侧。2. 多Agent的骨架拓扑、角色与协作2.1 三种常见拓扑结构及其适用场景拓扑结构本质上就是Agent之间的连接方式。我在项目里常用的有三种串行Pipeline、层级协调、环形自组织。串行Pipeline最简单前一个Agent的输出作为后一个Agent的输入就像工厂流水线。它适合流程固定、顺序明确的任务比如“规划→执行→审查”。优点是链路清晰、容易追踪缺点是前一步出错会一路传导到后面而且没有并行能力。层级协调是目前落地最多的方案一个协调者Agent负责拆任务、分发子任务、回收结果。它像是一个项目经理带着一队专员干活控制力强方便做人工介入点。环形自组织最灵活Agent之间按消息总线互相通信类似开放讨论但它也是失控重灾区因为没有任何一个中心节点能兜底。我建议大多数业务系统优先考虑层级协调局部流程用串行Pipeline环形结构只用在探索性实验里别直接上生产。这里给一张拓扑对比表方便你决策时对着选拓扑类型控制力并行度调试难度适用场景串行Pipeline中低中固定流程、线性依赖层级协调高高中复杂任务、需要人工介入环形自组织低高高探索性实验、复杂讨论实际系统里纯用某一种拓扑的不多更多是混着用。比如我在做某内容生成Demo时整体是层级协调协调者下挂规划Agent和写作Agent但“规划→写作→审查”这一段又是串行Pipeline。混用不是问题只要你能说清楚每个Agent的上下游是谁就行。2.2 角色拆分的粒度如何把握角色拆分的粒度是个平衡问题。角色太少Agent会退化回单Agent模式一个Prompt里要塞很多职责效果跟单Agent没区别角色太多每个Agent都只干一小点活通信成本反而超过了协作收益。我习惯按“职责边界领域术语”来拆而不是按语气或人设来拆。角色名称要让Agent自己都清楚该干什么。比如“规划Agent”比“聪明助手”要好得多因为“聪明助手”没有行为边界。设计角色时我建议给每个Agent做一张角色卡包括五个字段角色名、核心职责、输入来源、输出形式、禁止事项。下面是我在某内容生成Demo里实际使用的角色卡片段字段规划Agent写作Agent审查Agent核心职责拆解任务、生成大纲根据大纲撰写完整内容检查事实、逻辑与合规输入来源用户原始需求规划Agent输出的大纲写作Agent输出的草稿输出形式JSON大纲Markdown正文审查意见JSON禁止事项不允许直接写正文不允许改变大纲结构不允许直接改正文这张角色卡有两点很关键。一是“禁止事项”必须写清楚比如审查Agent只能出意见不能直接改正文避免它越权把前面的成果改坏。二是每个角色的输出形式要固定最好都约定成结构化数据后面编排器处理起来会省很多事。2.3 协作模式与任务编排角色定好之后要解决“怎么一起干活”的问题。协作模式说到底就是三种顺序执行、并行执行、条件路由。顺序执行最简单A做完做B适合依赖链明显的任务。并行执行适合互不依赖的子任务可以显著缩短耗时但要注意并行Agent之间的输出合并问题不是简单把结果拼起来就行通常需要一个汇总Agent或协调者来做归并。条件路由是让编排器根据前一步的产出决定下一步走哪个分支比如审查不通过就走打回分支通过就走发布分支。我建议不要把这些编排逻辑全部塞进某个Agent的Prompt里而是写在编排器代码或配置中。编排器本质上是一个轻量级状态机维护“当前步骤、下一跳、重试次数”这些状态。用JSON配置比改代码要灵活得多而且业务人员也能看懂流程。下面是一个简单的配置示例我用它描述某个内容生成流程{ pipeline: [ {step: plan, agent: planner, next: write}, {step: write, agent: writer, next: review}, {step: review, agent: reviewer, next: publish, on_reject: {target: write, max_retries: 3}} ] }这个配置表达的流程是先规划再写作最后审查如果审查打回就回到写作步骤重写最多重试3次。把策略放在配置里编排器代码就不用跟着业务不断改。3. 通信与状态多Agent的隐形成本3.1 通信协议与消息结构设计多Agent系统里Agent之间的通信成本往往被严重低估。很多项目一开始只把上一个Agent的输出直接拼到下一个Agent的Prompt里跑通是能跑通但一旦出问题你根本没法定位是哪一步丢的信息。我强烈建议给每个Agent间的交互定义一个统一消息结构至少包含这些字段消息ID、链路追踪ID、发送者、接收者、任务ID、消息体、上下文引用、时间戳。链路追踪ID是最关键的它会把一次完整任务的流转串起来后续做日志排查全靠它。没有这个ID多Agent的日志就像一堆没有页码的信纸翻起来十分痛苦。下面是用Python dataclass定义的一个消息体示例实际项目中字段可以再加但这几个是底线from dataclasses import dataclass, field from typing import Any dataclass class AgentMessage: trace_id: str # 一次任务的全局追踪ID message_id: str # 本条消息唯一ID sender: str # 发送Agent名称 receiver: str # 接收Agent名称 task_id: str # 子任务ID payload: Any # 实际业务载荷 context_ref: str # 上下文引用比如KV存储中的Key timestamp: float field(default_factorytime.time)这里有个细节payload里尽量放结构化数据而不是大段自然语言文本。比如规划Agent的输出最好是一个JSON大纲而不是一段“我建议这样规划……”的文字。结构化载荷能减少后续Agent解析的难度也方便做校验。实测下来让Agent以固定JSON格式输出系统稳定性会有明显提升。3.2 状态所有权与上下文管理多Agent和单Agent最大的区别之一是状态管理。单Agent只要管好一个上下文多Agent一多状态就容易乱。我在早期项目里踩过一个大坑所有Agent共享同一份完整上下文结果token消耗翻了好几倍而且一个Agent的中间草稿会被另一个Agent误当成最终结果。后来我定了一个原则状态所有权。每条状态只能有一个生产者也只能被特定的消费方读取。规划Agent只能读用户输入写自己的规划状态写作Agent只能读规划状态写自己的草稿状态审查Agent只能读草稿状态写审查意见。谁也不能直接改别人的状态。这就像公司里的协作文档谁编辑谁负责别人只能提评论不能乱改正文。上下文传递也要克制。并不是把全部历史都传给下一个Agent就最好上下文越长越容易让Agent迷失重点。我一般只传当前步骤需要的最小信息必要时用一段摘要代替完整历史。比如写作Agent不需要知道用户需求是怎么拆解的细节只需要拿到最终大纲本身。3.3 中间产物落盘是调试的救命稻草单Agent的调试还能靠打印日志多Agent一旦跑起来问题往往出现在几个Agent来回传递之间单纯打印不再够用。我的做法是让每个Agent的输入输出都落到本地或集中式存储里按任务ID和步骤名命名比如{trace_id}/{step_name}_input.json和{trace_id}/{step_name}_output.json。这套做法在排查“哪个Agent改了什么东西”的时候特别好使。我曾经排查过一个案例写作Agent产出的内容总比大纲少一节起初以为是写作Agent的Prompt有问题后来翻中间产物才发现问题出在规划Agent的JSON输出里少了一个节点。如果没有中间产物落盘这种跨Agent的问题排查起来会非常痛苦。落盘的存储方案不用太复杂本地文件系统配合任务ID目录就够用。如果多人协作或需要远程分析再用集中式KV存储以trace_id为key存JSON文档。落盘频率也不用每个token都记只在每个Agent执行完成后记一次输入和输出即可。4. 关键实操搭一个最小多Agent系统4.1 用一个可复刻的场景串起全流程理论讲再多不如一个能跑通的最小系统。下面我用一个非常常见、非常适合练手和复刻的场景做例子某跨平台内容生成Demo需求是“给定一个产品主题自动生成一篇结构完整且合规的推广文案”。系统里只有三个Agent规划Agent、写作Agent、审查Agent。这个Demo麻雀虽小五脏俱全它包含多Agent最基本的几个要素角色拆分、链路编排、质量闸门、重试机制。你可以照着搭一个跑跑看比光读文档理解深得多。4.2 Agent基类与角色注册我习惯先写一个轻量Agent基类不引入复杂框架核心方法就是run接收消息返回消息。这样后续替换实现或测试都很方便。下面这个代码片段是工程中最简的版本生产环境里再加日志、超时和重试即可from dataclasses import dataclass dataclass class Agent: name: str system_prompt: str model: str # 模型标识 def run(self, message: AgentMessage) - AgentMessage: # 实际调用LLM的代码由具体的Agent实现类覆盖 raise NotImplementedError每定义一个具体Agent就重写run方法。比如规划Agent的run里会调用一次LLM把用户需求转成JSON大纲写作Agent的run里会拿着大纲调用LLM生成Markdown审查Agent的run里会对Markdown做检查返回通过或打回。在工程中Agent的system_prompt统一放到配置文件里管理不要散落在代码各处。角色卡上写的字段基本都能翻译成system_prompt的约束语句比如禁止事项可以直接写进Prompt的结尾。4.3 编排器的实现思路编排器是整个系统的中枢但它不要承担任何业务理解工作它只负责路由和状态维护。下面这段代码示意了一个最简编排循环def run_pipeline(user_input: str): trace_id create_trace_id() # 第1步规划 plan_msg AgentMessage(trace_idtrace_id, senderuser, receiverplanner, payloaduser_input, task_idplan) plan_output planner.run(plan_msg) # 第2步写作携带规划结果 write_msg AgentMessage(trace_idtrace_id, senderplanner, receiverwriter, payloadplan_output.payload, task_idwrite) write_output writer.run(write_msg) # 第3步审查 重试闸门 retries 0 while retries MAX_RETRIES: review_msg AgentMessage(trace_idtrace_id, senderwriter, receiverreviewer, payloadwrite_output.payload, task_idreview) review_output reviewer.run(review_msg) if review_output.payload[verdict] pass: return review_output # 打回带着审查意见重新写作 write_msg.payload[feedback] review_output.payload[issues] write_output writer.run(write_msg) retries 1 raise RuntimeError(超过最大重试次数)这段代码里最重要的一点是“审查打回”的闭环。被审查Agent打回之后写作Agent不是单拿旧稿重新生成而是携带审查意见重新生成。有了上一轮反馈写作Agent才知道自己错在哪、改什么。不带反馈的循环重试是我见过最多的设计错误会白白消耗大量token。4.4 关键参数选择迭代次数、温度与超时参数调优看着琐碎但直接决定系统能不能用。先说迭代次数我把它当成质量预算。MAX_RETRIES设成3到5次比较合理太少让写作Agent没有纠正机会太多会让系统在坏路径上无限烧钱。关键是每次重试时把上一次的审查意见截断成要点喂回去不要整个历史都塞进去。温度设置按角色区分很有讲究。规划Agent和审查Agent建议用低温比如0到0.2因为这两个角色要求稳定、忠实于事实低温能减少天马行空的风险。写作Agent可以适当高一点用0.6到0.8让它有表达空间但也不要太高超过1.0就会失去控制容易出现重复和跑题。我自己踩过的坑是让审查Agent用高温结果它每次都能找出莫名其妙的“新问题”后来温度调到0问题立刻就消失了。超时设计也不能忽略。每个Agent单次调用LLM要设置超时上限不同模型的耗时差异很大一般30到90秒。超时后建议做一次指数退避重试也就是延时2秒、4秒、8秒依次递增最多两次。这个组合比无脑重试要稳得多线上实测能显著减少上游偶发超时导致的链路失败。下面是参数速查表可根据自己的场景微调参数规划Agent写作Agent审查Agent温度0.0 ~ 0.20.6 ~ 0.80.0 ~ 0.2超时60s60s30s重试策略指数退避最多2次指数退避最多2次指数退避最多2次输出格式JSONMarkdownJSON5. 常见问题与排查技巧实录5.1 高频故障排查表多Agent系统上线后会暴露出一批典型故障。下面这张表是我在多个项目里反复遇到的值得收藏症状可能原因处理建议Agent进入死循环编排器没有设置最大重试次数全局设置迭代上限并强制熔断上下文漂移后文忘前文上下文太长或缺少摘要只传必要结果长历史压缩成摘要审查Agent永远打回审查标准过于严苛或温度过高审查Agent温度设0收敛判定标准写作Agent不按大纲来大纲约束力弱或feedback没有覆盖差异把大纲结构化成约束条件写入Prompt工具调用出错被复读Agent把工具错误当普通文本处理增加工具错误捕获分支让Agent输出错误标记状态相互覆盖多个Agent共享同一份可写状态引入状态所有权每个Agent只写自己的状态其中“审查Agent永远打回”值得展开说。它往往是审查Agent的判定标准没有量化比如“内容要生动”这种标准没法验证Agent就只能主观打分很容易找茬。我后来会在审查Agent的Prompt里给可勾选的条款比如“是否包含明确数据来源、是否有三个分论点、是否有合规违禁词”每一条都能验证打回率立刻下降。5.2 调试多Agent的几种实用手法调试多Agent系统不靠肉眼靠链条。第一是让trace_id贯穿整条链路这是前提中的前提。第二是善用中间产物落盘出了问题先看哪个步骤的输入输出对不上。第三是给每个Agent做隔离测试单独给定输入验证它自身的输出是否符合预期排除了单点问题之后再去查链路问题。还有个很实用的做法是“跟跑模式”。把线上某次完整任务的输入输出录下来离线用同样的数据重新跑一遍同时打印每一步的耗时和token数。这样既不影响线上又能反复复现问题。我在某次排查耗时突增的问题时就是靠录制回放发现是某个Agent把大段无用历史带进了重试白白多花了两轮token。一个小技巧是让每个Agent在输出结构化结果的同时附带一个reasoning字段记录它的判断依据。这个字段只用于调试不影响主流程。排查时看这个字段就能知道Agent为什么做出这个决策比瞎猜快很多。5.3 避坑清单最后整理一份亲测有效的避坑清单都是我交过学费换来的经验。不要把无限重试放在业务主链路上。系统要有熔断机制达到上限后明确报错宁可让任务失败也不要让它空转烧钱。不要让所有Agent共享同一个上下文。上下文越共享状态越混乱。隔离上下文是设计原则不是优化项。不要试图靠“角色扮演”覆盖工具能力。比如让审查Agent“假装很懂合规”来检查敏感词效果不如给它一个合规词表去比对。Agent擅长的是推理和表达不是没有工具支撑的领域知识判断。要最小化Agent的工具权限。每个Agent能访问哪些数据、调用哪些工具要按角色卡严格控制。比如写作Agent不需要也不应该能调用审查专用工具。不要把业务流程写成一大段Prompt。流程变动的频率远高于内容模板应该把流程放在编排器配置里Prompt只管角色能力和输出格式。6. 从能用走向好用评估、迭代与影响6.1 评估指标怎么定才不发虚多Agent系统上线后怎么判断它真的好用我的建议是不要只看一两个指标要搭一套分级指标。核心指标是端到端任务成功率也就是一次任务从入口到出口无人工干预完成的占比。辅助指标包括人工介入率、平均token成本、端到端延迟、返工率。这些指标配合着看才能发现系统真正的问题。比如成功率很高但token成本翻了三倍那可能是审查打回太多次导致的隐性浪费人工介入率低但用户投诉多说明审查Agent的判定口径和用户预期不一致。我通常用一张表格做监控面板指标计算方法参考基线端到端成功率通过任务数 / 总任务数依业务定人工介入率需要人工修改的任务数 / 总任务数越低越好token成本/任务总token数 / 任务数设预算上限端到端延迟平均完成时长视SLA定返工率审查打回次数 / 总任务数建议10%以内6.2 迭代方向人工在环、持久记忆与专用Agent系统稳定之后迭代方向上我建议优先做三件事。人工在环是第一个高价值方向。在审查Agent和最终发布之间加一个可选的人工审批节点让系统在低置信度时自动转人工。实现在编排器里加一个判断分支就够了但对信任建立非常关键团队敢把自动化系统的输出直接对外往往是从这个节点开始的。持久记忆是第二个方向。当前Agent每次执行都像失忆患者业务数据使用多了之后可以让系统把高频知识沉淀到外部存储。比如某内容生成Demo在一段时间后可以把审核通过的历史文章摘要存下来供写作Agent参考风格。专用Agent是第三个方向。当某个环节反复成为瓶颈时把它拆得更细。例如内容生成中审查Agent如果总在事实性错误上打回内容就拆一个专门的“事实核查Agent”出来在这一步先用工具比对数据源再交给审查Agent做整体判定。6.3 这套设计适用的边界与影响最后说点实话。多Agent不是银弹它带来的是一整套工程成本的置换。它适合有明确SLA、跨专业领域、并行子任务多的业务场景能带来质量与效率的提升但也会显著推高token花费、延迟和系统复杂度。团队引入多Agent之后通常会倒逼着建设可观测性、测试集和评估体系这些不是可有可无的而是运行多Agent的必需品。我个人在多次落地多Agent之后体会最深的一点是先有稳定的单Agent再有多Agent。如果单个Agent连最基本的任务都做不稳定拆成多Agent只会把问题放大。设计多Agent的核心其实是设计边界和流程而不是写更多的Prompt。先把角色拆清楚、把状态管住、把回流闭环接好系统自然能用起来。如果你还没动手建议拿本文第4章的最小系统先跑一遍你会比读十篇文章理解的都多。