单Agent写得再顺手,一碰到“需要同时处理多来源信息、多个环节校验”的需求,就会暴露两个尴尬:上下文越拖越长,模型注意力开始漂移,经常答非所问;所有任务挤在一个循环里,改一处逻辑就得重跑整个链路。最近我在做一个“某跨平台订单咨询助手”的演示项目时,把架构从单Agent拆成多智能体协作,踩了一路的坑,也算总结出一点能直接抄作业的经验。这篇博文就围绕多智能体(Multi-Agent)开发的全过程展开,从概念认知、架构设计、框架选型,到具体代码实现和线上排障,尽可能把关键决策背后的“为什么”也讲清楚。如果你正打算把单Agent升级成多Agent协作,或者刚接触这个方向不知道从哪下手,这篇内容应该能帮你省下不少试错时间。
1. 多智能体到底是什么——先把概念盘明白
1.1 Agent的本质:不只是“调一次模型接口”
很多人对Agent的理解停留在“让大模型多轮对话、能调用工具”的层面。但实际上,一个工程意义上的Agent应该具备完整的运行循环:感知(接收用户请求、系统状态)、决策(选择下一步动作或生成回复)、执行(调用工具、查询数据库、写文件)、反思(根据执行结果修正自身策略)。
这个循环不是一次性的,而是一个持续迭代的过程。用个直白的类比:负责下单的AI客服不是答完一句话就结束了,它需要确认用户意图,查库存,再看看用户的收货地址支不支持配送,最后才给出结果。每一步都可能需要回头调整。多智能体系统则是把这个循环拆给多个角色并行或串行协作,让每个Agent只需要在自己的专业领域内循环,不再背负不属于自己的职责。
我见过不少团队在落地Agent时,把“能调工具”和“Agent”画等号,结果工具越挂越多,提示词越来越长,最后模型根本不知道该先执行哪个动作。这是典型的把复杂系统塞进单线程思维里的问题。稍微复杂一点的业务场景,角色拆开几乎是必然的选择。
1.2 从单Agent到多智能体:三个关键变化
从单Agent升级为多智能体协作,表面上看只是“多写几个prompt”,实际上架构理念发生了三个根本变化。
第一个变化是上下文隔离。单Agent的所有信息都放在同一个上下文窗口里,用户问题、工具返回、中间推理过程全部混在一起。一旦任务超过几轮,早期的关键信息就可能被后续内容冲刷掉。多智能体架构把不同角色的上下文分开,比如意图识别Agent只需要读用户的第一句query,政策查询Agent只需要看知识库返回的结果,谁都不会被无关信息干扰。
第二个变化是职责边界。单个Agent出现问题,你很难判断是提示词写得不好、工具调用出错,还是模型理解偏差。多智能体架构下,每个Agent只做一件事,出问题时能快速定位到具体环节。这种模块化带来的可测试性,在开发阶段简直救命。
第三个变化是并行能力。单Agent想并行处理多个任务,需要自己写并发逻辑、管理共享状态,很容易出bug。多智能体架构天然支持不同Agent独立运行,比如同一次用户请求里,可以同时让两个Agent分别查库存和查价格,最后再由汇总Agent合并结果。整体响应时间不一定翻倍,甚至可能减半。
1.3 多智能体不是银弹——什么时候不建议用
我在项目里也走过一段盲目追求“智能体数量越多越酷”的弯路。实际经验是,以下几种情况根本没必要上多智能体。
任务链路短且固定时不要用。比如简单的“将用户输入翻译成英文”,单个Agent一步就能完成,拆成“理解Agent”和“翻译Agent”只是白白增加延迟。需要严格保证信息一致性的场景也不建议用。多智能体之间的信息传递依赖消息协议,一旦协议设计不好,状态同步会非常痛苦。如果你是单Agent都快hold不住token长度,想着“拆成多个Agent就好了”——这个方向对,但前提是你要先想清楚拆解之后的上下文隔离策略,否则拆完只会更乱。
判断标准很简单:如果你能用一个五六百字的prompt把任务描述清楚,那就别拆;如果任务里面涉及多个专业角色、多套知识库、多个工具,而且角色之间需要互相校验,才值得考虑多智能体。
2. 架构设计与角色规划:先画“虚拟公司”再写代码
2.1 用“虚拟公司”类比多智能体协作
我刚接触多智能体设计时最大的困惑是:不知道该怎么定义Agent的数量和职责。后来我导师跟我说过一句话,我一直记着——“你先把系统想象成一间公司,想清楚这家公司需要哪些岗位,再决定写几个Agent。”这句话基本解决了我所有的设计困惑。
比如要做一个售后咨询助手,一个正经的售后服务团队需要哪些角色?前台接待(理解客户问题是什么)、后台专家(查询订单、政策、价格)、主管(审核前台和专家的结论是否合理,给出最终应答)。映射到多智能体架构,就是意图识别Agent、业务查询Agent、合规质检Agent、以及最上层的监督调度Agent。
每次设计之前,先画出这家“虚拟公司”的岗位结构图。每个岗位就是一个Agent,岗位说明书就是它的系统提示词(System Prompt),岗位之间的协作流程就是消息流转机制。这个类比的好处是,它逼着你想清楚“谁发起任务”“谁执行任务”“谁验收任务”,而不是一上来就堆代码。
2.2 设计六步法:目标分解、角色定义、通信协议、编排模式、容错、观测
具体落地时,我会按六个步骤走。
第一步是目标分解。把整个业务目标拆成若干可独立验证的子任务,每个子任务能够写清楚输入和输出。例如“处理用户订单改期请求”可以拆成“识别改期意图”“查询新航班余票”“核对改期手续费”“汇总输出方案”四个子任务。
第二步是角色定义。每个Agent的职责要单一且边界清晰,避免两个Agent负责同一件事。角色定义要写清楚三件事:负责什么、不负责什么、什么情况下必须退出并把控制权交还给上级。很多新手Agent会陷入死循环,就是因为没定义清楚“不负责什么”。
第三步是通信协议。Agent之间的消息格式必须统一。我在项目里统一使用JSON格式,包含sender、receiver、message_type、payload几个字段。没有统一协议的多智能体系统,调试成本会成倍上升。
第四步是编排模式。决定多个Agent是串行、并行还是混合。串行适合流水线任务,并行适合相互独立的子任务,混合模式则用于大多数真实场景。
第五步是容错设计。每个Agent都要有超时、重试、降级策略。比如查询Agent超时了,是等待还是直接返回“未知”?降级策略不同,用户体验差别巨大。
第六步是观测设计。多智能体系统是一个分布式系统,没有日志和追踪,出问题就像在黑屋子里抓猫。后面我会专门讲日志和trace怎么设计。
2.3 三种主流协作拓扑:链式、星型、图型
多智能体的协作拓扑,直接影响系统的复杂度上限和容错能力。
链式拓扑是最朴素的模式,Agent A处理完交给Agent B,B处理完交给C,像流水线一样。适合任务阶段非常清晰的场景,但也最容易出现“前面错一步、后面全崩”的问题。只适合链路短、每个环节准确率极高的任务。
星型拓扑是我个人最推荐的起步方案。设计一个中央调度Agent(或者叫Supervisor),所有任务都由它接收、分解、分发给各个子Agent,子Agent把结果回传给调度者。调度者负责决定是继续分发、还是汇总结果、还是结束任务。这种模式最大的好处是控制点在中央,调试时只要盯住调度者的决策日志,就能定位绝大多数问题。
图型拓扑允许Agent之间任意通信、互相调用,灵活性最高,但也最复杂,容易出现循环调用和状态混乱。图型拓扑适合那些有大量动态依赖的研究型任务,不适合跑生产级业务。如果没有一个专门的推理框架帮你管理状态,我不建议DIY图型拓扑,坑太深了。
3. 环境与开发框架选型:把“地基”选好
3.1 我是怎么选开发框架的
多智能体领域现在框架很多,但真正适合生产落地的其实就那么几种形态。我自己的选型经验是,先从三个维度评估:是否支持可视化编排、上下文管理策略是否灵活、可观测性是否完整。
可视化编排直接影响开发效率。有些框架让你用代码定义Agent图谱,看起来自由度很高,但画不出来协作关系,团队沟通全靠口述。项目一复杂,没有可视化关系图,后期维护就是灾难。我更倾向于支持把Agent关系用声明式配置描述出来的框架,这样既能快速起项目,又能看到完整的调用链。
上下文管理策略也很关键。有的框架是全局共享上下文,Agent之间能看到彼此的完整对话历史,功能上很方便,但token消耗很快就烧起来。好的框架应该允许你给每个Agent配置独立的上下文窗口和记忆策略。
可观测性更不用说了。多智能体系统最怕出了错不知道是哪一步的锅。我选择的框架至少要支持输出结构化的运行日志,记录每次Agent调用的输入输出、token消耗、耗时。没有这类基础能力,后面排障会非常痛苦。
3.2 基础依赖与模型选择
框架定好之后,模型的选型通常会决定系统的成本下限和效果上限。
我的经验是不要所有Agent都用同一个最强模型。调度Agent、意图识别Agent这类职责简单、响应速度要求高的角色,可以考虑使用参数量较小的本地模型或者服务商的基础模型档位,成本低、延迟低。真正复杂的角色,比如需要深度推理、综合多方数据才能得出结论的业务专家Agent,才需要用到最强的推理模型。这种“混合模型架构”能省不少钱,实测下来效果不会差太多。
另外要注意,这些框架通常需要依赖一套完整的模型调用接口,可能是某个服务商的SDK,也可能是本地部署的推理服务。在实际开发中,我强烈建议在代码层套一层自己定义的模型接口封装,这样哪天换模型服务商或者换模型版本,只改一个配置文件就够了。别把模型调用直接写在每个Agent的代码里,否则升级一次模型,全量回归测试会做得很酸爽。
3.3 工程细节:日志、超时、重试
这一节说的东西很琐碎,但往往是线上系统崩不崩的分水岭。
日志第一步要结构化。不要打印一堆拼接字符串,全部输出成JSON行,包含:时间戳、Agent名称、消息ID、输入摘要、输出摘要、耗时、token用量。有了这些,至少能做后期统计,比如哪个Agent平均耗时最长、哪个Agent token消耗最多。我在项目里就靠这个发现了质检Agent的调用次数异常——一个简单check任务它偷偷重试了五遍,成本和延迟一下就上去了。
超时和重试策略必须分级。模型API超时一般设置10到30秒,工具调用超时设置5到10秒,重试次数最多三次,而且要带指数退避。更重要的是在Agent级别设置整体超时,比如“政策查询Agent单次执行最多60秒”,超时就返回一个预设的降级文案。没有这层兜底,整个多智能体链路会被一个慢请求拖死。
所有环境变量统一用.ENV文件管理并用默认值兜底。经验是:要么全用占位,要么全用默认值,不要混着来,混着来必然有环境之间配置不一致的问题。
4. 从零实现一个多智能体项目:某跨平台订单咨询助手Demo
4.1 整体需求与角色设定
这一节我用一个实际写过的Demo来展示完整实现过程。某跨平台订单咨询助手,输入是用户一句自然语言,输出是订单相关的准确回复。系统需要支持查订单状态、退改政策、价格明细等能力。要是在单Agent架构下写,这个系统需要把用户意图判断、订单查询、政策解释、回复润色全塞进一个上下文里,很容易崩溃。
我设计了四个角色:
- 调度Agent:接收用户原始输入,解析意图,决定后续任务分配,汇总最终结果。
- 订单Agent:负责调用订单查询工具,返回订单基本状态信息。
- 政策Agent:负责查询退改签政策、费用规则,返回结构化政策文本。
- 质检Agent:对最终回复做合规检查,防止出现“保证能退”“一定免费”这类绝对化表述。
这四个角色的“岗位说明”分别对应四个System Prompt。调度Agent的Prompt写得最简单,只负责“路由”,不负责“生成”。订单Agent和政策Agent的Prompt写满了工具调用的格式约束。质检Agent的Prompt则是一份负面清单加一个“通过/不通过”的结论输出要求。
4.2 代码结构与核心实现
工程结构上,我建议按角色拆分目录,而不是一个文件塞一堆Agent类。整体结构大概是这样的:
order_agent_project/ ├── agents/ │ ├── __init__.py │ ├── base.py │ ├── dispatcher.py │ ├── order.py │ ├── policy.py │ └── quality_check.py ├── core/ │ ├── config.py │ ├── memory.py │ └── parser.py ├── tools/ │ ├── order_api.py │ └── policy_db.py ├── logs/ │ └── agent_trace.log ├── run.py └── .env每个Agent继承一个BaseAgent,里面封装了模型调用、日志埋点、重试逻辑。核心的调用逻辑大概是这样的:
class BaseAgent: def __init__(self, config): self.config = config self.model_api = create_client(config["model"]) self.max_retries = 3 self.timeout = 30 def run(self, payload): # 1. 组装prompt # 2. 调用模型api # 3. 解析输出为结构化json # 4. 记录日志 # 5. 返回结果调度Agent的run方法内部会根据意图分发任务,然后在结果回收之后统一汇总。这里最关键的一点:调度Agent必须要有一套完整的“终止条件”判断,比如当订单Agent和政策Agent的结果都已返回,或者重试次数到上限时,必须停止继续调度,避免Agent之间无限循环。
4.3 关键参数与调优过程
写代码只是开始,参数调优才是真正磨人的环节。我调完后觉得最值得记录的是下面这几个参数。
temperature。调度Agent我设成了0.1,因为它只需要做确定性较高的分类和汇总任务,温度太高会导致意图飘忽,一会儿把查订单当成退改签处理;政策Agent设成了0.3,允许一定多样性但基本稳定;质检Agent设成了0,完全确定性输出,因为质检本质上是规则判断,不能自己发挥。
max_iterations。系统级我默认设5次,防止循环多头。调度Agent如果5次迭代还没有收敛出最终答案,就返回“暂时无法处理,请转人工”。这个数字太小会影响正常多轮任务的完成率,太大又会让成本和延迟飙升。我实测下来,对于这个订单咨询场景,5次已经足够覆盖绝大多数情况。
上下文截断策略。每个子Agent只保留自己当前任务相关的上下文,调度Agent维护一个轻量的“全局摘要”,但不会把完整子Agent对话塞回去。这个策略让整体token消耗下降了近一半。实现上可以在每次调度循环里调用一次摘要函数,只让原文最核心的信息参与下一轮决策。
4.4 一次真实运行的过程记录
拿“请问我的订单A20240315可以改签到下周三吗?”这句话来走一遍完整流程。
第一步,调度Agent接收输入,输出意图分类结果:{"intent": "modify_schedule", "entities": {"order_id": "A20240315", "target": "next_wednesday"}}。第二步,调度Agent并行分发两个子任务:订单Agent查该订单现在的状态,政策Agent查改签规则和费用。第三步,订单Agent返回订单当前状态为“已出票且未使用”,政策Agent返回“距出发时间大于3天可免费改签一次,舱位差价需要补足”。第四步,调度Agent汇总结果,生成一句自然语言回复,交给质检Agent。第五步,质检Agent检查句子中是否存在“保证”“一定”“免费”等高风险词,通过后把答案输出给用户。
整个流程走完大约用了11秒。最开始没有做并行分发时是18秒,后来把两个互不依赖的子任务改成并行调用,才降下来。另一个优化是意图分类的提示词从一开始的几百字压缩到不到两百字,准确率反而更高了,因为措辞更精炼,模型没那么容易被冗余信息干扰。
5. 常见问题与排查技巧实录
5.1 任务崩坏、循环触发
多智能体系统最常见的故障就是Agent陷入循环。明明库存没货,调度Agent还是会一直把任务分发给订单Agent,订单Agent又返回“无货可查”,调度Agent又判断“需要重新查询”。这个循环如果没上限,会把预算烧到让你怀疑人生。
我排查循环问题的第一步是看日志里消息ID的流转。如果同一个消息ID反复出现在同一对Agent之间,说明它们之间形成了环路。第二步是看调度Agent的决策输出,是不是一直在重复同一个分派动作。修复思路有两个:一是给调度Agent的System Prompt加一句“当子Agent返回失败或者无结果时,禁止重复分派相同任务,必须转入人工或给出兜底话术”;二是直接在代码层限制每个Agent的执行次数上限,超过就强杀。两者我建议都做,单纯靠提示词约束不可靠。
5.2 上下文污染、信息丢失
另一个高频问题是上下文污染。多个Agent共用同一个上下文对象时,订单Agent的原始查询数据会混进政策Agent的上下文里,导致政策Agent回答出现莫名其妙的数字。这类bug特别阴,因为不是每次都出错,偶尔才出来一个幻觉值。
我给每个Agent独立了内存空间,子Agent返回给调度Agent的结果只允许结构化摘要,不允许丢原文。同时我在日志里加了“上下文快照检查”,每次分发前记录各个Agent可见的上下文长度和关键摘要,能快速看出是谁把不该看到的内容带进去了。结论很简单:多智能体系统的上下文设计原则是“最小够用”,宁可少传信息,不要多传污染。
5.3 预算与延迟失控
多智能体系统因为调用次数显著增加,成本通常会比单Agent高上几倍。我遇到过最夸张的一次,一个测试请求因为重试策略设置不当,烧掉了相当于正常请求30倍的token。从那之后我强制在系统里加了一个总预算控制:每次会话开始分配一个token预算,Agent每次调用前都检查剩余预算,不够就直接降级成“余额不足,请人工处理”。延迟的控制核心就是并行化。只要子任务之间没有数据依赖,就不要写串行调用。另外给每个Agent配独立超时时间也很重要,任何一个单环节卡住都不要拖垮整条链路。
5.4 多智能体排查工具建议
最后建议给每个Agent的输入输出都加上校验器。比如订单Agent的返回值必须是固定的JSON结构,结构不对就视为该次调用失败。这一步能筛掉非常多模型返回格式漂移的问题。
还有一个习惯是我后来才养成的:每次修改Agent提示词或者调整模型参数,都会保留一份当时的全链路日志,作为回归测试基线。多智能体系统跟普通单体程序不同,改一个提示词可能影响链路下游所有Agent的判定。没有基线日志,你根本不知道改动到底是变好了还是变坏了。
我在实际项目里最大的体会是,多智能体开发最难的其实不是Agent怎么实现,而是怎么设计一个可控、可观察、可降级的协作结构。如果一上来就追求最灵活的自由通信模式,后面一定会在状态同步和异常排查上交学费。我的建议很简单:先从星型拓扑开始,中央调度Agent总控一切,子Agent越“蠢”越好,只做单一职责,等系统跑稳了,再逐步放开权限。最后再分享一个小诀窍:给每个Agent的System Prompt末尾都加一句“只有当信息充分且验证通过时才输出最终答案,否则输出EXECUTING并说明下一步计划”,这个习惯能减少大量半成品回复。