阅读材料: 应用部分 10. 防范古德哈特定律对指标的损害:守护指标与应急模式

模块「应用部分 10. 防范古德哈特定律对指标的损害:守护指标与应急模式」中第 1 / 5 节课
您正在未登录状态下查看课程。 请登录,以保存进度并参加测试。

应用部分 10. 保护指标免受古德哈特效应:守护指标与紧急模式

状态:建议。 用配对守护指标(guard 指标)和阻塞性紧急模式保护 KPI 是 Google SRE Book 中描述的成熟实践。具体阈值(silent_p0manual_review_flooraudit_trace_coverage)和 validation.md v1.1 格式是建议性框架,大多数团队都会对其进行适配。

对于学习路径而言,只需运行 examples/goodhart-validator/ 并观察良好的 MTTR 如何被 silent_p0 的增长所阻塞即可。指标网络、trace 字段和阈值校准属于完整生产轨道。如果下方出现名称「红色按钮」,请将其视为正式紧急模式的简短标签。

第一卷的第 9 部分中,一项检查对应一个指标已经足够:「发布后反馈可见」「余额不会为负」。在生产场景中,对 cdn_error_budget_burn 使用相同逻辑已显不足。来自第 11 部分的代理与非缺陷日志仪表板在发布后呈现出矛盾的画面,而单一指标成为了陷阱。在本节中,我们将其扩展为配对守护指标网络——即「KPI + 备份指标」组合,其中第二个指标可防止为优化第一个指标而造成隐性损害。防范此类典型操纵的目录在第 20 部分 SDD 反模式中进行了系统化整理。

阅读前

  • 来自第一卷的依据:第 9 部分教导验证事实而非令人信服的散文;第 20 部分展示了流程如何开始保护错误目标。
  • 本地学习案例:cdn_error_budget_burn,因为改进的 MTTR 可以被 silent_p0 的增长所阻塞。
  • capstone/ 中的足迹:一个目标指标、一个 guard 指标以及一个为 high_memory_usage 阻塞的示例。
  • 第一遍的关键术语:guard 指标和紧急模式(「红色按钮」)。其余的——silent_p0manual_review_flooraudit_trace_coverageedge_drift、trace 字段、指标网络——仅供参考,仅当 capstone/goodhart-note.md 的一行需要时再查阅。
  • 可推迟的内容:指标网络、trace 字段、drift 校准和完整的紧急模式。

目标

到本节结束时,您将构建一个 validation.md,它能预先捕捉古德哈特陷阱,并防止 LLM 事件管道以牺牲 triage 退化为代价来改进报告 KPI。

主要收益是:将指标分为可管理目标和不可侵犯的质量不变量。然后为它们设置可验证的阈值、trace 中的证据以及 CI 中的阻塞。

此处「诱饵指标」是指作为信号有用、但在与质量不变量分开进行优化时变得危险的 KPI。KPI(关键绩效指标)是团队希望通过发布改进的关键指标。

这种方法延续了 SDD 循环:规范、检查标准和迭代在实施变更前固定,而不是在得到漂亮结果后调整(GitHub Spec Kit Quickstart)。

