AI 学习笔记(二十四):LLM 变更冻结与风险窗口治理最小实践

上一篇我们把事故证据留存和 postmortem action tracking 补齐了。
但很多团队真正反复踩坑的地方,不是“事故后怎么复盘”,而是:

系统已经开始变脆了,团队却还在按平时节奏继续发。

于是经常出现两种极端:

  1. 出事以后才临时喊“先别动了”,但冻结范围、例外条件、恢复标准都不清楚
  2. 明明 error budget 快见底、业务高峰快到了,还是继续上线新模型、新 prompt、新工具权限

这篇继续生产运维主线,聚焦一个经常被提到、却很少真正做成规则的主题:
change freeze 和 risk window governance。

先说结论:

  1. 变更冻结不是“完全不发版”,而是把系统切到更保守的运行模式
  2. 风险窗口不只是节假日日历,还包括稳定性预算、事故状态、外部依赖波动和业务关键时段
  3. 更稳的做法不是一刀切,而是按变更类型和风险等级决定什么能发、什么只能灰度、什么必须冻结

1. 为什么 LLM 系统更需要变更冻结和风险窗口

传统应用在高风险时段,常见做法是减少数据库变更、减少核心链路发布。
LLM 系统里要防的东西更多,因为一次“变更”可能不是代码包,而是下面这些内容:

  1. model / provider route
  2. prompt_version
  3. schema / tool contract
  4. retrieval index / rerank strategy
  5. tool permission profile

这些东西很多都能快速改、快速生效。
优点是止血快,缺点是如果没有治理规则,系统在脆弱时段会被连续叠加风险。

所以 change freeze 要解决的,不是“让团队慢下来”,而是:

  1. 当系统不稳时,明确哪些变更必须停
  2. 当业务进入关键窗口时,明确哪些变更只能低风险放行
  3. 当必须做紧急修复时,明确谁审批、怎么回滚、留什么证据

2. 先把运行模式定清楚:不要只分“正常 / 冻结”

很多团队只在口头上有两种状态:

  1. 正常发布
  2. 全部冻结

这种划分太粗,落地时通常会失效。
更实用的最小治理方式,是定义 4 种模式:

1) normal

常规变更可发布,但仍需经过发布闸门、Canary 和自动回滚。

2) guarded

只允许低到中风险变更,要求更小流量灰度、更高审批级别、更严格观察窗口。

3) fix-only

只允许稳定性修复、回滚、安全收敛和只读化变更;禁止新模型、新 prompt 大改、新工具权限扩张。

4) freeze

除紧急止血和回退外,暂停所有生产变更。

这样分层的价值在于:

  1. 不会因为一点抖动就进入“全面停摆”
  2. 也不会因为“还没严重到全冻结”就继续冒险上新

3. 不是所有变更都该被同样对待

如果冻结策略不区分变更类型,最后通常会变成两个坏结果:

  1. 真正高风险变更没被挡住
  2. 低风险修复也被一起卡死

建议先把 LLM 变更粗分成 5 类:

1) 低风险变更

例如:

  1. dashboard
  2. 低敏告警阈值
  3. 文档和 runbook

2) 中风险变更

例如:

  1. 观测字段补充
  2. fallback 权重微调
  3. retrieval 参数小幅调整

3) 高风险变更

例如:

  1. 主模型切换
  2. prompt 大版本升级
  3. schema 结构变更
  4. rerank 或 chunking 策略重做

4) 高副作用变更

例如:

  1. 写工单
  2. 发消息
  3. 改数据库
  4. 调用外部审批或资金相关接口

5) 紧急止血变更

例如:

  1. 切回稳定模型
  2. 关闭高风险工具
  3. 切只读
  4. 回退 prompt / policy / route

也就是说:

freeze 治理的重点不是“有没有改”,而是“改了什么、影响多大、有没有副作用”。

4. 哪些信号应该触发风险窗口升级

很多团队只有“老板说先别发了”这种人工冻结。
更稳的方式,是把下面几类信号接进规则:

1) 稳定性预算逼近红线

比如:

  1. service budget left < 20%
  2. quality budget left < 20%
  3. 高风险写操作事故数大于 0

这类场景下,至少应该进入 fix-only

2) 重大事故尚未真正收尾

如果 P1 / P2 incident 还在处理中,或者 postmortem 高优 action 尚未落实,继续发高风险变更通常只会把问题复杂化。

3) 业务高价值窗口

例如:

  1. 大促
  2. 账单结算
  3. 月末报表
  4. 面向大客户的关键演示窗口

这类时间段不一定要全冻结,但至少要进入 guarded,并把高风险变更挪出窗口。

4) 外部依赖正在波动

例如:

  1. 上游 provider 刚大版本切换
  2. 向量库或工具平台刚升级
  3. 第三方接口 SLA 正在下降

这类时候即使你自己没改代码,系统整体风险也在上升。

5. 一份够用的最小策略配置

