做技术的人都知道,线上故障是最让人头疼的事情之一。

特别是在凌晨的时候,电话突然响了,告警信息一条接一条地弹出来,你从睡梦中爬起来,打开电脑,开始排查问题。那种心跳加速、手心冒汗的感觉,经历过的人都懂。

上个月我们就经历了一次这样的故障,而且是在一个多Agent协作系统上。这个系统是我们团队花了大半年时间做的,用了多个AI Agent来协作完成复杂的业务流程。上线之后一直跑得挺稳,没想到那天晚上出了一个大问题。

故障从凌晨两点开始,到早上六点多才完全恢复,前后折腾了四个多小时。那四个小时真的是惊心动魄,我们几个人轮班排查,试了各种方法,最后终于找到了根因,把问题解决了。

故障恢复之后,我们做了一次详细的复盘。这篇文章我想把这次故障的经历和复盘的结果分享出来。从故障发生、排查过程到根因分析和改进措施,聊聊那些惊心动魄的时刻和总结的经验教训。

如果你也在做多Agent系统或者复杂的分布式系统,希望这篇文章能给你一些启发,帮你避免类似的问题。

系统背景

先简单介绍一下我们的系统。

这是一个多Agent协作系统,用来处理客户的复杂咨询请求。系统里有五个不同的Agent,每个Agent负责不同的任务:

第一个是意图识别Agent,负责理解用户的问题,判断用户想要什么。第二个是信息检索Agent,负责从知识库和数据库中检索相关信息。第三个是推理分析Agent,负责根据检索到的信息进行分析和推理。第四个是回复生成Agent,负责生成最终的回复内容。第五个是质量检查Agent,负责检查回复的质量和安全性。

这五个Agent通过一个协调器串联起来,形成一个流水线。用户的请求进来之后,协调器依次调用各个Agent,每个Agent处理完之后把结果传给下一个,最后生成回复返回给用户。

除了流水线之外,有些Agent之间还有反馈机制。比如质量检查Agent如果发现回复有问题,会把结果反馈给回复生成Agent,让它重新生成。如果重新生成之后还是有问题,就会升级到人工处理。

这个系统上线之后效果不错,处理了大量的客户咨询,准确率和效率都比以前的单Agent系统好很多。我们都挺自豪的,觉得这个系统做得不错。

但就是这个看起来很完善的系统,那天晚上出了一个让我们始料未及的问题。

故障发生

故障发生在凌晨两点十七分。

当时我正在睡觉,手机突然响了,是运维同事打来的。他说系统告警了,错误率突然飙升,从平时的不到1%涨到了30%多,而且还在继续涨。很多用户的请求都失败了,客服那边已经开始收到投诉了。

我一下子就清醒了,赶紧打开电脑,登录到监控系统。

打开监控一看,确实很严重。错误率曲线像悬崖一样掉了下去,从1%直接跳到了35%,而且还在往上走。响应时间也从平时的两秒涨到了十几秒,有些请求甚至超时了。

我赶紧叫了另外两个同事,三个人一起排查。

首先看了一下各个服务的状态。协调器服务正常,数据库正常,缓存正常,网络也正常。看起来基础设施没有问题。

然后看了一下各个Agent的日志。意图识别Agent正常,信息检索Agent正常,推理分析Agent正常,质量检查Agent也正常。唯独回复生成Agent的日志里有大量的错误。

错误信息是:"上下文长度超出限制,请减少输入内容后重试。"

看到这个错误我有点懵。回复生成Agent的输入是前面几个Agent的输出加在一起,我们之前测试过,正常情况下总长度不会超过模型的上下文限制。怎么会突然超出限制呢?

我赶紧看了一下具体的请求内容,发现了一个奇怪的现象:有些请求的上下文里,包含了大量重复的内容。同一段文字被重复了几十次甚至上百次,导致上下文长度暴增,超出了模型的限制。

这些重复的内容是从哪里来的呢?我们开始顺着日志往上查。

排查过程

排查的过程真的是一波三折。

