跳到主要内容

你的 SRE 复盘模板遗漏了决定每次 LLM 故障的六个关键字段

· 阅读需 12 分钟
Tian Pan
Software Engineer

当你第一次用经典的 SRE 复盘(Postmortem)模板来分析 LLM 事故时,模板赢了,而事故输了。时间线、诱因、缓解措施、预防措施 —— 每个字段都填好了,每个复选框都勾选了,但在文档的最后,没人能回答唯一重要的问题:究竟是哪个变量发生了变动?不是部署事件。不是基础设施故障。不是代码变更。而是 Prompt 的修订、路由选择的模型切片、未触发报警的 Eval 评分所用的 Judge 配置、质量投诉发生时的检索索引状态、规划器(Planner)正在组合的工具 Schema 版本,或者是异常时间段内的流量组合。这些在模板里都没有对应的一行。

SRE 模板并不是为那些“事实来源是观察到的行为而非代码路径”的系统设计的。在 LLM 技术栈中默默变动的变量,正是模板从未需要列举的变量。强行借用模板,只会产生那种被归类为“持续调查中”的“我们不知道发生了什么变化”的复盘报告。

这并不是反对无责文化或 SRE 工作流。这两者都可以完美迁移。核心观点是,文档中的字段 —— 作者必须回答的明确提示 —— 需要为 LLM 系统实际具备的变量构建第二套方案。如果没有这些提示,复盘作者盯着空白的“诱因”框,只能写下“模型退化”,因为页面上没有任何字段要求他们去查看 Judge 配置。

SRE 模板缺失的六个字段

传统的 SRE 复盘将受测系统视为在给定输入、代码和配置下的确定性系统。复盘模板的“诱因”部分假设你可以通过阅读部署日志和基础设施仪表盘来识别变化。对于 LLM 系统,有六个变量超出了这个范畴,每一个都需要在文档中占据一行。

事故发生时的 Prompt 修订版本。 不是今天存在于 main 分支中的 Prompt,而是产生异常输出那一刻渲染出的 Prompt。Prompt 会通过系统提示词编辑、Few-shot 库轮换、检索上下文注入以及按请求组合的动态指令块发生演变。“我们改了几个字”在变更日志中看起来只是单行 diff;但在行为上,它可能是一次完整的重新校准。该字段必须捕获字面上渲染出的 Prompt,以及组成它的每个组件的版本 ID。

模型版本和路由切片。 单个产品表面通常通过路由器运行在多个模型层级上(廉价模型的快速路径、困难查询的高价模型兜底、合规要求的区域特定变体)。当某个切片的质量下降时,复盘需要知道受影响的请求落在了哪个切片中。“我们使用的是 Model X”这句话有两个错误:产品使用了多个模型,且受影响的用户昨天被路由到的模型可能与今天不同。

Judge 配置和 Eval 状态。 未能捕捉到退化的 Eval 套件本身也是一个系统,拥有自己的 Prompt、模型、评分标准和数据集版本。当事故暴露出 Eval 漏掉了故障时,你需要上次发布通过时的 Judge 配置快照 —— 而不是当前的 Judge 配置,因为后者可能已经在恐慌中被修改了。没有这个快照,“Eval 是在真实的损坏状态下通过的,还是 Eval 评分的对象根本就不对?”这个问题将无法回答。

检索索引状态和新鲜度延迟。 由 RAG 驱动的功能依赖于内容不断变化的索引:文档被添加、删除、重新嵌入、重新排序。复盘需要事故期间提供服务的索引版本(或 commit/快照 ID)、当时相对于事实来源的延迟,以及是否有任何部分索引重建正在进行中。“索引陈旧”是 SRE 模板视为基础设施故障的一种 Bug 类型;但在 LLM 系统中,陈旧 6 小时和陈旧 4 天会产生不同的行为,并对应不同的缓解后行动。

