跳到主要内容

凌晨三点调试 AI:LLM 驱动系统的故障响应指南

· 阅读需 10 分钟
Tian Pan
Software Engineer

你正在值班,凌晨三点,告警触发:过去一小时内 AI 聊天功能的用户满意度下降了 18%。你打开日志,却看到……什么都没有。每个请求都返回了 HTTP 200,延迟正常,没有任何报错。

这就是 AI 事故的体验。传统值班的肌肉记忆——grep 堆栈跟踪、找到异常、部署修复——在这里完全失效。系统并没有崩溃,它做的正是它被设计来做的事。只是输出结果是错的。

糟糕的生成结果没有堆栈跟踪,它有概率分布。如果你还没有为概率系统重新训练你的事故响应本能,你将在凌晨痛苦地盯着健康的监控指标,而你的用户却在得到错误的答案。

以下是如何调试它的方法。

为什么 AI 事故在结构上与众不同

传统软件事故有一个可以指向的根因:空指针解引用、防火墙规则配置错误、数据库查询缺少索引。某个具体的事情发生在某个具体的代码行。

AI 事故通常有诱因,而不是根因。模型本身没有崩溃——它产生的输出遵循一组条件:你给它的提示词、你传入的上下文、你包含的示例、你配置的采样参数,以及提供商今天实际运行的底层模型版本。任何这些条件都可能发生变化并导致输出质量下降,而不触发任何错误。

有研究追踪了 LLM API 的稳定性,发现当提供商更新其底层模型时,58.8% 的提示词和模型组合会出现退化——其中 70% 的退化代表超过 5% 的准确率下降。模型"改变了",但没有人动过你的部署。

这带来了三个传统事故响应所没有的诊断挑战:

设计上的不可复现性。 温度参数 > 0 意味着相同的输入会产生不同的输出。你无法可靠地复现用户得到的确切糟糕回复,只能表征响应的分布。

无声的成功信号。 你的基础设施监控全程看到的都是 200。唯一表明出了问题的信号来自下游的质量指标——用户反馈、任务成功率、输出评分流水线——而这些你可能已经建立,也可能还没有。

模糊的责任归因。 当模型产生错误答案时,这是模型失败?提示词缺陷?检索失败导致模型获得了错误的上下文?参数配置错误?每种原因需要不同的修复方案。

分类决策树

当 AI 事故触发时,在得出任何结论之前,按照以下顺序进行排查:

第一步:这是采样方差还是系统性问题?

取一个有代表性的失败输入,通过模型运行 10 次。如果大约一半的运行产生好结果,另一半产生差结果,你面对的是方差问题。如果每次运行都产生差结果,你有一个系统性故障。

这单个步骤能节省大量时间。方差问题的处理方式与系统性问题完全不同——许多凌晨三点的误报实际上是恰好在最近的监控窗口中集中出现的方差事件。

第二步:最近的部署周期中有什么变化吗?

按顺序检查:

  • 已部署的提示词版本(即使是措辞变化也算)
  • 模型版本或提供商端点变更
  • 检索流水线修改(如果使用 RAG)
  • 上游数据源更新

系统性故障几乎总是与最近的变更有关联。这个变更可能不是你做的——提供商会不通知地更新他们的基础模型——但某些东西发生了变化。

第三步:这是提示词问题还是模型问题?

这个区别很重要,因为它们有完全不同的修复路径。提示词问题你今晚就能修。模型能力缺口需要不同的策略。

提示词问题的迹象:

  • 失败集中在特定的输入模式或主题领域
  • 当你手动重新表述输入时,模型能产生正确答案
  • 上下文示例过时、相互矛盾或不再具有代表性
  • 指令模糊,或任务描述埋在长上下文的末尾

模型问题的迹象:

  • 失败在所有输入模式中均匀出现
  • 手动重新表述没有帮助
  • 模型从未在这个任务上达到准确,即使在早期测试中
  • 提供商的模型更新与退化开始时间相关

第四步:对于 RAG 系统,优先排除检索失败

检索失败尤其隐蔽,因为模型接收到错误的上下文,基于该上下文生成一个充满信心的响应,而基础设施监控看起来一切正常。检索流水线中存在七种已记录的失败模式,但运行时最常见的是:

  • 检索到的片段技术上相关但缺少所需的具体事实
  • "迷失在中间"的退化:模型对上下文窗口开头和结尾的关注效果好,但对埋在长输入中间的信息,准确率下降 30% 以上
  • 上下文腐蚀:随着总上下文长度增加,模型性能下降,即使所有内容各自都是相关的

检查实际的差响应是否与特定检索文档相关联。如果你能重建模型实际获得的内容,并发现答案根本不在那里,那模型从来就不可能正确生成它。

事故之前你需要的可观测性基础设施

没有在事故发生前记录正确的内容,你就无法调试 AI 事故。挑战在于,记录"模型返回了什么"是必要的,但远远不够。

通过记录以下内容来重建完整的图景:

完整的请求上下文。 不只是最终消息——而是完整的序列化提示词,包括系统消息、上下文示例、检索到的片段、对话历史以及任何注入的数据。这通常比你为传统 API 调用记录的内容大 3-10 倍,但这是重现模型实际看到的内容的唯一方式。

模型参数。 温度、top_p、max_tokens、模型版本和提供商端点。这些在部署配置中会悄悄变化,并可能显著改变输出分布。

中间流水线步骤。 对于 RAG:每个检索到的片段及其相关性分数、重排序决策以及最终组装的上下文。对于多步骤代理:每个工具调用及其参数和响应。这些中间产物通常就是故障所在之处。

质量信号。 不只是请求是否成功,而是输出是否良好。这可以是一个轻量级的启发式方法、一个评分模型,或者甚至只是来自下游用户行为的布尔值。没有这个,你根本无法判断是否存在事故。

没有这四类信息,你就是在背着手调试。你会看到日志显示模型成功运行,却无法知道它实际获得了什么或产生了什么。

量化事故:值班的统计现实

传统事故量化很简单:错误率从 0.1% 增加到 2.3%。对于 AI 来说,这更难。

输出质量是连续的,不是二元的。一个响应可以部分正确、风格上有问题、事实上不完整,或与意图不符,而不完全崩溃。这意味着你需要质量分数的分布,而不仅仅是错误计数。

量化事故时,需要回答:

  • 退化开始前后的平均质量分数是多少?差值是多少?
  • 退化是在所有用户和输入类型中均匀分布,还是集中在特定的用户群体?
  • 质量分数的方差是否改变了,即使均值保持不变?(即使平均质量保持稳定,方差增加也是一个事故。)

群体分析通常能揭示真实的范围。看起来像是 3% 整体质量下降的事故,实际上可能是特定用户群体、输入语言或主题领域下降了 40%——而大部分流量不受影响。找到这个边界能告诉你问题确切在哪里。

对于跨模型版本的统计测试,标准 t 检验适用于平均质量分数等连续指标。对于任务完成率(二元结果),卡方检验或双比例 z 检验能给你严格的比较。无论如何:使用适当的样本量。LLM 输出方差足够高,以至于小样本会产生误导性结果。

事后复盘问题:为随机系统编写事故报告

AI 事故响应中最难的部分不是调试,而是事后复盘。

诱人的做法是写"根因:模型幻觉"然后关闭工单。要抵制这种诱惑。这不是解释——它只是症状的标签。而且它不给你任何可操作的内容。

对随机系统有效的事后复盘需要:

表征概率,而不仅仅是发生情况。 "模型产生了错误答案"没有用。"模型在 2.4% 匹配此输入模式的查询中产生错误答案,而基线是 0.3%"给了你一个可衡量的改进目标。

识别诱因,即使它们不是确定性的。 随机失败仍然有使其更或更少可能发生的条件:上下文质量、提示词清晰度、输入特征、采样参数。记录哪些因素与失败相关。即使没有单一根因,你也可以通过解决诱因来降低失败概率。

将检测缺口与故障本身分开。 在大多数 AI 事故中,故障在任何人注意到之前就已经发生了。事后复盘分析的一部分应该检查:这实际上是什么时候开始的?是什么监控缺口允许它未被检测到?答案通常会揭示本可以更早发现它的缺失质量指标。

创建回归测试。 在你关闭工单之前,失败案例需要进入你的评估套件。这是 AI 版本的修复 bug 后编写的单元测试。区别在于你不是在测试特定输出——你是在测试输出质量分布不会退化到阈值以下。

凌晨三点的快速版本

如果你真的在凌晨三点看到这篇文章,这是最短的路径:

  1. 将失败的输入运行 10 次。是持续差还是间歇性差?持续 = 系统性问题。间歇 = 方差。
  2. 检查部署时间线。过去 24 小时内什么发生了变化?提示词、模型、检索、数据?
  3. 检查模型实际获得了什么。拉出完整的记录上下文。正确的信息在那里吗?
  4. 按群体检查输出质量。这影响所有人,还是特定的流量切片?
  5. 如果是系统性的且没有最近的变化:检查提供商是否更新了他们的底层模型。

答案在于模型看到了什么,而不在于模型是否响应了。将你的调试本能从"系统是否崩溃?"转变为"模型是否获得了它所需的内容?"——你会更快找到问题所在。

结论

LLM 系统的事故响应是与传统值班不同的学科,但它是可以学习的。根本性的转变是从寻找错误到表征分布:不是"它失败了吗?"而是"它在什么输入上以多高的频率失败,为什么那些输入不同?"

处理 AI 事故好的团队有几个共同特征:他们虔诚地记录完整的请求上下文,他们拥有在用户投诉之前触发的质量指标,他们已经内化了模型无法做某事与提示词不让它做某事之间的区别。大多数凌晨三点的 AI 事故是提示词问题,少部分是检索失败,少数是真正的模型能力回退。

在你需要它之前建立可观测性基础设施。在请求时未能捕获的日志,正是你在事故时迫切需要的日志。

References:Let's stay in touch and Follow me for more thoughts and updates