首先我们怀疑是信息检索Agent出了问题,可能是检索到了重复的文档。但看了信息检索Agent的日志,发现它返回的结果是正常的,没有重复。

然后我们怀疑是推理分析Agent出了问题,可能是它在处理的时候产生了重复内容。但看了推理分析Agent的日志,它的输出也是正常的。

那重复内容到底是从哪里来的呢?

我们仔细对比了几个失败请求的完整链路日志,终于发现了问题所在。

问题出在质量检查Agent和回复生成Agent之间的反馈机制上。

正常的流程是这样的:回复生成Agent生成回复之后,传给质量检查Agent。质量检查Agent检查没问题,就返回给用户。如果有问题,就反馈给回复生成Agent,让它重新生成。

但那天晚上,因为某种原因,质量检查Agent和回复生成Agent之间形成了一个死循环。质量检查Agent说回复有问题,让重新生成;回复生成Agent重新生成之后,质量检查Agent还是说有问题,又让重新生成。这样来来回回,每一次循环,上下文里都会加上一段反馈信息。循环几十次之后,上下文里就堆满了重复的反馈信息,长度就超出限制了。

找到了问题的直接原因,但我们还需要搞清楚,为什么会形成死循环?质量检查Agent为什么一直说回复有问题?

我们又仔细看了质量检查Agent的检查逻辑。质量检查Agent会检查几个方面:内容是否准确、是否有敏感信息、是否符合格式要求、是否回答了用户的问题。

那天晚上,有一类用户的请求触发了一个边界情况。用户问的是一个比较模糊的问题,意图识别Agent把它识别成了A类型,但实际上应该是B类型。因为意图识别错了,后面的检索和推理都偏了,回复生成Agent生成的回复自然就不对。

质量检查Agent检查的时候,发现回复没有回答用户的问题,就判定为有问题,让重新生成。但因为意图识别的结果是错的,重新生成的回复还是基于错误的意图,所以还是不对。质量检查Agent还是判定有问题,继续让重新生成。

这样就形成了死循环。

更糟糕的是,我们的反馈机制没有设置最大重试次数。也就是说,只要质量检查Agent说有问题,就会一直重新生成下去,直到成功或者出错。那天晚上就是因为没有重试次数限制,才导致循环了几十次,把上下文撑爆了。

找到了根因之后,我们赶紧采取了紧急措施。首先在协调器里加了一个最大重试次数的限制,最多重试3次,超过之后就升级到人工处理。然后把那类有问题的请求暂时路由到人工客服,避免继续触发死循环。

做完这些之后,错误率开始下降,从35%慢慢降到了10%,再降到5%以下。到早上六点多,系统基本恢复正常了。

那四个多小时,我们三个人几乎没有停过,一直在看日志、分析问题、试解决方案。最后问题解决的时候,大家都松了一口气,但也累得不行。

根因分析

故障恢复之后,我们做了一次详细的根因分析。

表面上看,故障的直接原因是质量检查Agent和回复生成Agent之间形成了死循环,导致上下文长度超出限制。但深入分析之后,我们发现背后有几个更深层次的原因。

第一个原因是意图识别的准确率不够高。

意图识别是整个流水线的第一步,如果第一步就错了,后面的所有步骤都会跟着错。那天晚上就是因为意图识别错了,导致后面的检索、推理、生成全都偏了,质量检查怎么都通不过。

我们之前评估意图识别的准确率有95%,觉得还不错。但95%的准确率意味着5%的请求会识别错误。在大流量下,5%就是很多请求了。而且我们没有对识别错误的情况做兜底处理,一旦识别错了,后面就会一路错下去。

第二个原因是反馈机制没有重试限制。

我们在设计反馈机制的时候,只考虑了"质量检查不通过就重新生成"这个逻辑,没有考虑到如果一直不通过怎么办。我们默认假设重新生成几次之后就能通过,但实际上有些情况下,因为上游的错误,重新生成多少次都不会通过。

没有重试限制是一个很严重的设计缺陷。任何有循环的地方,都必须有退出条件,否则就有可能形成死循环。这个道理我们都懂,但在设计的时候还是忽略了。