工具 Schema 版本和组合图。 调用外部工具的 Agent 依赖于工具目录的 Schema —— 每个工具接受什么参数、返回什么形状、携带什么权限范围。工具提供商会在不和你协调的情况下更改响应形状。一家供应商增加了一个可空字段、弃用了一个枚举值,或悄悄收紧了速率限制,都可能导致你的规划器(Planner)在两个请求之间从正常变为崩溃。复盘需要事故发生时生效的 Schema 版本,以及产生故障的具体组合(工具 A → 工具 B → 工具 C)。

流量组合和输入分布。 SRE 复盘很少记录输入分布,因为确定性系统并不以同样的方式依赖它。LLM 系统则不然:多意图查询的长尾峰值、营销邮件后冷缓存请求的异常激增、导致流量转向不同模型变体的地域转移 —— 这些都会引发看起来像模型退化但其实不是的行为事故。该字段需要事故窗口内的请求类别直方图,并与基准线进行对比。

必须增加的故障类型分类

SRE 模板通常会问“这是哪种类型的故障?”,并提供常见的选项:停机、延迟增加、数据损坏、安全事件、容量耗尽。这些都无法描述主导 LLM 生产故障的失效模式。复盘模板需要增加五种额外的故障类型,每种类型都有其专属的诊断清单。

静默质量退化 (Silent quality regression)。没有触发错误,没有违反 SLO,没有报警——但在数小时内,相当一部分用户得到的答案变差了。这是最难处理的一类,因为 SRE 监控流水线没有任何可以上报的指标。它通常通过用户投诉、支持工单激增或数天后才波及的下游产品指标暴露出来。复盘引导问题:“最早检测到这一情况的用户信号是什么?从发生到检测到之间有多长的延迟?”

评测模型导致的漏报 (Judge-induced false negative)。评测套件通过了,但模型依然上线了存在退化的版本。Bug 出在 Judge(评测模型)身上,而不是模型:Judge 的提示词没有惩罚某种失效模式;Judge 模型的偏差与候选模型的偏差一致(导致它给自信的错误答案打高分);或者评分标准已经过时。复盘引导问题:“批准该发布的评测是否运行了最新的 Judge 配置?Judge 上次根据人工标签进行校准是什么时候?”

检索过期故障 (Retrieval staleness incident)。索引提供的是一个已经偏离真实数据源的世界观,而模型的回答与其看到的过期视图是一致的。模型的表现是正确的,是索引欺骗了它。复盘引导问题:“故障期间的刷新延迟是多少?哪些源数据变更尚未反映出来?哪些查询落在了过期的分片上?”

工具结构漂移 (Tool-shape drift)。供应商更改了响应结构,内部工具增加了一个字段,或者某个工具已弃用但规划器 (planner) 仍在调用它。故障表现为格式错误的规划器输出、被拒绝的工具调用,或悄无声息地降级到备用路径。复盘引导问题:“故障期间生效的是哪些 Schema?在之前的 14 天内有哪些 Schema 变更上线(你自己的或供应商的)?Trace 中的哪些工具调用表现出了漂移特征?”

提示词发布偏差 (Prompt-rollout skew)。故障期间,两个地区、两个测试组或两个金丝雀桶运行了不同的提示词版本,且故障与其中一个桶相关。这类故障在多地区部署中尤为棘手,因为提示词变更的传播速度不同,而团队往往倾向于将“提示词”视为单一的产物。复盘引导问题:“故障期间各地区/各测试组的提示词版本分布是怎样的?故障率是否与特定的版本相关?”

如果团队的模板中没有这些分类,他们就会强行套用最接近的 SRE 分类,错误地标记故障。在一个季度的时间里,你会积累一个名为“其他质量问题”的垃圾筐,所有有趣的 AI 失效案例都会在那里无疾而终。

时间线重建需要回放,而不仅仅是日志

SRE 的时间线重建基于日志行:事件、部署标记、警报触发、运维操作。对于 LLM 故障,日志行是不够的——你需要能够 重新运行故障。如果无法针对候选修复方案回放损坏的状态,复盘中的“预防”部分就会退化为一厢情愿。

