1. 稳定多智能体系统的设计起点
1.1 多智能体系统的不稳定来自哪里
这几年在AI应用架构这个方向上,多智能体系统已经从概念演示走进生产环境,我见过不少团队把Agent画得漂漂亮亮,一上线就被各种稀奇古怪的失败打垮。做过多智能体项目的人都知道,系统不稳定不是靠调提示词能解决的,它是一道实打实的架构题。
从底层逻辑来看,多智能体系统天然带着不确定性。每个Agent的输出都有概率性,两个Agent之间靠文本交接信息,文本本身又会引入理解误差,链路越长,误差叠加越明显。上游Agent漏了一个字段,下游Agent可能直接就着这个错误往下走,最后产出一个看着像模像样、实际完全跑偏的结果。拿生活里的事情类比,多智能体系统就像临时拼起来的项目组,每个人能力不差,但任务理解全靠上一个人的转述,转述多了任务肯定走样。没有明确的交接规范和兜底机制,项目组再能干也白搭。
这篇文章适合正在做AI应用架构、负责多Agent编排,或者准备从单Agent升级到Multi-Agent系统的读者。我在这里不聊概念,聊的都是在真实业务里能落地的东西:怎么定义稳定指标,怎么选编排模式,怎么设计容错防线,出现问题怎么排查。
1.2 先定义稳定的指标,再谈架构
多智能体的“稳定”必须量化。我在项目启动时会给系统定三个硬指标,这三个指标也是后续所有架构决策的考核标准。
- 任务成功率:按业务口径定义,比如“用户投诉被正确分类并给出可执行方案”的占比。
- 端到端延迟:从用户提交请求到拿到最终结果的时间,多Agent链路里最容易失控的就是延迟,所以必须盯P95,而不是平均值。
- 单任务成本:Token消耗、外部API调用费用、人工介入成本,通通算进去。如果设计时不控成本,一个失败重试链就能烧掉几十万Token。
除了指标,还要提前定义终态。任务可以失败,但失败必须是可预期的,而且要有补偿路径。用户能接受“稍后再试”,不能接受“页面显示成功但订单其实没改”。所以架构师第一步不是画Agent拓扑图,而是明确哪些状态是终态、哪些可以重试、哪些操作必须幂等。
1.3 识别适合多智能体的场景,别为架构而架构
不是所有任务都适合拆成多Agent。一个任务如果本身没有明确的子目标,硬拆只会增加通信损耗和失败点。我用两个维度做判断:第一,任务有没有清晰可拆分的子目标;第二,子目标之间是顺序依赖还是可以并行。真正值得做多Agent的场景,通常有一个调度中心,多个专职Agent各自解决相对独立的子问题,最后再由另一个Agent汇总。
如果任务本身是直线型的“输入到输出”,单Agent加一套提示词就够了,硬上多Agent反而会引入新的延迟和错误。识别场景这件事看似简单,实际上决定了后面所有设计。架构师的价值不是把系统堆复杂,而是把复杂度用在真正需要的地方。
2. 编排模式与通信协议——架构师的第一张决策表
2.1 控制流:四种典型编排模式
设计多智能体系统,首先要定的是控制流,也就是谁在什么时候调用谁,后一个步骤到底依赖前一个步骤的什么结果。我常用四种模式解决不同的问题。
| 模式 | 控制特点 | 优点 | 适用场景 |
|---|---|---|---|
| Router模式 | 入口Agent按意图路由到专业Agent | 路径单纯,容易排查 | 任务边界清晰,分类明确 |
| Orchestrator-Worker模式 | 协调者拆任务、发任务、收结果 | 控制集中,适合生产落地 | 多数业务系统的主干方案 |
| Hierarchical模式 | 顶层拆、子层还能再拆 | 适合复杂项目型任务 | 多层任务树,限制层级 |
| Swarm模式 | Agent之间自由协商 | 自主性强 | 实验型场景,不推荐直接上生产 |
我自己的取舍很简单:生产环境默认优先考虑前三种,尤其是Orchestrator-Worker。它把控制权集中到一个节点上,超时、重试、追踪都能在这一层做,出问题时也能顺着链路快速定位。Swarm模式虽然听起来高级,但多个Agent互相协商的路径太多,失败点和成本都不可控,我只在实验环境里玩。
2.2 数据流:用明确的Schema替代自由对话
Agent之间的通信如果设计成“自由聊天”,这是给自己埋雷。两个Agent像真人一样你来我往,聊着聊着就跑偏,或者为了一个字段反复拉扯。架构师要做的是把Agent之间的每次交互都定义成结构化消息,字段固定,类型固定,语义固定。
我常用的消息Schema包含五类字段:任务类型、上下文、要求、结果、置信度。每个Agent的输出必须符合预先定义好的JSON格式,编排层负责校验,不通过就重试或者转人工。这个约束看起来限制了Agent,实际上恰恰是它在帮Agent做减法。模型最擅长的不是吵架,而是根据清晰的输入产出高质量输出,你把协议固定住,模型才能真正把聪明用在任务本身。
2.3 状态管理:让Agent当状态搬运工,而不是状态仓库
多智能体系统里最常见的隐性故障,是把状态放进Agent的上下文窗口。Agent上下文一长,召回质量就会下降,进程一挂,状态全丢。正确做法是把状态放到外部存储里,Agent按需读取,处理完写回,Agent自己只是一个“读状态-加工-写状态”的处理器。
比如一个客服工单的生命周期,我会在数据库里保存状态字段:待处理、处理中、已完成、需人工介入。Agent崩溃了,调度器从数据库里捞到未完成的任务,重新分配一个Agent继续跑。这个思路和做微服务的“无状态化”完全一致,Agent本身也不是什么神秘的东西,它的可管理性取决于把多少状态放在它能控制的范围之外。
3. 稳定性设计的三道防线
3.1 第一道防线:输入输出校验与结果守门员
多智能体系统最怕坏数据往下传,所以第一道防线是校验。我在每个专业Agent前面放一个输入校验器,负责三件事:格式校验,看输出是否符合Schema;业务校验,看结果是否违反业务规则;不确定性校验,比如Agent输出的置信度低于阈值,或者回复里含有“可能”“需要确认”这类模糊词,就直接标记为需人工复核。
这道防线还可以加一个独立“守门员Agent”,专门负责审查前面的Agent输出,自身不参与执行。独立裁判的价值在于避免同一个Agent既干活又验收,少了“自己骗自己”的盲区。但守门员同样可能误判,所以它的指令要写得非常具体,只做判定,不擅自修改结果,否则守门员本身也会变成新的故障源。
3.2 第二道防线:执行保护与重试策略
第二道防线是保护Agent的执行过程。Agent本质上是模型调用和工具调用的组合,以前做微服务用的保护手段,这里一个都不能少。超时是前提,重试则需要区分场景:模型API问询这种幂等操作,可以大胆重试;写数据库、发消息这种操作,必须先确认上次调用是否生效,再做补偿或跳过。
我习惯用指数退避加随机抖动。第一次失败后等待1秒,第二次2秒,第三次4秒,再加0到500毫秒的随机值,避免多个Agent同时重试把外部服务打爆。重试上限默认3次,超过就直接走回退。这个上限不是随便定的,我统计过模型API在连续多次失败后,再重试成功率提升非常有限,反而白白增加成本和延迟。
并发控制也属于执行保护。多个Agent并行调用外部模型接口,很容易触发供应商限流。我会在Agent层前面加一个共享限流器,按账号配置每分钟请求数,按任务类型分配预算。关键链路多留一些,次要任务少分一些,宁可排队也不要直接把服务打挂。
3.3 第三道防线:级联回退与人工接管
无论前面两道防线做得多完备,模型总会在某一天输出一个完全不可用的结果。所以必须有第三道防线:回退链。我给每个关键Agent准备一到两个回退选项。首选标配大模型,失败时切到备用模型,备用模型再失败就调经典规则模块兜底,规则模块搞不定就转人工。
回退的编排原则是每一层都能独立降级,整体链路不能因为一个Agent挂掉就全盘崩溃。比如订单改签任务,智能Agent连续失败后,系统自动降级为记录用户诉求并转人工处理。对用户来说结果可接受,对业务来说损失可控。还有一个关键点:回退链必须用确定性代码预先配置,不能靠模型现场决策。它是系统的最后底线,底线是不能有概率的。
4. 核心环节实现:一个跨境客服工单系统的实战拆解
4.1 场景与Agent角色
我拿一个实际落地的场景举例,跨境电商客服工单系统。用户自然语言投诉“我上周买的耳机连不上手机,想退款”,系统需要识别意图、检索历史订单、判断是否符合售后条件、草拟回复,再由另一个Agent检查政策合规。
这个场景如果单Agent硬做,效果通常不稳定,因为需要同时处理自然语言理解、数据库查询、政策匹配多类能力。拆成多Agent后,每个角色只专注一件事:
- 意图识别Agent:判断用户诉求是物流、售后、退款还是商品咨询;
- 订单查询Agent:根据用户ID检索订单系统,返回订单状态和售后窗口;
- 方案生成Agent:基于订单信息和售后政策生成回复草稿;
- 合规审查Agent:检查回复是否符合平台政策,不合规就退回方案生成Agent修改。
四个Agent由Orchestrator统一调度,前两个可以部分并行,后两个先后依赖,整体呈现一个典型的Orchestrator-Worker结构。
4.2 状态机与编排配置
工单任务我设计成一个有限状态机:RECEIVED转INTENT_CLASSIFIED,再到ORDER_FETCHED,再到PLAN_GENERATED,再到COMPLIANCE_CHECKED,最终COMPLETED。中间任何异常都可以跳转到NEEDS_HUMAN,由人工接管。状态机存在数据库里,Agent每完成一步,调度器根据结果决定下一步走向。
实际的编排层也不是让Agent自由发挥,而是用结构化配置驱动。下面是一段简化后的编排配置,不同框架写法有差异,但核心思路一致。
{ "orchestrator": { "max_retries": 2, "timeout_seconds": 300, "steps": [ {"id": "intent_classifier", "agent": "classifier", "timeout": 30, "retries": 1}, {"id": "order_fetcher", "agent": "order_agent", "timeout": 60, "retries": 2}, {"id": "plan_generator", "agent": "planner", "timeout": 90, "retries": 2, "requires": ["intent_classifier", "order_fetcher"]}, {"id": "compliance_reviewer", "agent": "reviewer", "timeout": 60, "retries": 1, "requires": ["plan_generator"]} ], "fallback": [ {"condition": "compliance_reviewer_failed", "action": "needs_human", "message": "人工复核"} ] } }这段配置说明了一个关键设计:每个步骤都有独立的超时、重试次数和依赖关系。好处是当某个Agent卡住时,系统能精确定位问题步骤,对局部做补偿,而不是拖着整条链路一起失败。另外回退是确定性配置,不存在模型“灵机一动”走野路的可能。
4.3 关键容错细节与参数选择
参数不能拍脑袋定,要结合模型延迟和外部系统耗时。我一般给单次模型推理设30到40秒超时,工具调用设60秒以上,整条链路预算200到300秒。如果你用的模型P95延迟是15秒,把步骤超时设成15秒就会出现大量误杀,至少要留1.5到2倍余量。当然链路总预算要向产品侧看齐,用户等太久就会流失,所以超时具体值要在用户体验和模型性能之间找平衡。
批量处理场景还必须做并发控制。我给每个Agent加了信号量,比如同一时刻最多5个方案生成任务并发执行,超过就排队;排队超过30秒就触发降级转人工。信号量既保护模型供应商配额,也保护下游数据库,避免瞬时流量打崩。初始并发量建议调小,然后根据压测的延迟和错误率慢慢往上加,比一上来就拉满稳妥得多。
4.4 可观测性:必须能回答“为什么走到这一步”
多智能体系统本质上是分布式的,没有埋点和日志,出问题基本靠猜。我在每个Agent执行前后都记录结构化日志,字段包括输入摘要、输出摘要、耗时、Token消耗、置信度、错误码。每条日志都带一个Trace ID,用户报一个工单号,就能把整条处理链路串起来。
调试时最有价值的是日志回放。把线上的输入和输出完整保存下来,离线用同一批历史数据做回归。比如线上某个Agent某天突然把退货判成换货,我就把前几天的请求样本拿出来重放,对比是新版提示词导致的,还是外部订单数据变化引起的。没有回放机制,只能靠一个个样本肉眼挑,效率太低了。
4.5 上线前的故障演练
上线之前,我习惯故意给测试环境注入故障。比如把某个Agent的超时设成极短,或者在外部API响应里注入异常,再看系统会不会兜住。所有回退逻辑都要亲眼验证过才算数。我见过太多回退流程只在代码库里存在,从没人真正跑通过,真出事才发现兜底逻辑本身也是坏的。
故障演练的另一个好处是把“人会慌乱”这个因素提前暴露出来。生产环境里一旦出现异常,人的第一反应经常是手忙脚乱,但演练过几次之后,大家就知道该看哪个Trace、改哪个参数、走哪个流程。系统稳定从来不只是技术问题,也包含应急机制是否熟练。
5. 常见问题与排查技巧实录
5.1 子Agent之间字段对不上
典型症状是Agent B拿到的关键字段是空的,或者字段里带着解释性文字,比如金额字段写成了“约120元左右”。原因是Agent A没有严格按Schema输出,虽然看起来像正常文字,但混入了自然语言噪声。
排查方法先看编排层校验日志,确认在哪一步丢的字段,再到Agent A的原始输出里找根因。解决方案是强制Agent A输出JSON,校验不通过时重放一次,并把错误信息作为提示词的一部分喂回去。模型看到“上次缺少order_id字段”之后,多数情况会自动修正。
还有一个很实用的小技巧:输出字段命名要让模型容易理解。用customer_id,不要用cust_id;用order_status,不要用st。字段名越接近自然语言,模型遵守率越高。
5.2 Agent进入循环,成本失控
典型症状是两个Agent在循环里互相要求补充信息,或者一个Agent不停调用工具重试,Token消耗几分钟内暴涨。原因往往是没有给循环设置边界,或者反馈条件写得太宽。
我的解法是三层限制。第一层,编排层限制每个Agent最多交互5轮,达到上限强制终止;第二层,限制单Agent最大工具调用次数,超过就停;第三层,在Agent提示词里写入“如果你发现需要超过3次工具调用才能完成任务,请直接标记为需人工处理”。前两层是硬限制,第三层是模型自己止损。
成本失控靠预算熔断兜底。我给每个任务设Token预算,比如单工单上限8万Token,超过直接终止并转人工。宁可让人花几分钟处理,也不能让机器烧半天没结论,这个原则在很多场景都适用。
5.3 上下文无限膨胀
典型症状是Agent处理到后面,前面的信息全被忘光,或者开始重复问已经回答过的问题。原因是整个对话上下文越攒越长,模型注意力被后期内容绑架。
解决方案是分层记忆。长期信息像用户ID、订单号、历史结论,放在外部存储里,让Agent按需读取;短期交互信息才放上下文。当上下文接近窗口上限时,调度器触发一次摘要压缩,把之前的对话压成“已确认事实”,然后清空中间过程。用摘要替代原始对话,多轮任务能稳定很多。
5.4 工具副作用重复执行
典型症状是Agent因为超时重试,把同一个写操作执行了两遍,比如重复发了两张优惠券。模型调用本身是幂等的,但工具调用不是,这是生产事故的主要来源。
排查时要区分两个层面:查询类工具重试没有副作用,写操作类工具必须在调用前先做一次状态登记,登记为“将执行操作X”,执行完再更新为“已完成”。调度器看到状态已完成后就不会重放。这就是分布式系统里最常见的“先标记、后执行、再确认”模式,多Agent系统完全适用。
更稳妥的做法是尽量把写操作从Agent里剥离。Agent只负责决策,比如决定“应当发放5元优惠券”,真正的发券动作由编排层用确定性代码完成。Agent负责出主意,系统负责动手,即使Agent说了胡话,系统也不会直接闯祸。
5.5 全链路都成功但业务结果不对
最难受的故障是日志看每个Agent都正常返回,最终结果却不符合业务预期。原因通常不是格式问题,而是语义校验缺失。各环节校验都偏向于“格式正确”,没有真正检查“业务对不对”。
我引入了一个端到端评估集,每月准备几百个真实用户问题,标注标准答案和关键评分点,每次发版都用这批样本回归。同时线上增加抽检评审Agent,对已完成任务按比例抽查,重点看策略类回复有没有违规。这套评估体系初期投入不小,但长期收益很高,线下测试稳定住了,线上才不会天天救火。
6. 一些实实在在的体会
做了几年多智能体系统,我最大的体会是:稳定不是靠提示词写得多漂亮,而是靠系统结构兜底。架构师真正要做的事,是把模型当成一个“能力有限但潜力很大的处理器”,嵌进一套确定性流程里,让聪明用在刀刃上。
如果你正准备从单Agent升级到多Agent,我的建议是先别急着铺开一堆Worker。先把指标打点、超时重试、回退路径这三件事做扎实,再慢慢加Agent。任何一种编排模式都救不了没有基本保护的系统,但一套有保护的系统,哪怕每个Agent都普通,也能稳定地产出可接受的结果。
最后分享一个小技巧,每次上线前我会故意搞几次故障演练,把某个Agent的超时设成极短,或者在回复里注入乱码,看系统能不能兜住。多死几次,系统就越接近稳定,人也越有底气。