Тема: Приложение D. Калибровка порогов
Уровень сложности: Средний
Расчётное время изучения: 6-8 часов (теория: 3 часа, практика: 3-5 часов)
Предварительные требования: Прохождение глав 5, 6, 9, 10 и 11 курса AgentClinic
Базовое знание YAML и работы с командной строкой
Понимание концепций мутационного тестирования, теневых спецификаций и ярусных бюджетов
Опыт работы с метриками SLA и CI/CD-процессами
Знакомство с эффектом Гудхарта и его проявлениями в инженерных системах
Цели обучения: Настраивать и калибровать пороги мутационного тестирования (strict_reject_rate, depth_of_diagnostics, recovery_time_p95) с учётом специфики проекта и фиксировать обоснование в validation.md
Пересчитывать веса и пороги отбора теневых спецификаций (keep-threshold, reject-threshold, budget-tokens) при изменении цены ложной эскалации и бюджета образцов-подсказок
Проектировать ярусные бюджеты токенов с корректным разбиением local/frontier и проверять целостность через compile.py
Выявлять симптомы эффекта Гудхарта в сети защитных метрик и корректировать пороги только парными сдвигами с сохранением инвариантов
Определять production-готовность системы с учётом типа действий (stateless/stateful) и независимых блокирующих инвариантов
Обзор: Приложение D — справочный материал для переноса процесса AgentClinic-production в собственный проект. Оно систематизирует все таблицы порогов «Низкий / По умолчанию / Высокий» для пяти ключевых областей: мутационное тестирование, отбор теневых спецификаций, ярусные бюджеты, защита метрик от Гудхарта и production-готовность. Центральный принцип: пороги имеют смысл только в паре. Сдвиг одного значения без пересчёта связанного — это не калибровка, а демонтаж контура управления. Руководство содержит практические упражнения на реальных скриптах, сигналы для пересмотра порогов и предупреждения о рисках некорректной калибровки.
Ключевые концепции: Парная калибровка: Фундаментальный принцип приложения D: любой порог существует в связке с другими параметрами. Например, strict_reject_rate и depth_of_diagnostics двигаются только вместе. Если strict_reject_rate растёт, а depth_of_diagnostics падает — это симптом Гудхарта, а не улучшение качества. Аналогично: штраф false_escalation и вес mttr_gain, silent_p0 и manual_review_rate, edge_drift и audit_trace_coverage.
Значения по умолчанию agentclinic: Базовая линия для production-системы со средним потоком инцидентов (200/день), зрелым SDD-процессом (6+ месяцев) и смешанным типом действий. Все отклонения от этой линии требуют обоснования в validation.md.
Сигналы пересмотра порогов: Конкретные триггеры, указывающие на необходимость калибровки: отсутствие блокировок за квартал (порог избыточно низкий), концентрация регрессий по одному mutation_id (порог недостаточен), падение recovery_time_p95 к нулю при росте strict_reject_rate (Гудхарт), рост manual_review_rate при росте mttr_gain (подмена цели).
Эффект гудхарта в метриках: Когда метрика становится целью, она перестаёт быть хорошей метрикой. В контексте AgentClinic: рост strict_reject_rate без реального улучшения диагностики, сокращение MTTR за счёт игнорирования сложных кейсов, рост mttr_gain при параллельном росте false_escalation — все эти паттерны требуют парной коррекции.
Независимые блокирующие инварианты: Метрики, которые не входят в суммарный порог, но блокируют слияние независимо. Например, audit_trace_coverage = 1.0 — обязательное условие для production-готовности; при audit_trace_coverage = 0.7 готовность 22/25 или даже 25/25 не допускает auto-переключение.
Ротация зерна мутаций: Практика периодической смены seed в мутационном тестировании для предотвращения переобучения валидатора на фиксированный набор mutation_id. Одно зерно, повторяющееся пять спринтов подряд — сигнал к ротации.
Ярусное разбиение бюджета: Архитектура распределения токенов между local-ярусом (9M из 10M) и frontier-ярусом (1M). Пропорции связаны с SLA по фазам: изменение daily_budget_tokens без обновления budget_plan_phases нарушает целостность и приводит к ошибке в compile.py.
Профили риска для теневых спецификаций: Конфигурация весов в формуле отбора: консервативный профиль (0.3, 0.4, 0.2, 0.8) сильнее штрафует ложную эскалацию и меньше награждает MTTR, чем стандартный (0.5, 0.3, 0.2, -0.4).
Режимы эксплуатации: Иерархия допусков: auto ≥23/25 — полностью автоматический режим; полуручной 20–22 — остановка после implement с явным подтверждением; canary — постепенное развёртывание. Снижение ниже 23/25 — это изменение режима, а не «мягче порог».
Сеть зависимостей метрик: Полная модель взаимосвязей: silent_p0 и escalation_rate положительно влияют на MTTR; manual_review_rate и audit_trace_coverage отрицательно влияют на MTTR и escalation_rate; postmortem_regression положительно связана с audit_trace_coverage и отрицательно — с manual_review_rate.
Практические упражнения: Название: Упражнение D.1: Калибровка depth_of_diagnostics
Проблема: В проекте с графом маршрутов из 80 рёбер (multi-tenant) требуется проверить, как изменение порога depth_of_diagnostics_min с 3 на 5 повлияет на immunity_score. Используйте скрипты из book2/examples/stress-mutator.
Решение: 1. Перейдите в директорию: 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 — должен пройти (средняя глубина 4 > 3)
- Запустите ужесточённый расчёт: python3 scripts/immunity_score.py --validator-results out/validator_results.json --expected out/expected_failures_depth5.json --out out/immunity_depth5.json — должен завершиться кодом 1
- Сравните дельту: это не новый дефект, а цена ужесточения порога. Зафиксируйте обоснование в validation.md: «Граф расширен до 80 рёбер, multi-tenant; depth_of_diagnostics_min повышен до 5, strict_reject_rate пересчитан до ≥0.995».
Сложность: intermediate
Название: Упражнение D.2: Консервативный профиль теневого аукциона
Проблема: Команда здравоохранения требует снизить ложные эскалации. Сформируйте консервативный профиль весов и проанализируйте, какие кандидаты изменят статус.
Решение: 1. Перейдите в директорию: 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
- Зафиксируйте в validation.md: «Цена ложной эскалации повышена до 0.8, keep-threshold снижен до 0.70, budget-tokens увеличен до 2000 для компенсации консервативности».
Сложность: intermediate
Название: Упражнение D.3: Проверка целостности ярусного бюджета
Проблема: Проект с потоком 50 инцидентов/день требует бюджет 5M токенов. Проверьте, что compile.py отклонит некорректную спецификацию с изменённым daily_budget_tokens без пересчёта фазовых квот.
Решение: 1. Перейдите в директорию: cd book2/examples/budget-keeper
- Скомпилируйте корректный план: python3 scripts/compile.py --budget-spec specs/budget_network_5m.yaml --out out/budget_plan_5m.json
- Проверьте сохранение пропорций: local = 4.5M, frontier = 0.5M (90/10)
- Смоделируйте отказ локального яруса: 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"
- Создайте некорректную спецификацию: скопируйте budget_network_5m.yaml, измените только daily_budget_tokens на 3M, оставив фазовые квоты
- Убедитесь, что compile.py падает с ошибкой суммы — это защита от демонтажа контура.
Сложность: intermediate
Название: Упражнение D.4: Демонстрация опасности раздельной калибровки guard-метрик
Проблема: Покажите, почему ослабление двух независимых защит одновременно пропускает плохой релиз, тогда как ослабление одной — нет.
Решение: 1. Перейдите в директорию: cd book2/examples/goodhart-validator; mkdir -p out
- Первый сценарий — ослабление silent_p0_cap: 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 — результат: FAILED (silent_p0=0.18 > 0.08)
- Второй сценарий — опасное двойное ослабление: 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 — результат: PASSED (ложноположительный)
- Сделайте вывод: guard-метрики образуют единый контракт риска, ослаблять их по отдельности нельзя. Зафиксируйте в validation.md запрет на однострочные изменения YAML.
Сложность: advanced
Название: Упражнение D.5: Проверка независимости audit_trace_coverage
Проблема: Покажите, что audit_trace_coverage — независимый блокирующий инвариант, не входящий в сумму 25/25 для production-готовности.
Решение: 1. Перейдите в директорию: 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
- Запустите с readiness_block_audit.json: python3 out/check_readiness_t22.py --readiness fixtures/readiness_block_audit.json
- Результат: BLOCKED из-за audit_trace_coverage=0.7 < 1.0, несмотря на сумму 22/25 (или даже потенциальные 25/25)
- Проанализируйте: это демонстрирует, что Security=0 или audit_trace_coverage<<1.0 — абсолютные блокираторы. Снижение THRESHOLD до 22 — это переход в полуручной режим, а не «мягче допуск». Зафиксируйте в validation.md: «audit_trace_coverage=1.0 — регуляторный инвариант, не подлежит калибровке».
Сложность: intermediate
Кейсы: Название: Кейс: Перекалибровка порогов при выходе на рынок здравоохранения
Сценарий: Команда AgentClinic-production, работавшая с внутренними инструментами (strict_reject_rate ≥ 0.98, depth_of_diagnostics ≥ 3), получила контракт на обработку инцидентов медицинской информационной системы. Регуляторные требования: пропуск P0-критичного инцидента влечёт штраф до 2% годового оборота и уголовную ответственность руководства.
Задача: Стандартные пороги AgentClinic недостаточны для здравоохранения. Команда столкнулась с необходимостью одновременного повышения strict_reject_rate до ≥0.995, depth_of_diagnostics до ≥5 (граф маршрутов расширен до 120 рёбер, multi-tenant с изоляцией пациентов), снижения recovery_time_p95 до ≤1500 мс при >500 PR/день. Риск: изолированное повышение strict_reject_rate без depth_of_diagnostics приведёт к эффекту Гудхарта — валидатор будет отклонять всё подряд, включая корректные фиксы.
Решение: Команда провела парную калибровку по методологии Приложения D: 1) Зафиксировала обоснование в validation.md: «Регуляторный контракт §12.3, цена пропуска P0: штраф 2% + уголовная ответственность»; 2) Повысила strict_reject_rate до 0.995 и одновременно depth_of_diagnostics до 5; 3) Увеличила число мутантов на класс до 5+ с ротацией зерна каждые 2 спринта; 4) Провела стресс-тестирование через immunity_score.py с искусственно ужесточённым порогом depth_of_diagnostics_min=5; 5) Убедилась, что recovery_time_p95 не падает к нулю при росте strict_reject_rate — отсутствие симптома Гудхарта.
Результат: Система прошла аудит регулятора с нулевыми пропусками P0 за 6 месяцев эксплуатации. MTTR вырос на 12% из-за углублённой диагностики, но это было принято как необходимая цена соответствия. Ротация зерна выявила 3 переобучения валидатора до их попадания в production.
Извлечённые уроки: Регуляторные требования требуют не «максимальных» порогов, а обоснованных парных сдвигов с документированием цены решения
Ротация зерна — не опциональная практика, а обязательная при повышении числа мутантов, иначе валидатор переобучается на фиксированные паттерны
Рост MTTR на 12% при увеличении depth_of_diagnostics — ожидаемый trade-off, который нужно закладывать в SLA с заказчиком заранее
Отсутствие симптома Гудхарта (recovery_time_p95 → 0) — более важный индикатор здоровья, чем абсолютное значение strict_reject_rate
Связанные концепции: Парная калибровка
Сигналы пересмотра порогов
Эффект Гудхарта в метриках
Ротация зерна мутаций
Название: Кейс: Демонтаж контура при некорректном сжатии бюджета
Сценарий: Финтех-стартап, переживавший кризис ликвидности, решил сократить расходы на LLM-инфраструктуру на 50%. Технический директор изменил только daily_budget_tokens в спецификации с 10M до 5M, не пересчитав фазовые квоты.
Задача: Скрипт compile.py упал с ошибкой суммы — это защитный механизм, предусмотренный в Приложении D. Однако директор, не разобравшись, закомментировал проверку целостности в локальной копии и собрал бюджет вручную. Результат: local-ярус получил 4M вместо 4.5M, frontier — 0.2M вместо 0.5M, а остаток 0.8M был «распределён по усмотрению» без связи с SLA фаз.
Решение: Проблема была выявлена через 3 недели, когда при отказе local-coder система не смогла переключиться на frontier: шлюз оказался слишком жёстким из-за недостатка токенов во frontier-ярусе. Команда восстановила исходный механизм compile.py, провела корректную компиляцию с пропорциями 90/10 (4.5M/0.5M), смоделировала отказ через simulate.py и убедилась в работоспособности failover_to_frontier.
Результат: Простой системы составил 47 минут, 12 инцидентов ушли в manual_queue с превышением SLA. Финансовые потери превысили экономию от сжатия бюджета в 8 раз. Технический директор был отстранён от принятия инфраструктурных решений.
Извлечённые уроки: compile.py — не бюрократическое препятствие, а защита контура управления; обход проверок — это демонтаж, а не оптимизация
Пропорции 90/10 связаны с SLA по фазам; их изменение требует пересчёта budget_plan_phases, а не только daily_budget_tokens
Экономия на инфраструктурных guard-метриках имеет отрицательную ROI: потери от одного простоя превышают годовую экономию
Методология Приложения D требует, чтобы любое изменение бюджета проходило через полный цикл: compile → simulate → inspect → документирование в validation.md
Связанные концепции: Ярусное разбиение бюджета
Парная калибровка
Сигналы пересмотра порогов
Название: Кейс: Ложноположительный проход при раздельной калибровке guard-метрик
Сценарий: Команда платёжной системы, работавшая под давлением бизнеса «ускорить релизы», решила «оптимизировать» guard-метрики. Вместо парной калибровки silent_p0 и manual_review_rate, они независимо ослабили оба порога: silent_p0_cap с 0.05 до 0.20 и manual_review_rate с 0.15 до 0.10.
Задача: По отдельности каждое изменение казалось обоснованным: «у нас редкие P0» и «ручных рецензентов мало». Одновременное ослабление создало ложноположительный проход: плохой релиз с silent_p0=0.18 и manual_review_rate=0.08 прошёл валидацию, хотя оба показателя были критически низкими. Это прямое проявление принципа Приложения D: guard-метрики образуют единый контракт риска.
Решение: Инцидент с утечкой платёжных данных (последствия: 340K затронутых транзакций, регуляторный штраф $2.4M) привёл к аудиту. Аудиторы воспроизвели упражнение D.4: ослабление только silent_p0_cap до 0.08 всё ещё блокировало плохой релиз, тогда как двойное ослабление — пропускало. Команда внедрила жёсткое правило: любое изменение validation.yaml требует review с проверкой на «независимые ослабления» и автоматический прогон через goodhart-validator.
Результат: Система восстановлена, регуляторные требования выполнены через экстренное повышение manual_review_rate до 0.25 и введение подписанной трассировки audit_trace_coverage. Время на релиз выросло на 40%, но нулевой уровень пропущенных P0 восстановлен.
Извлечённые уроки: Guard-метрики — это не набор независимых рубильников, а единый контракт риска; ослабление одного в отрыве от остальных — демонтаж защиты
Ложноположительный проход хуже ложноотрицательного блока: пропущенный дефект в production стоит на порядки дороже задержки релиза
Автоматизация проверки «независимых ослаблений» через goodhart-validator должна быть обязательной, не опциональной
Давление бизнеса «ускорить» не оправдывает обход методологии; нужен механизм эскалации, а не тихий компромисс
Связанные концепции: Независимые блокирующие инварианты
Эффект Гудхарта в метриках
Сеть зависимостей метрик
Парная калибровка
Советы по изучению: Проходите материал последовательно по разделам D.1–D.5, не пропуская упражнения — каждое упражнение демонстрирует конкретный риск некорректной калибровки
Ведите собственный validation.md параллельно с изучением: фиксируйте гипотетические обоснования для своего проекта, даже если это учебная симуляция
Используйте подход «предскажи, потом проверь»: перед запуском скрипта запишите ожидаемый результат, затем сравните с фактическим — это развивает интуицию о поведении порогов
Создавайте таблицу соответствия «мой проект → параметры AgentClinic → отклонения → обоснование» — это шаблон для реального переноса
Для визуального стиля: перерисуйте mermaid-схему сети зависимостей метрик на бумаге, отмечая положительные и отрицательные связи разными цветами — это помогает запомнить, какие метрики двигаются парно
Практикуйте выявление симптомов Гудхарта: придумывайте сценарии «что если» и проверяйте, есть ли в Приложении D соответствующий сигнал пересмотра
Группируйте изучение по принципу «один день — одна система риска»: D.1+D.4 (мутации и Гудхарт), D.2+D.5 (теневые спецификации и готовность), D.3 (бюджеты) — так лучше прослеживаются связи между разделами
Для аудиального стиля: диктуйте вслух обоснования калибровки в формате «Если [параметр проекта], то [порог], потому что [риск]» — это готовит к реальным дискуссиям с stakeholders
Тестируйте граничные случаи: что произойдёт, если установить все пороги в «Низкий» или «Высокий» одновременно? Почему это неработоспособно?
Дополнительные ресурсы: Исходные материалы курса agentclinic: Главы 5, 6, 9, 10, 11 — теоретическая база, на которую опирается Приложение D
Репозиторий book2/examples: Практические скрипты для всех упражнений: stress-mutator, shadow-auction, budget-keeper, goodhart-validator, real-api
Шаблон validation.md: Формат документирования обоснований калибровки; создайте собственную копию для проекта
Документация по mermaid: Для самостоятельного редактирования и расширения схем сети зависимостей метрик
Статья «goodhart's law and machine learning» (varoquaux): Теоретическое обоснование парной калибровки и сетевой структуры guard-метрик
Google sre book, chapter 4 (service level objectives): Параллели между error budgets и ярусными бюджетами токенов в AgentClinic
Чек-лист переноса agentclinic в production: Самостоятельно составьте на основе таблиц D.1–D.5 с колонками «Параметр проекта», «Наше значение», «Отклонение от умолчания», «Обоснование», «Дата пересмотра»
Резюме: Приложение D — практический справочник по калибровке порогов AgentClinic-production для переноса в собственные проекты. Ключевые принципы: (1) пороги имеют смысл только в паре, сдвиг одного без пересчёта связанного — демонтаж контура; (2) любое отклонение от значений по умолчанию требует обоснования в validation.md; (3) guard-метрики образуют единый контракт риска, а не набор независимых рубильников; (4) симптомы Гудхарта (рост целевой метрики без реального улучшения, падение связанных показателей) — главный сигнал для парной коррекции; (5) независимые блокирующие инварианты (audit_trace_coverage = 1.0, Security = 0) не подлежат калибровке вообще. Пять разделов покрывают мутационное тестирование, теневые спецификации, ярусные бюджеты, защиту от Гудхарта и production-готовность — каждый с таблицами порогов, практическими упражнениями на реальных скриптах, сигналами пересмотра и предупреждениями о рисках. Методология требует документирования, автоматической проверки целостности и регулярной ротации параметров для предотвращения переобучения.