AI 学习笔记(二十六):LLM 风险接受到期复盘与例外债清理最小实践

上一篇我们把紧急变更例外和风险接受流程补齐了。
但真到线上运转一段时间后,团队很容易碰到第二层麻烦:

例外当时批得很谨慎,过了几天却没人记得把它关掉。

常见症状很像:

  1. expires_at 写进单子里了,但到期前没人回头看
  2. 同一个高风险白名单续了三次,每次都说“再观察一周”
  3. 事故已经收尾,可临时放开的 tool 权限、prompt 绕路和 fallback 开关还挂在生产上

这时候问题已经不只是“有没有审批”,而是:

风险接受正在变成例外债(exception debt),而且会悄悄常驻系统。

这篇继续沿着生产运维主线往下走,聚焦一个更容易失控、却很少被单独设计的环节:

怎么把 risk acceptance 的到期复盘、续期判断和例外债清理做成可执行流程。

先说结论:

  1. expires_at 只是起点,不是治理本身
  2. 每一次续期都应该被视为新增风险,而不是自动延长
  3. 例外不关闭,系统就会慢慢回到“默认靠特批运行”的状态

1. 为什么有了 expires_at 还会失控

很多团队第一次做风险接受治理时,已经会要求填写:

  1. accepted_risk
  2. rollback_plan
  3. verification_gate
  4. expires_at

这当然比口头放行强很多。
可线上真正失控的地方,往往出现在到期之后。

因为系统不会自己变干净。

只要你把一个临时例外放进生产,后面就会自然冒出几件事:

  1. 依赖方开始默认这条例外会一直存在
  2. 新版本和新 runbook 会围着这条例外继续叠逻辑
  3. 值班同学为了稳定,倾向于“先别动这块”

于是 expires_at 如果只是个字段,就只会带来一种虚假的安全感:

看起来它是临时的,实际上没人负责让它结束。

所以更稳的思路不是“给例外加过期时间就够了”,而是:

  1. 到期前必须触发复盘
  2. 复盘必须产出明确去向
  3. 没有去向,就不能默许继续存在

2. 什么叫例外债,它和平常技术债不太一样

技术债通常来自历史实现、抽象不稳或交付速度压力。
例外债更麻烦,因为它带着明确的风险标签,却还在生产里继续生效。

一个例外开始转成债,通常会出现下面几类信号:

1) 续期次数开始增加

第一次续期可能是现实妥协。
第二次、第三次还在续,往往说明团队已经把“临时”当成常态。

2) 依赖面变大了

最初只是一个租户、一个队列、一个高风险工具白名单。
后面如果扩大到多个工作区、多类请求、更多角色,债务规模会迅速变大。

3) 原始触发条件已经消失

事故止血了、第三方抖动结束了、正式修复也上线了,但例外没撤。
这类债最危险,因为它没有了合理性,却还保留着副作用。

4) 团队已经说不清为什么它还在

当值班、平台、业务 owner 都只能回答“历史原因先留着”,基本就说明它已经失控。

也就是说,例外债不是抽象概念。
它就是那些明知高风险、原本说好临时、却在生产里常驻的特殊路径

3. 到期复盘别做成“问一句还要不要续”

很多团队的到期检查,最后会退化成群里一句:

  1. 这个例外还保留吗
  2. 没问题的话再延一周

这种做法不够。
因为它没有强迫团队回到证据。

更实用的最小复盘,建议固定成 4 个问题:

1) 当初接受的风险,现在还存在吗

如果外部波动已经消失、根因修复已经合入,那续期理由本身就可能失效。

2) 这条例外今天还在保护什么

要回答得具体:

  1. 哪条路由
  2. 哪类请求
  3. 哪个租户
  4. 哪个工具或权限

说不清保护对象,通常也说不清保留价值。

3) 它当前带来的副作用,是否已经大于收益

例如:

  1. 放开的 tool 权限让审计压力持续升高
  2. 临时 fallback 让成本长期偏高
  3. 特殊 prompt 绕路让行为分叉越来越多

4) 如果今天必须关掉它,团队是否知道怎么回退

真正健康的例外,不是“开着更安心”,而是“关掉也有清晰动作”。

4. 到期时只允许 4 种去向

复盘完成后,不要再给“先这样吧”留空间。
更稳的做法,是把去向收敛成 4 种明确结果:

1) close

条件:

  1. 风险触发条件已消失
  2. 正式修复已经上线
  3. 关闭后有验证路径

这是最理想的结果。
也是默认优先级最高的结果。

2) renew

条件:

  1. 风险仍然存在
  2. 暂时没有更低风险替代方案
  3. 本次续期有新的截止时间和新的证据

重点是:

续期不是复制上一张单子。

每次续期都应该重新写:

  1. 当前还接受什么风险
  2. 为什么还不能关
  3. 下个到期点前要完成什么动作

3) downgrade