先别上复杂平台。
很多团队一开始只要有一份结构化策略,就能明显降低混乱。

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
33
34
35
36
37
38
39
40
41
42
risk_window_policy:
modes:
normal:
allow:
- model_minor_tuning
- prompt_patch
- retrieval_tuning
require_approval: false
require_canary: true
guarded:
allow:
- prompt_patch
- observability_change
- rollback_preparation
require_approval: true
require_canary: true
observe_minutes: 60
fix_only:
allow:
- rollback
- safety_policy_hardening
- readonly_mode
- incident_hotfix
block:
- model_upgrade
- prompt_major_release
- tool_permission_expand
freeze:
allow:
- rollback
- kill_switch
block_all_other_changes: true
triggers:
guarded_if:
business_event_window: true
provider_degraded: true
fix_only_if:
error_budget_left_ratio_lt: 0.2
active_p1_incident: true
freeze_if:
risky_write_incident_gt: 0
repeated_rollback_within_24h: true

关键不是字段设计得多漂亮,而是:

  1. 发布器能读
  2. 值班同学能懂
  3. 事后能回溯为什么那天进入了 guardedfreeze

6. 把决策接进流水线,而不是停留在群消息里

真正容易失控的,不是策略不存在,而是策略只存在于聊天记录。

最小实践里,建议把发布入口前置一个风险判断:

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
33
34
35
36
37
38
export function decideRiskWindow(input) {
const {
activeP1Incident,
riskyWriteIncidents,
errorBudgetLeftRatio,
businessEventWindow,
providerDegraded,
changeType,
} = input;

if (riskyWriteIncidents > 0) {
return { mode: "freeze", reason: "risky-write-incident" };
}

if (activeP1Incident || errorBudgetLeftRatio < 0.2) {
return { mode: "fix-only", reason: "stability-critical" };
}

if (businessEventWindow || providerDegraded) {
return { mode: "guarded", reason: "high-risk-window" };
}

return { mode: "normal", reason: "steady-state" };
}

export function canRelease(changeType, mode) {
const blockedInFixOnly = ["model_upgrade", "prompt_major_release", "tool_permission_expand"];

if (mode === "freeze") {
return changeType === "rollback" || changeType === "kill_switch";
}

if (mode === "fix-only") {
return !blockedInFixOnly.includes(changeType);
}

return true;
}

这段逻辑的作用很简单:

  1. 先判断当前处于什么风险窗口
  2. 再判断这次变更是否允许进入生产

只要这一步自动化起来,团队就不再需要临场争论“这次到底算不算能发”。

7. 紧急例外不能靠口头放行

freeze 最大的误区之一,是一边说“冻结”,一边在群里不断加例外。
最后冻结名义还在,约束已经失效。

更稳的做法是给紧急例外固定 4 个要求:

  1. 必须关联 incident 或明确风险接受记录
  2. 必须说明为什么不改会更危险
  3. 必须带回滚路径和验证步骤
  4. 必须留下审批人与执行时间线

对于高风险工具、写操作和权限扩张类变更,例外要求应该更严格。
否则 freeze 反而会变成“高压时段偷偷发版”的遮羞布。

8. 怎么把它和前面的发布闸门、值班、postmortem 串起来

这套治理不要单独存在。
更实用的接法是:

  1. SLO / error budget
    决定什么时候从 normal 升到 guardedfix-only
  2. release gate / canary / rollback
    决定允许的变更进入后,怎么安全放量
  3. on-call runbook
    决定进入 freeze 时谁来执行止血动作
  4. postmortem actions
    决定什么时候可以从 fix-only / freeze 恢复到正常模式

也就是说:

change freeze 不是一个孤立制度,而是稳定性治理链路里的“节流阀”。

9. 一周落地建议:先把冻结规则做小、做硬

如果你想一周内落地,我建议按这个顺序推进:

  1. Day 1
    定义 4 种模式:normal / guarded / fix-only / freeze
  2. Day 2
    梳理 8~12 类常见变更,并标注风险等级
  3. Day 3
    把 error budget、active incident、业务窗口接进发布判断
  4. Day 4
    给紧急例外补齐审批、回滚和验证模板
  5. Day 5
    用一次演练验证:预算不足时是否真的会自动收紧发布

不要一开始就追求全公司统一平台。
先把核心链路的冻结规则跑通,比设计一套很宏大的制度更有价值。

总结

真正有用的 change freeze,不是“大家先别动”,而是:

  1. 系统知道什么时候该进入保守模式
  2. 团队知道哪类变更还能发、哪类必须停
  3. 紧急例外有审批、有回滚、有证据

如果你这周只能先做一件事,我最建议的是:

把 error budget、active incident 和业务关键窗口接进发布入口,让风险窗口自动决定变更策略。

这样 freeze 才不会停留在口头共识,而会变成真正可执行的工程规则。

参考资料

本文永久链接: https://www.mulianju.com/learning-notes/ai-learning-notes-llm-change-freeze-risk-window-governance-minimal-practice/