「当度量变成目标时,它就不再是一个好的度量」这一效应被经典地称为古德哈特定律(Wikipedia: Goodhart's law)。Google SRE 对 SLO 的定义直接基于这种谨慎(SRE Book: Service Level Objectives)。

最小学习场景

学习案例

来自book/part-11-second-feature-phase.md的学习代理日志中呈现的生产事件 cdn_error_budget_burn。一次发布将 MTTR 从 660s 改进到 290s,形式上看是成功。但 silent_p0 从 0.02 跃升至 0.18,manual_review_rate 从 0.18 下降到 0.12。目标是看到 CI 网关如何捕捉到此变化并阻塞合并,尽管 MTTR 显示「绿色」。

准备

  • book2/examples/goodhart-validator/specs/validation.yaml — 不变量和红色按钮检查。
  • book2/examples/goodhart-validator/fixtures/baseline_metrics.json — 基准(MTTR 660s,silent_p0 0.02)。
  • book2/examples/goodhart-validator/fixtures/new_metrics_good.json — 无盲点的改进。
  • book2/examples/goodhart-validator/fixtures/new_metrics_bad.json — 「MTTR 盲点」(290s,silent_p0 0.18)。
  • book2/examples/goodhart-validator/fixtures/new_metrics_drift.json — 边相关性上的漂移。
  • book2/examples/goodhart-validator/scripts/run_validation.pycompare_drift.pyci_gate.py

步骤

  1. cd book2/examples/goodhart-validator预期:您位于示例目录中,没有额外依赖。
  2. 「好」运行:python3 scripts/run_validation.py --validation specs/validation.yaml --metrics fixtures/new_metrics_good.json。*预期:返回码 0,状态 PASS,所有三个不变量 OK。*
  3. 「MTTR 盲点」运行:python3 scripts/run_validation.py --validation specs/validation.yaml --metrics fixtures/new_metrics_bad.json。*预期:返回码 1,red_button_mttr_blindness 触发,manual_review_floorsilent_p0_cap 标记为 FAIL。*

差: 仅看 MTTR——发布更快,似乎「更好」。

好: 使用不变量运行验证——在 silent_p0=0.18 时的「更快」会被自动阻塞。

  1. 针对 drift 装置运行漂移比较:python3 scripts/compare_drift.py --baseline fixtures/baseline_metrics.json --new fixtures/new_metrics_drift.json。*预期:edge_drift > 0.12,返回码 1。*
  2. 对照:相同的 compare_drift.py 对好指标运行。*预期:edge_drift <= 0.12,返回码 0。*
  3. 完整 CI 网关:python3 scripts/ci_gate.py --validation specs/validation.yaml --baseline fixtures/baseline_metrics.json --new fixtures/new_metrics_bad.json。*预期:返回码 1,reasons 中列出具体违反的不变量,而非通用的 FAIL。*
  4. 将运行结果记录为简短的 anti-Goodhart 注释:目标指标得到改进,但 silent_p0_capmanual_review_floor 阻塞了发布。预期:下一次具有更快 MTTR 的 PR,验证器不是「绿色对比旧基准」而是与 good/bad/drift 装置进行比较。

如果您已安装 Qwen Code 并需要供审查使用的解释,请执行一个单独的可选步骤:

qwen -p "读取 @fixtures/new_metrics_bad.json 和 @specs/validation.yaml。即使 MTTR=290s,哪些不变量不能绕过?不要修改文件。" --approval-mode plan

此类输出作为说明有用,但不能替代 run_validation.pycompare_drift.pyci_gate.py

检验事实

步骤 2 返回码为 0,步骤 3 和 4 返回码为 1,并具体指出违反的不变量。步骤 6 在复合网关中显示相同行为。如果 CI 网关放行 new_metrics_bad.json,则验证器配置已被削弱——silent_p0_capmanual_review_floor 阈值已被移动。

它如何进入 capstone/

将一个目标指标、一个 guard 指标和一个被阻塞的示例转移到 capstone/goodhart-note.md。如果主要计分案例是 high_memory_usage,请将此运行记录为同一回路的 anti-Goodhart 风险:不能以 silent_p0、手动审计或 5xx 为代价改进 memory 或 MTTR。如果指标网络未被重新计算,请不要全部转移;对于学习最小值,只需展示改进的 KPI 在没有保护性不变量的情况下无法通过。

最小片段:

target_metric: "MTTR <= 5m"
guard_metric: "silent_p0 <= 0.05 and manual_review_rate >= 0.15"
blocked_example: "new_metrics_bad.json"
reason: "MTTR improved, but silent_p0 and manual_review_floor fail"

可审查的足迹

脚本 run_validation.pycompare_drift.pyci_gate.py 将结果写入 stdout,不创建单独的 out/ 目录。对于学习路径,请将结果转移到 capstone/goodhart-note.md:目标指标、guard 指标、被阻塞的示例和原因。

如果您在项目中保存 outputs/goodhart.last-run.txt,它应该是审查的可读附件,而非空标记。在 SDD 中,事实是可复现的命令或可读工件,而非提交本身的存在。

关键思想

首先确定哪些指标保持为质量不变量,哪些成为优化目标并因此容易被操纵。不变量不能通过直接施压来「改进」:它描述了系统最低可接受状态。不变量示例:

  • 审计完整性;
  • 手动检查比例;
  • silent_p0 上限(即未经升级而关闭的「静默」严重事件的比率)。

优化目标则相反,可以降低或提高,但只能在保护走廊内进行。MTTR 作为恢复速度指标有用,但作为模型或团队的唯一奖励是危险的。

validation.md 中明确这一区别。MTTR<=5m 可以是目标。而 manual_review_rate>=15%silent_p0<=5%audit_trace_coverage==100% 保留为入场条件。

差:

> 达到 MTTR 低于 5 分钟。

问题:没有守护指标的裸目标,直接通向 silent_p0

好:

> MTTR <= 5m AND silent_p0 <= 5% AND manual_review_rate >= 15% AND audit_trace_coverage == 100% — 违反任何条件 = CI_BLOCK

当指标成为现实的替代品时,古德哈特陷阱就会出现。系统开始优化度量方式而非 triage 质量。如果 MTTR 被孤立检查,模型会学会更快关闭事件、减少升级比例并避免冗长调查——这正是拖累平均恢复时间的原因。

在图表上,这看起来像胜利:MTTR 降至 5 分钟或更低。但在操作回路中,这可能意味着相反。复杂的 P0 并没有消失,而是变得不可见,因为它们被错误地归类为误报、低紧急性或「自愈」事件。

「MTTR 5 分钟」陷阱对罕见严重事件尤其危险,因为关闭速度与调查完整性相互竞争。用数字看是这样的:

  • 重放中 300 个事件的基准:MTTR 11:00,升级比例 14%,silent_p0 2%;
  • 新优化版本:MTTR 4:50,升级 6%,silent_p0 18%。

形式上 KPI 有所改进。但系统开始更频繁地遗漏关键事件,不进行手动检查和升级。阻塞此类发布:它将风险从可见报告转移到未来的重复事件、事后分析回归和责任链丢失。

validation.md 中的抗体是防止优化重新定义质量含义的形式条件。最小集是三条规则,必须同时检查:

规则保护内容边界
manual_review_floor手动验证决策的比例不低于 15%

| silent_p0_cap | 未经升级关闭的「静默」P0 比例 | 不高于 5% | | audit_trace_required | 决策足迹的完整性(prompt、diff、来源) | 100%,无例外 |

单独来看,这些规则会留下漏洞。高可追溯性无法补偿 silent_p0 的增长。如果无法恢复提示、差异和决策来源,则手动检查无用。配置「红色按钮」使其不是对单一不良数字触发,而是对保护回路的违反触发。

选择什么作为目标,什么作为保护

并非所有 KPI 都需要相同的保护。手动 triage 操作和自动修复具有不同的风险水平,因此最小不变量集也不同。核心规则是:操作越危险,与目标 KPI 配对的守护指标就越多。

决策类型改进内容必须配对的内容
手动 triage 操作MTTR决策足迹已完整保存
无操作的自动分类分类的速度和准确性无静默 P0;决策足迹已保存
自动升级升级延迟无静默 P0;无错误升级

| 无状态的自动修复 | MTTR | 无静默 P0;有手动检查;完整审计足迹 | | 有状态的自动修复(DB、缓存) | MTTR | 加上经确认的备份 | | 新策略发布 | 重放上的准确性 | 无「边界漂移」;完整审计足迹 |

完整的英文指标名称(silent_p0manual_review_flooraudit_trace_coveragefalse_escalation_rateedge_driftpostmortem_gapbackup_verified)及其阈值和公式已移至附录 D。此处重要的是规则而非名称表:「改进什么」的每一行必须有一到两个来自同一风险领域的守护指标。

对于危险操作(最后三行),还应包括「红色按钮」——一个阻塞网关,没有第 3 章的评审团无法绕过。对于手动和观察性操作(前三行),软警告已足够。

该表的目的不是将其变成教条,而是帮助您看到遗漏的内容。如果在「有状态的自动修复」行中没有备份检查,这是重写 validation.md 的信号,而非「优化 MTTR」的信号。

> [conceptual interface]validation.md 的结构,请根据您的足迹文件进行调整。

#### validation.md v1.1 的最小结构
version: 1.1
invariants:
  - name: manual_review_floor
    expression: "manual_review_rate >= 0.15"
  - name: silent_p0_cap
    expression: "silent_p0 <= 0.05"
  - name: audit_trace_required
    expression: "audit_trace_coverage == 1.0"

checks:
  - name: red_button_mttr_blindness
    when: "MTTR <= 5m"
    assert: "manual_review_rate >= 0.15 and silent_p0 <= 0.05 and audit_trace_coverage == 1.0"
    fail: "CI_BLOCK"

带有 artifact_inputsnetwork_consistency 和通过 COUNT(events_with(...)) 表达 audit_trace_required 的精确表达的完整形式位于 [examples/goodhart-validator/specs/validation.yaml](examples/goodhart-validator/specs/validation.yaml)。

下一层保护是规范中直接检测隐藏偏差。将 triage 行为变化视为 KPI 不变时的回归。此处回归是指聚合中不可见的决策分布变化。

原因:危害并不总是出现在顶层数字中。MTTR 可能保持不变,升级比例可能看起来正常,但模型开始在 auto_closemanual_reviewdefer 之间以不同方式分配边缘案例。

因此在 validation.md 中,不仅比较聚合,还要比较行为模式:

  • severity 转移矩阵;
  • 关闭原因分布;
  • 重新打开的事件比例;
  • 事后标记的延迟;
  • manual_review_ratesilent_p0 之间关系的变化。

如果 drift_budget(与基准的可允许偏差走廊)超出,则即使 KPI 显示「绿色」也要阻塞构建。这意味着系统已经改变了决策模式。

要看到主要陷阱,三个指标和一个守护就足够了:

flowchart LR
    MTTR[MTTR]
    silent_p0[silent_p0]
    manual_review_rate[manual_review_rate]
    audit_trace_coverage[audit_trace_coverage]
    silent_p0 -->|不诚实地拉低 MTTR| MTTR
    manual_review_rate -->|诚实地拉高 MTTR| MTTR
    audit_trace_coverage -->|限制 silent_p0| silent_p0

读法:如果允许「静默」P0 未经升级关闭,可以人为改进 MTTR。守护 audit_trace_coverage 禁止无足迹关闭,manual_review_rate 保持手动检查比例。包含其他指标(escalation_ratepostmortem_regression)的完整图景在附录 D 中;那里还有形式阈值和关系。

将检查绑定到 Qwen 日志、决策和差异链——否则无法在生产中部署而不会丢失上下文。每事件的最小足迹组成:trace_id(链)、prompt_hash(提示哈希)、decision(选择内容)、policy_version + diff_id(哪个版本及哪个变更引入)以及 postmortem_label(复盘确认的内容)。带有 agentraw_alert_excerptreasoning_deltareview_outcome 的完整字段集属于完整轨道,并在 [examples/templates/validation.md](examples/templates/validation.md) 中收集。

这五个字段允许在阻塞后回答工程问题:规范的哪个版本改变了行为,哪个提示促使模型自动关闭,哪个差异引入了新启发式。没有此链接,validation.md 仍是声明;有了它,它成为可复现的审计工件。

将指标设计为依赖网络而非独立计数器集合。这正是 network_consistency:一个指标的变化不应与相关指标相矛盾。共同重新计算 MTTR、silent_p0manual_review_rateescalation_ratepostmortem_regressionrollback_rateaudit_gap(见上图)。一个数字的局部改进通常在另一个数字上产生债务。实际标准是边一致性:如果 MTTR 下降,但同时手动检查下降且延迟确认的 P0 比例上升,则将系统标记为有风险。这将 CI 从「通过/不通过 KPI」检查转变为 triage 行为稳健性检查。

> [conceptual interface]scripts/metrics/network_recompute.py 显示指标网络局部重新计算的形式;教材仓库中没有现成的 CLI。具有 anti-Goodhart 检查的 CI 网关的可运行模拟是 python3 examples/goodhart-validator/scripts/run_validation.pyci_gate.py(见下方「检验事实」)。

#### spec 变更后更新指标网络和验证 CI-gate
python3 scripts/metrics/network_recompute.py \
  --spec specs/incident-spec.md \
  --replay data/replay_*.jsonl \
  --out .artifacts/metric_network.json

python3 scripts/metrics/ci_gate.py \
  --artifact validation.md \
  --metric-network .artifacts/metric_network.json \
  --traces .artifacts/qwen_trace.ndjson

CONTROL: CI_GATE = PASS if (edge_drift <= 0.12 && silent_p0 <= 0.05 && manual_review_rate >= 0.15 && audit_trace_coverage == 1.0) else CI_BLOCK

完整轨道:阈值校准

silent_p0manual_review_rateedge_driftaudit_trace_coverage 的「低/默认/高」表、同时「危险」削弱两个保护的练习以及完整指标依赖网络位于附录 D,第 D.4 节。在第一遍中,只需看到糟糕的发布被 guard 指标阻塞即可。

示例与应用

示例:团队希望证明新的 triage 管道已准备好进行更激进的自动关闭。首先使用目标优化 MTTR<=5m 运行重放。然后通过 red_button_mttr_blindness 检查相同的事件集。

如果结果看起来像 MTTR=4:50silent_p0=18%manual_review_rate=12%,则阻塞发布。原因不是糟糕的速度,而是保护不变量的违反。这是一个重要区别:目标已达成,但质量合同已被破坏。

> [conceptual interface]scripts/metrics/simulate.pyvalidate_red_button.py 显示紧急模式检查的形式;教材仓库中没有现成的 CLI。在学习装置上检查相同不变量的可运行模拟是 python3 examples/goodhart-validator/scripts/run_validation.py(参见 examples/goodhart-validator/README.md)。

#### 在重放上运行红色按钮的示例
python3 scripts/metrics/simulate.py \
  --scenario data/replay_300.jsonl \
  --goal "MTTR<=5m" \
  --spec specs/incident-spec.md

python3 scripts/metrics/validate_red_button.py \
  --artifact validation.md \
  --mode red_button \
  --assert "silent_p0<=5% && manual_review_rate>=15% && audit_trace_coverage==1.0"

CONTROL: red_button = BLOCKED (MTTR=4:50, silent_p0=18%, manual_review_rate=12%)

第二个示例——错误地将 40 个 P0 作为「误报」自动关闭。在事后分析之前,指标看起来很干净:事件快速关闭,升级减少,队列不增长。

与标签核对后发现另一情况。五个事件是真实的严重故障。它们本应增加 silent_p0escalation_regretpostmortem_regression

validation.md 中将这种情况记录为 triage 的预测性失败。不要等到生产中的用户损害。使用重放和事后真相作为阻塞的早期信号。

实践中将 validation.md 存储在规范旁边,并且仅通过与 triage 规则相同的审查流程进行更新。每次变更的 CI 都会重建指标网络、运行重放、检查足迹完整性并比较行为与基准。阈值变更——例如将允许的 silent_p0 从 5% 提升到 7%——应通过第 3 部分中的 mutable 规则进行,作为风险合同变更,而非 YAML 的技术性编辑。此屏障保护系统免受在便利报告压力下逐渐侵蚀不变量。

总结

诱饵指标的危险不在于它们是假的。它们在成为唯一优化目标之前是有用的。

可靠的 validation.md 解决五项任务:

  • 将目标与不变量分开;
  • silent_p0 增长时阻塞 MTTR 改进;
  • 要求最小手动验证;
  • 检查 triage 行为漂移;
  • 保留 Qwen 日志、决策和差异的证据链。

> [runnable] — 本章的最小冒烟运行位于 [examples/goodhart-validator/](examples/goodhart-validator/README.md)。

cd book2/examples/goodhart-validator
python3 scripts/run_validation.py \
  --validation specs/validation.yaml \
  --metrics fixtures/new_metrics_good.json

python3 scripts/ci_gate.py \
  --validation specs/validation.yaml \
  --baseline fixtures/baseline_metrics.json \
  --new fixtures/new_metrics_good.json

红色按钮场景的预期失败示例使用 fixtures/new_metrics_bad.jsonrun_validation.pyci_gate.py 应以返回码 1 结束,因为 manual_review_floorsilent_p0_cap 被违反。

在下一章中,此保护回路将连接到实际监控和部署 API。

工件与准备标准

工件准备完成条件
book2/examples/goodhart-validator 的本地运行优化目标与不可侵犯不变量分离

| 三个装置:good / bad / drift | new_metrics_good.json 通过,new_metrics_bad.json 因具体原因被阻塞,new_metrics_drift.jsoncompare_drift.py 阻塞 | | 由于守护指标导致的阻塞示例 | MTTR 改进,但发布因 silent_p0manual_review_floor 被阻塞 | | capstone/goodhart-note.md 中的记录 | 三行:目标指标、配对 guard 指标、阻塞条件 |

完整轨道添加了带有目标指标和质量不变量的 validation.md.artifacts/metric_network.json 或可运行的指标网络模拟、重放装置以及带有 edge_driftsilent_p0audit_trace_coverage 的 CI 网关报告。在以下情况下视为准备完成:紧急模式在 silent_p0 增长时阻塞 MTTR<5m;足迹字段链接提示、差异、决策和事后标签;阈值变更形式化为风险合同变更而非 YAML 装饰。

实践

  1. cd book2/examples/goodhart-validator && python3 scripts/run_validation.py --validation specs/validation.yaml --metrics fixtures/new_metrics_good.json --json — *预期:码 0,JSON 中 "status": "PASS",所有三个不变量 manual_review_floorsilent_p0_capaudit_trace_required 具有 "ok": true。*
  2. python3 scripts/run_validation.py --validation specs/validation.yaml --metrics fixtures/new_metrics_bad.json --json — *预期:码 1,JSON 中 "status": "CI_BLOCK",检查 red_button_mttr_blindness 触发并具有 "fail": "CI_BLOCK"violated_invariants: [manual_review_floor, silent_p0_cap]。同样 python3 scripts/compare_drift.py --baseline fixtures/baseline_metrics.json --new fixtures/new_metrics_drift.json --threshold 0.12 返回码 1 并打印 edge_drift=0.18 threshold=0.12 -> FAIL。*
  3. 将三行转移到 capstone/goodhart-note.md:目标指标(MTTR)、guard 指标(silent_p0audit_trace_coverage)、红色按钮规则。*预期:下一次尝试合并具有改进的 MTTR 但 audit_trace_coverage 下降的 PR 会被自动阻塞。*

检查问题

  1. 为什么 MTTR 作为唯一优化目标是危险的?
  1. 质量不变量与 KPI 有什么区别?
  2. 调查古德哈特回归需要哪些足迹字段?
  3. 发布后 MTTR 下降了 30%,但 silent_p0 从 4% 增长到 12%。您会接受发布、回滚,还是要求额外诊断?您具体会检查什么?
我的笔记
0 / 10000

笔记保存在当前浏览器中。在其他设备上将不会显示。

课程菜单

课程

在 Qwen Code CLI 开发中使用 SDD。应用课程
进度 0 / 95