条件:

  1. 例外还不能完全关闭
  2. 但可以缩小作用域、流量或权限

例如:

  1. 从全量白名单缩到单租户
  2. 从自动写操作缩到人工审批后执行
  3. 从主路由 fallback 缩到离线任务队列

这类动作特别适合把“大例外”先降成“小例外”。

4) formalize

条件:

  1. 这条“临时例外”其实已经证明自己是长期刚需
  2. 继续靠特批维持反而更危险

这时候该做的不是一直续期,而是把它转成正式能力:

  1. 写进默认策略
  2. 补权限和审计设计
  3. 接进发布闸门和回滚流程

如果一条例外连续续期很多次,最常见的真实答案往往不是 renew,而是该进入 formalize

5. 例外债台账至少保留这 9 个字段

想把例外债真正管起来,就别只看审批单。
建议单独维护一个 exception debt register,最少保留这 9 个字段:

  1. exception_id
  2. linked_incident_or_change
  3. scope
  4. accepted_risk
  5. owner
  6. expires_at
  7. renew_count
  8. last_review_decision
  9. cleanup_status

如果还能再多加两项,我建议补:

  1. exit_criteria
  2. verification_evidence

这样做的价值不在“表格更完整”,而在于它把例外从一次性审批动作,变成了可持续追踪对象。

尤其是 renew_count 很重要。
因为它能快速帮你识别哪些例外已经开始积债。

6. 一个够小但真能执行的服务层示例

下面这段逻辑只做三件事:

  1. 到期前触发复盘
  2. 给高续期、高影响例外打分
  3. 限制例外的可选去向
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
function shouldTriggerReview(exceptionItem, now = Date.now()) {
const reviewLeadMs = 72 * 60 * 60 * 1000;
const expiresAt = new Date(exceptionItem.expiresAt).getTime();
return expiresAt - now <= reviewLeadMs;
}

function computeExceptionDebtScore(exceptionItem) {
let score = 0;

score += Math.min(exceptionItem.renewCount || 0, 3) * 2;
score += exceptionItem.scope === "global" ? 3 : 1;
score += exceptionItem.hasWriteSideEffects ? 3 : 0;
score += exceptionItem.hasDedicatedExitCriteria ? 0 : 2;

return score;
}

function decideDisposition({ riskStillExists, hasSaferAlternative, canReduceScope, debtScore }) {
if (!riskStillExists) {
return "close";
}

if (canReduceScope) {
return "downgrade";
}

if (!hasSaferAlternative && debtScore < 6) {
return "renew";
}

return "formalize";
}

这里故意没有写得很复杂。
因为真正关键的是几条约束:

  1. 到期前 72 小时就得触发复盘,不是过期以后再补
  2. 续期次数、全局作用域、写副作用会拉高债务分
  3. 债务高到一定程度后,默认就不该再轻易续期

7. 怎么把它接进前面的冻结、发布和复盘链路

如果例外债只存在于运维表格里,很快又会脱离主流程。
更稳的做法,是把它接回你前面已经搭好的治理链路:

1) 接进 change freeze / fix-only

当系统处于 guardedfix-onlyfreeze 时,禁止新增“高债务例外”的自动续期。

2) 接进发布闸门

如果一个高风险例外已经超过到期时间,或者债务分过高,就阻断高风险发布。

3) 接进 incident closeout

事故关闭前,必须说明相关例外是:

  1. 已关闭
  2. 已续期
  3. 已缩范围
  4. 已转正式方案

4) 接进 postmortem action

如果一条例外连续续期,应该新增 action item,明确谁来拆掉它,而不是让它在系统里悬着。

这样一来,例外债就不再是“历史遗留问题”,而会进入你每天都在跑的工程节奏。

8. 一周落地建议:先把“续期”变成需要证明的动作

如果你想一周内把这件事做起来,我建议按这个顺序走:

  1. Day 1
    固定复盘模板,明确到期时只能得到 close / renew / downgrade / formalize
  2. Day 2
    建一个最小 exception debt register,把现有例外录进去
  3. Day 3
    在发布入口或值班系统里加 T-72h 到期提醒
  4. Day 4
    给高风险例外加 renew_count 和债务分规则
  5. Day 5
    用一次真实例外演练:到期前提醒、证据复盘、缩范围或关闭是否真能跑通

别一开始就想做一整套复杂平台。
先把“续期必须重新举证”这件事做硬,治理质量就会明显提高。

总结

风险接受最怕的,不是当时批错,而是:

原本只该活三天的例外,最后活成了系统默认设置。

如果你这周只能先做一件事,我建议优先完成:

给每条例外增加到期复盘和 renew_count,并把高续期例外强制纳入清理清单。

这样 expires_at 才不是一个好看的字段,而是真能把临时风险收回去的工程约束。

参考资料

本文永久链接: https://www.mulianju.com/learning-notes/ai-learning-notes-llm-risk-acceptance-expiration-review-exception-debt-cleanup/