资讯详情

多Agent协作系统崩溃排查:从单元测试全绿到真实链路崩塌

📅 2026/10/20 8:10:09 | 华诺云谱 👁 阅读
多Agent协作系统崩溃排查:从单元测试全绿到真实链路崩塌
昨天排查一个多 Agent 协作系统的崩溃问题团队复盘的时候每个人都拍着胸脯说“我的 Agent 测过了”——单元测试确实全绿Mock 也打得很漂亮但一放到真实协作链路里整个系统直接跪下。这个场景我太熟了几乎每个开始做多 Agent 项目的团队都会在某个深夜撞上同一堵墙。先说结论单个 Agent 测试通过只能证明它在孤立环境里能完成自己的局部任务。多 Agent 系统崩的从来不是某个 Agent而是 Agent 之间的“接缝”。这篇文章会把这类问题拆开讲透包括为什么测试全绿还会崩、高频根因有哪些、一次真实崩溃的完整排查链路以及我后来总结出的四件套改造方案。适合正在做多 Agent 应用、或者准备把 Agent 从 Demo 推向生产环境的开发者参考。1. “每个 Agent 都测过了”为什么成为最危险的假信号1.1 单元测试验证的是“孤岛”不是“协作”大多数团队给 Agent 写测试的时候思路还停留在传统后端服务的单元测试把输入喂进去检查输出对不对。这个思路没错但对 Agent 来说它天然会掩盖一类问题——依赖假设。举个例子某团队做过一个客服工单自动分拣系统分三个 Agent读信 Agent 负责提取用户诉求分类 Agent 负责打标签路由 Agent 负责派单。每个 Agent 的测试用例都设计得很完善读信 Agent 把“退款、投诉、咨询”分类准确率跑到 95% 以上。可是联调的时候分类 Agent 隔三差五就把正常工单丢进“无法识别”队列。反复查了很久才发现读信 Agent 在自己测试时输入文本是干净的、结构化的。但在真实链路里前面的 OCR 识别会往文本里插入不可见字符读信 Agent 提取的“诉求”后面跟着一个看不见的控制字符分类 Agent 的匹配逻辑是按完全匹配来设计的自然全部落空。这就是典型的“每个 Agent 都测过了”陷阱每个模块在自己的隔离环境下确实工作正常但模块之间的数据流如果在某个环节发生了微妙变形下游 Agent 根本感知不到于是只能在最终结果上爆发。这种问题在单体应用里通常不会出现因为数据和函数之间的接口是强类型、编译期就能发现的而在多 Agent 系统里Agent 之间传输的是自然语言文本、半结构化的 JSON、或者带嵌入向量的记忆片段这些内容的“类型安全”全靠自觉。1.2 Mock 把“接缝”问题藏得无影无踪有人会反问我做单元测试的时候也 mock 了其它 Agent为什么没发现问题因为 mock 恰恰是最容易制造假安全感的一环。你 mock 其它 Agent 时通常是按照“我觉得它会返回什么”来构造假数据的而这个“我觉得”往往就是问题的源头。我见过一个内部知识库问答系统规划 Agent 会调用两个检索 Agent一个查文档库一个查数据库。测试时团队按预设格式 mock 了两个检索结果规划 Agent 完美地把答案拼接出来了。实际联调时文档检索 Agent 返回的结果里混入了一段 Markdown 表格数据库检索 Agent 返回的是纯文本规划 Agent 的提示词只教过它处理“纯文本上下文”突然来一段 Markdown 表格它直接把表格原样吞进答案最终用户的界面上出现了满屏的管道符号和横线。Mock 的悲剧在于它把“接缝”处的格式约定、字段内容、边界行为全都按最理想情况模拟了。你 mock 掉的是问题不是依赖。真实的另一个 Agent 是神经网络驱动的它的输出带有概率性和不确定性今天返回这句话明天可能换个措辞、换个结构。这是多 Agent 系统和传统微服务最本质的区别微服务的接口契约是代码强制的Agent 的输出契约是提示词软约束的随时可能漂移。2. 多 Agent 协作崩溃的四大高频根因2.1 上下文污染不是模型傻是共享记忆串台了多 Agent 系统最常见的架构是把多个 Agent 挂在同一个会话上下文里共用一套 memory。这个设计省事但也最容易出鬼。上下文就像一个合租房的公共冰箱每个 Agent 都往里放东西也都从里面拿东西但没人给冰箱做分区管理。举个例子一个跨境电商的售后分析系统有情绪分析 Agent 和退款决策 Agent。情绪分析 Agent 在处理完一条“愤怒的差评”后把“用户情绪阈值极差”写进了上下文。紧接着退款决策 Agent 读取同一份上下文它把“极差”理解成了“风险极高”于是直接给一个正常退货请求打上了“疑似恶意退款”标签。如果上下文里没有别的 Agent 留下过类似字段单测根本测不出这种问题——因为单测时每个 Agent 拿到的都是刚初始化、干净的上下文。我自己的经验是上下文污染的本质是命名空间冲突和状态残留。多个 Agent 共用一个 context 时字段的覆盖顺序、读取时机、清理策略全都变成了隐性契约。A Agent 写一个status字段B Agent 读它C Agent 可能中途又把status改成别的东西。只要某个环节的时序一变整条链路的判断就全偏了。2.2 共享状态竞争不是我慢是你改了第二个高频根因是共享状态的并发竞争。多 Agent 系统通常会有缓存、任务队列、向量数据库这些外部依赖多个 Agent 可能同时读写同一份数据。比如两个并行 Agent 同时把各自的处理结果写回同一个缓存键后写的覆盖先写的这还算温和的更麻烦的是两个 Agent 同时在向同一个状态表里插入记录如果没有幂等设计最终表里会出现双份数据或者互相矛盾的记录。有次排查一个多 Agent 调度系统场景是“并行调研 汇总”。三个调研 Agent 各自去搜索资料最后汇总 Agent 读取它们的结果给老板生成报告。单测时三个调研 Agent 都是按顺序执行完再汇总的没问题。但真实运行时三个 Agent 是并发执行的它们会同时往一个共享队列里追加内容。队列没有加锁也没有唯一 ID结果某个瞬间两个 Agent 往同一个 ID 下写了内容汇总 Agent 读到一半觉得内容冲突直接判定“信息不可信”整条链路失败。这个问题的本质是多 Agent 系统天然是并发的但你写测试的时候按顺序执行的惯性思维还在。这就像你在单线程环境里测试多线程代码永远测不出竞争条件。另外要注意 Agent 之间的重试机制一个 Agent 超时重试可能把同一批消息发出两遍下游 Agent 如果没做幂等处理数据就翻倍了。2.3 超时与级联你等它、它等你第三个根因是超时和级联失败这可能是最隐蔽的。在一个多 Agent 协作链路里每个调用都有超时时间单个 Agent 响应慢一点没问题但如果多个 Agent 互相等待就会形成链路级别的死锁。讲一个我实际踩过的场景。某调度系统有四个 Agent任务分解 Agent、执行 Agent A、执行 Agent B、校验 Agent。正常流程是任务分解 Agent 把任务拆成两部分分别交给 A 和 B然后校验 Agent 等 A 和 B 都完成后统一检查。问题是A 的完成回调会等校验 Agent 确认校验 Agent 又在等 B 的结果而 B 因为上游某接口繁忙迟迟没返回。A 等得超时后自动重试重试又加重了 B 的压力最终四个 Agent 全部陷入等待整个链路被“重试风暴”打挂。这种问题的悲哀在于每一个 Agent 单测时都是好的——单测时你只测它的输入输出超时时间给得也足够宽裕。但在真实协作环境里多个 Agent 的等待时间会累加任何一个环节的延迟都可能成为压垮全链路的最后一根稻草。处理超时一定要看整条链路的预算而不是单点预算。2.4 数据契约漂移字段在某个环节悄悄变了最后一个根因是 Agent 之间传递数据的契约漂移。前面提到过Agent 的输出是自然语言/半结构化数据很容易被中间环节改动。最常见的场景是上游 Agent 返回一个 JSON下游 Agent 解析但中间经过日志系统、消息队列或者某种管道时JSON 字段被重新序列化了一遍缩进变了、字段顺序变了、甚至数字变成了字符串。有团队做过一个数据清洗链路清洗 Agent A 输出{amount: 100}下游审核 Agent B 读这个字段做金额判断。一切正常。后来运维在中间加了一个审计管道把消息转成字符串后拼接了一些元信息再转回 JSON。结果数字全变成了字符串B 的判断逻辑是if data[amount] 100字符串和数字比较时类型不匹配直接走了兜底分支。整条链路没有报错但是所有金额判断全部失效。这种漂移在单测中极难发现因为你单测时不会在中间塞一层“会篡改数据”的代理。想要防住只能在集成测试里加真实组件的验证或者给每个 Agent 的输出加一层严格 schema 校验。3. 一次真实崩溃的完整排查链路3.1 复现条件单 Agent 都正常组合必现上周我帮忙排查的崩溃长这样系统是一个合同审查平台三个 Agent 分工——抽取 Agent 负责从合同文本里抽关键条款比对 Agent 负责跟公司标准条款比对修订 Agent 负责生成修改建议。上线前三个 Agent 各自测试全都通过。上线后差不多每处理 50 份合同就有 1-2 份崩溃报错是“修订建议生成失败”。崩溃还不是必现的看起来像偶发这是最恼人的。我的第一反应是看日志但日志里三个 Agent 各自都没有 error。于是我们开始做复现实验把崩溃的合同单独拿出来分别喂给三个 Agent发现三个 Agent 都能处理。再把完整链路跑一遍又崩。这一下就确认了问题一定出在 Agent 之间的传递层某个 Agent 的输出在传递过程中被污染了。3.2 逐段隔离把链路拆开找到第一个错误点排查的方法是逐段隔离。我们在抽取 Agent 输出后加了一段 dump比对 Agent 输入前加了一段 dump比对 Agent 输出后又加了一段 dump修订 Agent 输入前再加了一段 dump。通过对比这些 dump才发现链条断在比对 Agent 的输出到修订 Agent 的输入之间。比对 Agent 的提示词里有一个隐含要求让它输出“修改建议列表”当它发现需要修改的条款过多时它会以“以下是需要修改的条款”开头然后列一大段。修订 Agent 的输入解析逻辑按“第一条必须是 JSON”来设计的看到大段自然语言直接解析失败返回空建议上层没有做失败兜底整个任务就崩了。根因很有意思比对 Agent 在数据量小时输出完全干净一旦数据量大它会自动切换成“长篇自然语言”模式完全不做格式约定。这是一个典型的、需要在真实数据分布下才会暴露的行为模式。3.3 根因定格你以为的“偶发”其实是统计必然复盘的时候我们都感慨这个问题看似偶发其实是必然。它只在“需要修改条款超过 20 条”时触发而 50 份合同里总有 1-2 份条款多、修改点密的。只要样本量够大崩溃概率就趋向于 1。这件事给我一个很重要的教训多 Agent 系统的 bug 往往不是逻辑性的而是“分布性”的。它跟某个 Agent 的行为在某个输入分布区间发生了模式切换有关这种模式切换在单测样本里样本量不够根本不会触发。所以排查多 Agent 崩溃一定不要只盯着“固定输入看结果”而要主动去找“触发条件”。4. 让多 Agent 系统真正可测的四项改造4.1 契约测试Agent 之间的“接口合同”第一项改造是给 Agent 之间加契约测试。别把 Agent 的交互当成“智能体聊天”把它当成微服务接口来对待。每个 Agent 的输入输出都要定义清晰的 schema包括字段名、类型、允许的取值范围、可选的描述文本格式。然后写契约测试跑的不是 Agent 的“智能逻辑”而是它的“输入输出格式是否严格达标”。具体做法是在测试环境里用固定的输入样本断言 Agent 输出必须符合预定义 schema。schema 校验不通过测试就失败。比如上文里的比对 Agent契约测试会强制要求“修改建议列表”必须以 JSON 数组返回不允许输出任意自然语言。一旦模型抽风输出了非 JSON契约测试立刻亮红灯。这只是把原来“提示词里的软约束”升级成了“测试里的硬约束”。契约测试还有一个好处它能逼着你去明确每个 Agent 的边界和所有权。比如定义清楚哪些字段是这个 Agent 负责的哪些是外部传入只读的哪些是允许它新增的。这个思路和微服务设计里的“Bounded Context”是一个道理只不过在 Agent 场景里你要通过契约测试来保证不会因为模型漂移而越界。4.2 场景级集成测试按业务流程编排而不是按 Agent 编排第二项改造是把测试从“按 Agent 组织”改成“按业务场景组织”。过去的测试是“测试抽取 Agent”“测试比对 Agent”好的做法是“测试完整合同审查流程”同时覆盖正常合同、条款缺失的合同、全都是坑的合同、超大条款量的合同等各类场景。这里的核心原则是集成测试场景要模拟真实的多 Agent 协作流而不是反复验证单个 Agent 的能力。我在实践里会把测试场景分成三类正常流、边界流、故障流。正常流是一个典型业务请求从入口到出口全链路跑通边界流是触发某个 Agent 的极限条件比如超长输入、超大输出故障流是人为让某个 Agent 返回异常或超时验证其它 Agent 能否优雅降级或超时退出。写集成测试的时候有一个容易被忽略的点不要用完全固定的 mock 数据。哪怕你不真的起模型也要准备几组“带噪声”的数据比如多了空白字符的文本、字段顺序打乱的 JSON、被截断的长文本。这样才能覆盖到真实协作链路里最容易出问题的“数据形态漂移”。4.3 故障注入把崩溃提前到上线之前第三项改造是主动做故障注入演练。多 Agent 系统真正上线后最怕的不是哪个 Agent 挂了而是某个 Agent 在整条链路中段挂掉后面的 Agent 还在傻等。与其等线上出现这种事故不如在测试环境里主动制造。我们用的办法是写一个流量染色工具在测试场景中随机给某个 Agent 的调用插入延迟、随机报错、随机返回空结果。然后观察整条链路的反应会不会因为一个 Agent 超时引发重试风暴会不会因为一个 Agent 返回了意料之外的输出导致下游 Agent 的解析逻辑直接炸故障注入的目的不是验证 Agent 本身而是验证协作链路的韧性。做这个演练时我给的建议是把故障注入做成随机参数但不要只注入一次。因为多 Agent 系统的很多问题都是概率性的、时序耦合的重复注入几次可能触发完全不同的失败模式。我第一次做故障注入时连续跑了 3 轮才看到一个必现的时序竞争问题这要是直接上线大概率要在半夜才能撞上。4.4 可观测性链路追踪、状态快照、行为重放第四项改造也是我认为最值得投入的是把可观测性做成多 Agent 系统的基础设施。排查多 Agent 问题最难的不是找到错误日志而是还原每个 Agent 在哪个时间点、基于什么输入、做了什么决定。现在很多团队只记录最终的 API 调用日志但多 Agent 系统的中间状态太重要了一个 Agent 的中间推理过程、它读到的上下文快照、它写回去的字段、它的输出格式、下游 Agent 实际收到的内容——这些才是在出问题时最需要回看的证据。我的做法是给每个 Agent 加四个数据埋点拿到什么输入、读了哪些上下文、产生了什么输出、向上游和下游传递了什么。每条数据都带时间戳和链路 ID方便做全链路回放。另外还要注意给 Agent 加日志别只记录结果要记录行为和选择。比如一个 Agent 在多个工具之间做选择时它选了哪个工具、因为什么理由选的这些信息在排查时价值巨大。有了完整的链路追踪再配合之前说的“加 dump 对比输入输出”的排查方法效率会高非常多。5. 写在最后的实操心得现在再做多 Agent 项目我会在第一周就搭好三件事契约测试、场景级集成测试、全链路日志。看起来多花了两三天时间但省下的是以后无数个凌晨排查崩溃的夜晚。另外分享一个小技巧单测保住下限契约测试管住格式场景测试堵住流程故障注入练出韧性。这四个层级缺一不可前两个是防线后两个是防线之外的容灾能力。很多人觉得 Agent 项目迭代快写太多测试会影响速度我的体会正相反多 Agent 系统的不确定性远比传统服务高没有这套防线每改一个提示词都可能引入新的连锁故障到那时候补测试的时间比一开始就写测试要多出一个数量级。最后别迷信“每个 Agent 都测过了”这句话。每个 Agent 都测过只是起点。真正要测的是它们连接之后组成的那个更大系统——那才是你实际要交付的东西。
📝

华诺云谱内容团队

资深建站顾问 · 行业研究员

10年+企业数字化服务经验,专注智能建站、SEO优化与品牌营销,持续输出建站技巧、行业洞察与营销干货,已帮助5000+企业实现数字化增长。

你可能需要的服务

订阅华诺云谱资讯周报

每周一封,精选建站技巧、SEO与营销干货,直达邮箱。已有 8,000+ 企业主订阅,助你少走弯路。

↑