第三个原因是缺乏全局的上下文管理。

在多Agent协作中,每个Agent都会往上下文里加内容。如果没有全局的上下文管理,上下文的长度就会不受控制地增长。

我们当时的做法是,每个Agent把自己的输出追加到上下文里,传给下一个Agent。但没有一个全局的机制来监控上下文的总长度,也没有在长度接近限制的时候做截断或者摘要处理。

第四个原因是监控和告警不够完善。

其实在故障发生之前,就已经有一些征兆了。比如重试次数开始增加,上下文长度开始变长。但我们的监控只关注了最终的错误率和响应时间,没有监控这些中间指标。所以当错误率突然飙升的时候,我们才知道出了问题,而没有提前预警。

第五个原因是测试覆盖不够。

我们在测试的时候,主要测试了正常的流程,对边界情况和异常情况测试得不够。比如意图识别错误的情况、质量检查一直不通过的情况、上下文超长的情况,这些都没有充分测试。

如果我们在测试的时候能覆盖这些边界情况,这个问题在上线之前就能发现,不会等到线上出故障了才知道。

这五个原因加在一起,最终导致了这次故障。每一个原因单独看可能都不是大问题,但组合在一起,就造成了一次严重的线上事故。

改进措施

找到了根因之后,我们制定了一系列的改进措施。

第一,提高意图识别的准确率。

我们对意图识别Agent做了优化,增加了更多的训练数据,特别是那些容易混淆的类别。同时引入了置信度机制,当意图识别的置信度低于某个阈值的时候,不直接走自动化流程,而是先做一次澄清或者转人工。

另外,我们还在流水线中加了一个意图校验的步骤。在检索和推理之前,先校验一下识别出的意图是否合理,如果不合理,就重新识别或者转人工。

第二,给所有的循环和反馈机制加上限。

这是最直接的改进。我们给质量检查的反馈机制加了最大重试次数,最多3次。超过3次还不通过,就直接升级到人工处理,不再继续循环。

同时我们检查了系统中所有的循环和反馈机制,每一个都加上了明确的退出条件和最大次数限制。任何可能形成循环的地方,都必须有兜底的退出机制。

第三,建立全局的上下文管理机制。

我们在协调器中加了一个上下文管理器,负责跟踪和管理整个流水线的上下文长度。每一个Agent处理完之后,上下文管理器都会检查总长度,如果接近限制,就对之前的内容做摘要或者截断,保证总长度在安全范围内。

同时,上下文管理器还会记录每个Agent贡献的内容长度,如果某个Agent的输出异常长,会触发告警。

第四,完善监控和告警。

我们增加了很多中间指标的监控,包括每个Agent的调用次数、重试次数、平均输出长度、上下文总长度、质量检查通过率等等。

同时设置了更灵敏的告警阈值。比如重试次数超过正常水平的2倍就告警,上下文长度超过限制的80%就告警。这样在问题还没发展成严重故障的时候,我们就能收到预警,及时处理。

第五,加强测试覆盖。

我们建立了一个更完善的测试用例库,不仅覆盖正常流程,还大量增加了边界情况和异常情况的测试用例。比如意图识别错误、质量检查不通过、上下文超长、某个Agent超时、某个Agent返回异常等等。

同时我们引入了混沌工程的思路,在测试环境中主动注入故障,看看系统能不能正确处理。通过这种方式,发现了很多潜在的问题。

第六,建立故障演练机制。

我们决定每季度做一次故障演练,模拟各种可能的故障场景,测试团队的应急响应能力和系统的容错能力。通过演练,让每个人都熟悉故障处理的流程,避免真正出故障的时候手忙脚乱。

这些改进措施,我们在故障之后的两周内全部落地了。落地之后又做了一轮全面的测试,确认没有问题之后才重新全量上线。

经验教训

这次故障给了我们很多教训,也让我们对多Agent系统的设计和运维有了更深的理解。

第一个教训是,多Agent系统比单Agent系统复杂得多。

