Приложение D. Калибровка порогов
Это справочное приложение. На первом проходе оно не нужно: учебный минимум каждой главы рассчитан на пороги по умолчанию для AgentClinic-production. Этот файл собирает все таблицы «Низкий / По умолчанию / Высокий», упражнения по сдвигу порогов и сигналы, по которым порог нужно пересматривать. Используйте его при переносе процесса в свой проект, когда стандартные значения перестали подходить.
Принцип, общий для всех таблиц: пороги имеют смысл только в паре. Сдвиг одного значения без пересчёта связанного — это не калибровка, а демонтаж контура. У каждого раздела явно перечислены риски такого сдвига.
D.1 Мутационное тестирование (глава 5)
Числа главы 5 — значение по умолчанию для AgentClinic-production со средним потоком инцидентов и зрелым SDD-процессом. В вашем проекте пороги зависят от цены пропуска P0, сложности графа маршрутов, SLA-окна CI и стабильности входящего потока. Сдвиг любой строки должен сопровождаться записью в validation.md с обоснованием.
| Параметр проекта | Низкий | По умолчанию (AgentClinic) | Высокий |
|---|
| Цена пропуска P0 | strict_reject_rate ≥ 0.92 | **≥ 0.98** | ≥ 0.995 (платежи, здравоохранение) | | Сложность графа маршрутов | depth_of_diagnostics ≥ 2 (≤10 рёбер) | **≥ 3** (10–50 рёбер) | ≥ 5 (>100 рёбер, multi-tenant) | | SLA-окно CI | recovery_time_p95_ms ≤ 800 | **≤ 1200** | ≤ 1500 (>500 PR/день) | | Стабильность потока инцидентов | 1 мутант на класс | 2 мутанта на класс | 5+ мутантов на класс + ротация зерна |
Упражнение
cd book2/examples/stress-mutator
mkdir -p out
cp expected/expected_failures.json out/expected_failures_depth5.json
sed -i 's/"depth_of_diagnostics_min": 3/"depth_of_diagnostics_min": 5/' out/expected_failures_depth5.json
python3 scripts/immunity_score.py \
--validator-results out/validator_results.json \
--expected expected/expected_failures.json \
--out out/immunity_default.json
python3 scripts/immunity_score.py \
--validator-results out/validator_results.json \
--expected out/expected_failures_depth5.json \
--out out/immunity_depth5.json
Первый запуск должен пройти: средняя глубина диагностики равна 4 и превышает порог 3. Второй запуск должен завершиться кодом 1: тот же валидатор уже не проходит искусственно ужесточённый порог depth_of_diagnostics_min = 5. Дельта показывает не новый дефект в мутантах, а цену ужесточения порога.
Когда пересмотреть порог
- За квартал ни одно слияние не заблокировано порогом — он избыточно низкий.
- Больше 10 регрессий с одним и тем же
mutation_idза неделю —depth_of_diagnosticsнедостаточен, увеличить. recovery_time_p95падает к нулю при ростеstrict_reject_rate— признак Гудхарта.- Появился новый класс инцидентов — пересчитать все три порога заново.
- Одно зерно (
seed) повторяет тот же наборmutation_idпять спринтов подряд — нужна ротация зерна.
Риск: если strict_reject_rate растёт, а depth_of_diagnostics одновременно падает, это симптом Гудхарта. Оба параметра двигают только парой.
D.2 Отбор теневых спецификаций (глава 6)
Веса 0.5*mttr_gain + 0.3*early_signal + 0.2*coverage - 0.4*false_escalation и пороги keep/reject — значения по умолчанию для AgentClinic-production. В вашем проекте они зависят от цены ложной эскалации, важности раннего сигнала, размера исторической базы и доступного бюджета на образцы-подсказки.
| Параметр | Низкий | По умолчанию (AgentClinic) | Высокий |
|---|---|---|---|
| Цена ложной эскалации | штраф false_escalation: 0.2–0.3 | **0.4** | 0.6–0.8 (здравоохранение, платежи) |
| Важность раннего сигнала | вес early_signal: 0.2 | **0.3** | 0.4–0.5 (радиус последствий >5 сервисов) |
| Размер исторической базы | 20–50 кейсов (дымовая проверка) | 50+ кейсов | 200+ кейсов с ротацией окон |
| Бюджет образцов-подсказок | keep-threshold 0.80, 4 слота | **0.70, 8 слотов / 2000 токенов** | 0.60, 12 слотов / 4000 токенов |
Упражнение
Прогоните аукцион с консервативным профилем риска (выше штраф за ложную эскалацию):
cd book2/examples/shadow-auction
python3 scripts/score.py --candidates candidates/candidates.yaml --incidents data/incidents.jsonl --weights "0.3,0.4,0.2,0.8" --out out/scorebook.json
python3 scripts/decide.py --scorebook out/scorebook.json --budget-tokens 2000 --keep-threshold 0.70 --reject-threshold 0.40 --out-auction out/auction.json --out-quarantine out/quarantine.json
При таком профиле shadow.p0.voice_handoff переезжает из winner в disputed, а shadow.alert.red_color_urgency остаётся в rejected. Это проявление нового профиля: команда меньше награждает сокращение MTTR и сильнее штрафует ложную эскалацию.
Когда пересмотреть порог
- За месяц ни один
winnerне показал положительного эффекта в пост-мортемах —keep-thresholdслишком низкий. - Доля
disputedустойчиво выше 40% — формула не различает кейсы. - В одной фазе выбирается больше 8 победителей —
budget-tokensподобран без учёта размераQWEN.md. - Появился новый класс инцидентов вне исторических данных.
mttr_gainиfalse_escalationрастут вместе — симптом Гудхарта.
Риск: штраф false_escalation и вес mttr_gain двигают только парой. Сдвиг одного без пересмотра другого ломает связь «полезный сигнал ↔ ложный шум».
D.3 Ярусные бюджеты (глава 9)
Бюджет 10M токенов с разбиением 9M/1M (local/frontier) — значение по умолчанию для AgentClinic-production со средним потоком инцидентов. В вашем проекте размер бюджета и пропорции зависят от потока инцидентов, средней стоимости фазы, доли спорных ревью и чувствительности к падению local-coder.
| Параметр проекта | Низкий | По умолчанию (AgentClinic) | Высокий |
|---|---|---|---|
| Поток инцидентов в сутки | ≤50/день → 2–3M токенов, 90/10 | 200/день → 10M, 9M/1M (90/10) | ≥500/день → 25–40M, 80/20 |
| Стоимость фазы (токенов) | ~20K | ~50K | 100K+ (многошаговый реплей) |
| Доля спорных ревью | ≤5% → frontier 5–7% | ~10% → 1M (10%) | 15–25% → 15–20% frontier |
Чувствительность к падению local-coder | ≤1 раз/месяц → резерв 5% | 2–4 раза/месяц → 7% | еженедельно → 15% + дублированный провайдер |
Упражнение
cd book2/examples/budget-keeper
python3 scripts/compile.py --budget-spec specs/budget_network_5m.yaml --out out/budget_plan_5m.json
python3 scripts/simulate.py --plan out/budget_plan_5m.json --scenario scenarios/fail_local_45m.json --out out/fail_result_5m.json
python3 scripts/inspect.py --result out/fail_result_5m.json --query "failover_to_frontier==2 && degraded_queue==18 && token_health_min>=0.5"
Проверьте, удержался ли token_health_min выше 0.5 при половинном бюджете. В готовом 5M-варианте пропорции сохранены: local-ярус получает 4.5M, frontier — 0.5M. Если изменить только daily_budget_tokens, но не фазовые квоты, compile.py обязан упасть с ошибкой суммы.
Когда пересмотреть порог
- За месяц ни одного срабатывания
degraded_mode— бюджет избыточен либо реальный поток ниже ожидаемого. token_health_minуходит ниже 0.5 чаще раза в неделю — локального яруса недостаточно.failover_to_frontierустойчиво равно 0 при отказах локального яруса — шлюз слишком жёсткий, frontier не работает как страховка.- Доля
manual_queueпосле ручного тайм-аута растёт два месяца подряд —manual_timeout_secслишком короткий. - В сутки тратится менее 60%
daily_budget_tokens— пора сжимать бюджет.
Риск: разделение 9M/1M связано с SLA по фазам. Сдвигать его без обновления budget_plan_phases в спецификации нельзя — frontier перестанет вмещать «спорные» кейсы.
D.4 Защита метрик от Гудхарта (глава 10)
Пороги silent_p0 ≤ 5%, manual_review_rate ≥ 15%, edge_drift ≤ 0.12, audit_trace_coverage = 1.0 — значения по умолчанию для AgentClinic-production. В вашем проекте они зависят от цены пропущенного P0, доступности ручных рецензентов, динамики входящего потока и регуляторных требований к аудиту.
| Параметр проекта | Низкий | По умолчанию (AgentClinic) | Высокий |
|---|---|---|---|
| Цена пропущенного P0 | silent_p0 ≤ 8% | **≤ 5%** | ≤ 1–2% (платежи) |
| Доступность ручных рецензентов | manual_review_rate ≥ 8% | **≥ 15%** | ≥ 25% (регуляторно) |
| Динамика входа | edge_drift ≤ 0.20 | **≤ 0.12** | ≤ 0.05 (сезонные пики) |
| Регуляторика аудита | audit_trace_coverage ≥ 0.95 | **= 1.00** | = 1.00 + подписанная трассировка |
Упражнение
cd book2/examples/goodhart-validator
mkdir -p out
# Скопировать spec в локальный out/ и ослабить silent_p0_cap до 0.08
cp specs/validation.yaml out/validation_loose.yaml
sed -i 's/threshold: 0.05/threshold: 0.08/' out/validation_loose.yaml
python3 scripts/run_validation.py \
--validation out/validation_loose.yaml \
--metrics fixtures/new_metrics_bad.json
# Опасный вариант: ослабить сразу две независимые защиты
cp specs/validation.yaml out/validation_unsafe.yaml
sed -i 's/threshold: 0.15/threshold: 0.10/' out/validation_unsafe.yaml
sed -i 's/threshold: 0.05/threshold: 0.20/' out/validation_unsafe.yaml
python3 scripts/run_validation.py \
--validation out/validation_unsafe.yaml \
--metrics fixtures/new_metrics_bad.json
Первый прогон должен остаться красным: плохой релиз с silent_p0=0.18 всё ещё нарушает silent_p0_cap. Второй, опасный, вариант проходит только потому, что одновременно ослаблены две независимые защиты. Это показывает, почему guard-метрики нельзя калибровать по одной строке YAML.
Когда пересмотреть порог
- За квартал ни один релиз не заблокирован
silent_p0_cap— либо команда не делает рискованных изменений, либо порог избыточно мягкий. manual_review_rateпадает три спринта подряд при ростеmttr_gain— симптом Гудхарта, ручные рецензенты перестали быть страховкой.edge_driftстабильно колеблется около 0.10–0.11 — реальная динамика входа близка к порогу.audit_trace_coverageопустился ниже 1.0 хоть в одном прогоне — нарушение регуляторного инварианта, hot-fix, не калибровка.
- Появился новый класс инцидентов, который не попадает в
silent_p0, — нужны новые инварианты, не пересмотр старых.
Риски: silent_p0 и manual_review_rate двигают только парой. edge_drift имеет смысл только при audit_trace_coverage=1.0, иначе дрейф вычисляется по частичной выборке. Все четыре порога образуют единый контракт риска: ослабить один в отрыве от остальных — значит сломать его, а не настроить.
Полная сеть метрик
В тексте главы используется упрощённая mermaid-схема с тремя метриками и одним сторожем. Полная сеть зависимостей выглядит так:
flowchart LR
MTTR[MTTR]
silent_p0[silent_p0]
manual_review_rate[manual_review_rate]
escalation_rate[escalation_rate]
postmortem_regression[postmortem_regression]
audit_trace_coverage[audit_trace_coverage]
silent_p0 -->|положительная_взаимозависимость| MTTR
escalation_rate -->|положительная_взаимозависимость| MTTR
manual_review_rate -->|отрицательная_взаимозависимость| MTTR
manual_review_rate -->|отрицательная_взаимозависимость| escalation_rate
audit_trace_coverage -->|отрицательная_взаимозависимость| escalation_rate
audit_trace_coverage -->|отрицательная_взаимозависимость| silent_p0
postmortem_regression -->|положительная_взаимозависимость| audit_trace_coverage
postmortem_regression -->|отрицательная_взаимозависимость| manual_review_rateЛогика та же, что в упрощённой версии: красная зона — MTTR и silent_p0; путь к её ослаблению идёт через сокращение ручной проверки и потерю аудит-следа.
D.5 Production-готовность (глава 11)
Порог 23/25 — значение по умолчанию для AgentClinic-production со средней зрелостью SDD-процесса и смешанным типом действий. В вашем проекте порог зависит от цены ошибки переключения (cutover), зрелости процесса, нагрузки на ручное ревью и характера действий (stateless / stateful).
| Параметр проекта | Низкий | По умолчанию (AgentClinic) | Высокий |
|---|---|---|---|
| Цена ошибки переключения | внутренний инструмент: 21–22/25 только полуручно | смешанный production: auto ≥23/25 | платежи/здравоохранение: auto ≥24/25 |
| Зрелость SDD-процесса | 3 месяца → только полуручной 20–22 | 6+ месяцев → полуручной 20–22, auto 23+ | 12+ месяцев + 50+ реплеев → auto 23+, меньше ручных остановок |
| Нагрузка ручного ревью | каждый пулл-реквест (~5/неделю) → можно держать 21–22 полуручно | 20–30% пулл-реквестов → auto 23+ | редко → auto 24/25 | | Тип действия | stateless → 22/25 только canary/полуручно, auto 23+ | смешанный → auto 23+ | stateful → auto 24+ и backup_verified |
Упражнение
Скрипт check_readiness.py хардкодит THRESHOLD = 23. Прогоните с другим значением через копию:
cd book2/examples/real-api && mkdir -p out
cp scripts/check_readiness.py out/check_readiness_t22.py
sed -i 's/THRESHOLD = 23/THRESHOLD = 22/' out/check_readiness_t22.py
python3 out/check_readiness_t22.py --readiness fixtures/readiness_block_audit.json
При THRESHOLD = 22 readiness_block_audit.json всё равно блокируется из-за audit_trace_coverage=0.7 < 1.0, хотя сумма 22/25 проходит. Это показывает, что audit_trace_coverage — независимый блокирующий инвариант, а не часть суммы. Упражнение про чувствительность порога, а не рекомендация снижать auto-допуск.
Когда пересмотреть порог
- За квартал ни одна готовность не заблокирована порогом — он слишком низкий для текущей зрелости команды.
- Доля полуручных инцидентов растёт три спринта подряд — порог 23/25 не достигается из-за системного пробела в Verification или Process.
- Появился класс действий с
stateful=true— требуйтеbackup_verifiedи поднимите порог по этому классу до 24/25. - Все провалы готовности в течение месяца идут по одной оси — это пробел в шаблонах SDD; исправлять шаблоны, а не двигать порог.
- Время сборки артефактов готовности превышает SLA на переключение — пересмотрите, какие баллы можно автоматизировать, а не снижайте порог.
Риск: порог 23/25 несовместим с нулевым баллом по Security при любой сумме — такой провал блокирует слияние независимо от итога. Снижение ниже 23/25 меняет режим эксплуатации: это уже не auto-допуск, а полуручной или canary-режим. Даже «low» (21/25) — это остановка после каждого implement-шага и явное подтверждение оператора, а не право агента выполнить ремедиацию самостоятельно.