可回放的 Trace 存储是基础。每个请求都应该有一个 Span 树,其中包含渲染后的提示词、检索到的文本块、工具的输入输出、模型输出以及当时生效的所有版本 ID。Trace 必须保存足够长的时间,以便在复盘中发挥作用(通常需要数周,对于缓慢恶化的质量问题有时需要数月)。大多数团队在第一次重大故障中都会发现,他们的 Trace 存储保留期只有 24 小时,而 Bug 其实在 11 天前就开始了。

三个核心的可观测性原语:

  • 每个 Span 可见的提示词版本锁定。每个 Span 属性都应包含构成请求的提示词版本 ID,而不仅仅是一个高级的 “prompt vX.Y” 标签。当故障原因是“系统提示词中的这一段导致了退化”时,你需要将 那一段 的差异与 那个 Span 关联起来。
  • 与每次评测运行绑定的 Judge 配置快照。三周前的评测通过并不证明模型当时是好的,除非你还拥有当时评分的 Judge 状态。快照的成本很低,但在故障期间凭记忆重建已删除的 Judge 配置则代价巨大。
  • 检索 Span 上的索引版本戳。每个检索 Span 都携带它查询的索引提交号(或快照 ID)。当故障是检索过期时,你可以将错误答案锁定在精确的索引状态,并证明(或证伪)关于过期的假设。

这些遥测数据的成本是真实存在的,但也是有限的。而 没有 这些数据的代价是:故障以“调查中”结案,并在六周后相同模式再次出现时重新开启。

模板实际上应该是什么样子

一个实用的 LLM 复盘模板应该是对 SRE 框架的扩展,而不是替代。保留时间线、促成因素、缓解措施、预防措施和行动项。在顶部增加一个固定章节,名为“故障开始时的系统状态”,并为上述六个变量设置明确的字段。在下拉菜单中增加五种新的故障类型。在时间线重建下增加一个“回放”子章节,链接到保存的 Trace 包。

最重要的结构性决策是:尽可能让 AI 特有的字段成为 强制性且由自动化预填 的内容。复盘作者不应该需要记住去记录提示词版本;故障响应工具应该从 Trace 存储中提取它,并在创建故障文档时自动填入。那些作者永远想不到去询问的强制性字段,往往正是捕捉到隐形成因的关键。

这可以防止一种组织层面的失效模式:复盘作者可能既没有写提示词,也没有调优 Judge,不负责索引流水线,也没有部署工具变更,但却被要求解释发生了什么。如果没有模板的提示,他们会默认转向自己最熟悉的领域——通常是模型。于是复盘的“根因”就变成了“模型退化了”,这与经典 SRE 中没有实际查询计划就说“数据库慢了”一样,都是错误的结论。

架构核心要点

SRE 复盘报告中的“诱因分析 (Contributing factors)”部分是推动预防工作的杠杆:它指向了发生波动的变量,而行动项则针对该变量进行优化。当这一部分无法捕捉到系统中实际波动的变量时,预防工作就会找错目标——通常是一些通用的“完善监控”或“增加更多 eval 案例”之类的条目,而没人能验证这些措施是否真正弥补了漏洞。

复盘模板是一种“强制函数 (Forcing function)”。它告诉作者在宣布事件调查清楚之前,必须回答哪些问题。对于状态存在于代码和配置中的系统,传统的 SRE 模板能强制引导出正确的问题。但 LLM 系统的主体状态存在于提示词 (Prompts)、模型、评估器 (Judges)、索引、Schema 和流量中——除非模板明确要求回答这些问题,否则每一次复盘都会漏掉那些真正决定事件走向的变量,而下一次事故将与上一次如出一辙。

这项工作并不起眼。它只是一个单页的模板扩展、一些追踪属性 (Trace attributes)、一个用于评估器配置的快照任务,以及一种在撰写叙述部分 之前 先填写 AI 相关字段的纪律约束,以防止作者跳过它们。但这正是“可落地的复盘”与“只能存档的复盘”之间的区别。

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