单Agent系统只有一个模型,出了问题比较好排查。但多Agent系统有多个Agent,每个Agent都可能出问题,而且Agent之间的交互也可能出问题。问题的来源更多,排查的难度也更大。

所以在做多Agent系统的时候,一定要有完善的日志和链路追踪。每个请求从进入系统到返回,每一步的输入输出、耗时、状态都要记录下来。这样出了问题才能快速定位。

第二个教训是,任何循环都必须有退出条件。

这是一个很基础的编程原则,但在复杂的系统设计中很容易被忽略。特别是在AI Agent的场景下,我们总觉得模型是智能的,应该能自己处理好各种情况。但实际上,模型也会犯错,也会陷入死循环。

所以在设计任何有循环或者反馈的机制时,一定要问自己:如果一直不成功怎么办?最大重试次数是多少?超过之后怎么处理?把这些问题想清楚,才能避免死循环。

第三个教训是,上下文管理是多Agent系统的核心挑战之一。

在多Agent协作中,上下文会随着Agent的处理不断增长。如果没有有效的管理,上下文很容易超出模型的限制,或者包含太多无关信息,影响模型的效果。

所以从设计之初就要考虑上下文管理的问题。要明确每个Agent应该往上下文里加什么内容,怎么控制总长度,什么时候做摘要,什么时候做截断。这些都需要精心设计,而不是等到出了问题再补救。

第四个教训是,边界情况比正常情况更重要。

正常情况下,系统一般都能正常工作。真正考验系统质量的,是那些边界情况和异常情况。比如输入异常、模型返回异常、网络超时、依赖服务不可用等等。

在设计和测试的时候,要把更多的精力放在边界情况和异常情况上。正常流程跑通了不算什么,能在各种异常情况下都优雅地处理,才是真正可靠的系统。

第五个教训是,监控要覆盖全链路,而不只是最终结果。

我们之前只监控了最终的错误率和响应时间,这是不够的。最终结果出问题的时候,故障已经发生了。要做到提前预警,就需要监控中间环节的指标,比如每个Agent的状态、重试次数、上下文长度等等。

全链路的监控能让你在问题的早期就发现异常,在它发展成严重故障之前就处理掉。这对于保障系统的稳定性非常重要。

第六个教训是,故障复盘要深入,不能停留在表面。

故障发生之后,很多人会找到一个直接原因,改了就完事了。但这样做的话,类似的故障以后还会发生。真正有价值的复盘,要深入到根因,找到背后的设计缺陷、流程问题、文化问题,然后从根本上解决。

我们这次复盘就花了很多时间,从直接原因挖到深层原因,从技术问题挖到流程问题,从设计缺陷挖到测试不足。只有这样全面深入的复盘,才能真正从故障中学习,避免重蹈覆辙。

写在最后

这次多Agent协作故障,是我们团队经历过的最惊心动魄的一次线上事故。四个多小时的排查和修复,现在想起来还心有余悸。

但换个角度看,这次故障也是一次宝贵的学习机会。它让我们看到了系统设计中的很多不足,也让我们对多Agent系统的复杂性有了更深的理解。通过这次故障和复盘,我们的系统变得更健壮了,团队的应急能力也提升了。

做技术就是这样,没有永远不出故障的系统。重要的是,出了故障之后能不能快速恢复,能不能从中学到东西,能不能让系统变得更好。

多Agent系统是一个很新的领域,很多最佳实践还在探索中。我们也是在摸着石头过河,踩了很多坑,也总结了一些经验。这篇文章分享的只是其中一次故障的经历,还有很多其他的坑和经验,以后有机会再慢慢分享。

如果你也在做多Agent系统,或者对这个领域感兴趣,欢迎交流。让我们一起在这个新领域里探索,一起成长。

最后用一句话来结束这篇文章:"故障不可怕,可怕的是不从故障中学习。每一次故障都是一次成长的机会,关键在于你能不能抓住它。"

愿我们的系统越来越稳定,愿我们的团队越来越强大。