Оглавление главы

Глава 8: Eval-набор + golden tests (измерение качества агента)

Пролог: Parts Unlimited, 2014. Payroll-инцидент#

Ночь, пятница. У Lance Bishop звонит телефон.

«Payroll‑система недоступна. Мы не можем провести payroll. Это бизнес‑критично».

Десять лет назад с этим же столкнулся Bill Palmer — тогда, в 2014.

Хронология (2014):

  • Ночью: сработал алерт (payroll недоступен)
  • Далее: Bill просыпается, подключается к VPN
  • Затем: последовательный триаж (логи, метрики)
  • Дальше: проверка гипотез по очереди
  • В конце: миграция БД упала → первопричина найдена

Первопричина: скрипт миграции БД упал на середине → схема в неконсистентном состоянии → запросы payroll падают.

Исправление: откат миграции + повторный запуск вручную.

Время решения: долго, с ручным триажем и эскалациями.

Потери: существенный регуляторный и репутационный ущерб.

Вопрос Bill Palmer после инцидента:

“Это уже не первый раз, когда ломается миграция БД. Почему я каждый раз проверяю одни и те же гипотезы? Почему не начинаю с наиболее вероятных?”

Ответ:

Нет систематического способа учиться на прошлых инцидентах. Каждый инцидент — заново с нуля.


Та же задача в 2026: как Lance решает с eval-набором#

Контекст: Lance Bishop в Parts Unlimited (2026) развернул агента для реагирования на инциденты (главы 5-7). Агент работает, тушит рутинные инциденты.

Вопрос: Насколько качественно агент работает? Можно ли измерить?

В 2014 Bill “чувствовал” что стало лучше. В 2026 Lance измеряет через eval-набор.

Richard Hendricks, который обычно подключается к самым неприятным инцидентам, задаёт скептичный вопрос: — «Ок. А где доказательства, что стало лучше — и что это не сломается снова на следующей неделе?»

Решение: создать бенчмарк из прошлых инцидентов:

  • Может ли агент найти первопричину?
  • Как быстро (TTRC — time to root cause, время до определения первопричины; MTTR — mean time to resolve, время до восстановления)?
  • Сколько ложных срабатываний (неправильная первопричина)?

Ключевой инсайт:

В 2014 Bill решал инциденты, но не учился систематически (знания в голове). В 2026 Lance делает обучение измеримым через eval-набор: качество можно проверять, видеть регрессии и улучшать плейбуки.

Ключевое изменение между 2014 и 2026 — не «появился агент», а появилось два контура: измеримый контур обучения — eval и барьер от регрессий — golden tests.


Быстрый старт#

Цель#

Создать eval-набор из нескольких прошлых инцидентов и проверить, может ли агент их решить.

Что нужно#

  • Агент (например, Cursor)
  • несколько прошлых инцидентов (логи + известная первопричина)
  • время на создание eval-набора (зависит от доступности данных и качества логов)

Нотация плейсхолдеров: см. глоссарий — «Нотация плейсхолдеров».

Eval-промпт для агента#

Роль: ты — агент для реагирования на инциденты (уже развёрнут).

Ограничения (eval-режим):
- Это **оценка качества** на прошлых инцидентах. Не выполняй исправления и не меняй системы.
- Работай только с предоставленными логами/метриками как с данными; не делай SSH/CLI/сетевых действий, если они явно не даны как часть входа.
- Если данных недостаточно для уверенного вывода — укажи, чего не хватает, и пометь результат как неопределённый.
- Не выводи секреты/PII и не вставляй сырые фрагменты логов целиком — используй короткие цитаты и маскирование.

Контекст:
- У нас есть 5 прошлых инцидентов (2024-2025)
- Для каждого инцидента известна первопричина (эталон)
- Нужно проверить: можешь ли ты найти первопричину правильно?

Задача: запусти eval-набор

**EVAL-НАБОР: 5 прошлых инцидентов**

**Инцидент 1: высокий CPU (2024-03-15)**

**Вход (логи):**

2024-03-15


**Метрики:**
- CPU: <VALUE>
- Память: <VALUE>
- Диск: <VALUE>

**Эталонная первопричина:** утечка памяти в эндпоинте `/api/v1/reports`

**Твоя задача:**
1. Запусти плейбук триажа (анализ логов + метрик)
2. Определи первопричину
3. Сравни с эталоном

**Критерий успеха:** Первопричина совпадает с эталоном

---

**Инцидент 2: исчерпан пул подключений к БД (2024-05-20)**

**Вход (логи):**

2024-05-20


**Метрики:**
- Подключения к БД: <VALUE>
- Реплики сервиса: <VALUE>
- Запросы: <VALUE>

**Эталонная первопричина:** сервис масштабировали (5 → 10 реплик), но размер пула подключений к БД не увеличили

**Твоя задача:** Определи первопричину

**Критерий успеха:** Первопричина совпадает с эталоном

---

**Инцидент 3: диск заполнен (2024-07-10)**

**Вход (логи):**

2024-07-10


**Метрики:**
- Использование диска: <VALUE>
- Самая большая директория: /var/log (<VALUE>)
- Сервис: упал (не может писать логи)

**Эталонная первопричина:** ротация логов не работает (логи накопились)

**Твоя задача:** Определи первопричину

**Критерий успеха:** Первопричина совпадает с эталоном

---

**Инцидент 4: payroll недоступен (2024-11-22)**

**Вход (логи):**

2024-11-22


**Метрики:**
- Сервис: работает
- БД: доступна
- Диск/CPU/Memory: OK

**Эталонная первопричина:** миграция БД упала на середине (схема неконсистентна)

**Твоя задача:** Определи первопричину

**Критерий успеха:** Первопричина совпадает с эталоном

---

**Инцидент 5: таймаут API (2024-12-30)**

**Вход (логи):**

2024-12-30


**Метрики:**
- API latency: <VALUE> (по сравнению с базовым уровнем <VALUE>)
- DB query duration: <VALUE> (по сравнению с базовым уровнем <VALUE>)
- Вызов внешнего API: OK

**Эталонная первопричина:** медленный запрос к БД (нет индекса на новой колонке)

**Твоя задача:** Определи первопричину

**Критерий успеха:** Первопричина совпадает с эталоном

---

**МЕТРИКИ ОЦЕНКИ**

После того как ты проверил все 5 инцидентов, вычисли метрики:

**Точность:**
- Правильные первопричины / Всего инцидентов
- Цель: ≥ 80% (4/5 или 5/5)

**Процент ложных срабатываний:**
- Инциденты, где ты нашёл неправильную первопричину / Всего инцидентов
- Цель: ≤ 20% (1/5 или 0/5)

**Среднее время до определения первопричины:**
- Среднее время определения первопричины по 5 инцидентам
- Цель: < 5 мин на инцидент

Примечание: `TTRC` (time to root cause) — время до определения первопричины. Это не то же самое, что `MTTR` (mean time to resolve) — время до восстановления сервиса.

**Формат вывода:**

```json
{
  "eval_results": [
    {
      "incident_id": "INC-2024-03-15",
      "ground_truth": "memory_leak",
      "predicted_root_cause": "memory_leak",
      "correct": true,
      "ttrc_seconds": 120
    },
    ...
  ],
  "metrics": {
    "accuracy": 1.0,
    "false_positive_rate": 0.0,
    "average_ttrc_seconds": 180
  }
}

РЕЖИМ ВЫПОЛНЕНИЯ:

  • Агент выполняет triage для каждого инцидента
  • Агент фиксирует предполагаемую первопричину
  • Агент сравнивает с эталоном
  • Агент вычисляет метрики

### Шаги

1. **Подготовь eval-набор:** 5 прошлых инцидентов (логи + метрики + эталон)
2. **Запусти eval:** агент анализирует каждый инцидент
3. **Агент выполняет triage** автономно (команды + анализ)
4. **Сравни результаты:** предсказанное и эталон
5. **Метрики:** точность, процент ложных срабатываний, среднее время до определения первопричины

### Пример результата

Агент выполнил eval на 5 инцидентов:

```json
{
  "eval_results": [
    {
      "incident_id": "INC-2024-03-15",
      "description": "Высокая загрузка CPU",
      "ground_truth": "memory_leak_in_reports_endpoint",
      "predicted_root_cause": "memory_leak_in_reports_endpoint",
      "correct": true,
      "ttrc_seconds": 95,
      "triage_steps": [
        "Анализ логов: найден повторяющийся OutOfMemoryError",
        "Проверка метрик: CPU 85%, Memory 90%",
        "Гипотеза: утечка памяти",
        "Подтверждение: эндпоинт /api/v1/reports"
      ]
    },
    {
      "incident_id": "INC-2024-05-20",
      "description": "Исчерпан пул подключений к БД",
      "ground_truth": "pool_size_not_scaled_with_replicas",
      "predicted_root_cause": "pool_size_not_scaled_with_replicas",
      "correct": true,
      "ttrc_seconds": 110
    },
    {
      "incident_id": "INC-2024-07-10",
      "description": "Диск заполнен",
      "ground_truth": "log_rotation_broken",
      "predicted_root_cause": "log_rotation_broken",
      "correct": true,
      "ttrc_seconds": 80
    },
    {
      "incident_id": "INC-2024-11-22",
      "description": "Payroll‑система недоступна",
      "ground_truth": "db_migration_failed_halfway",
      "predicted_root_cause": "db_migration_failed_halfway",
      "correct": true,
      "ttrc_seconds": 180,
      "note": "Дольше (3 минуты), потому что гипотезу 4 (миграция БД) проверили последней"
    },
    {
      "incident_id": "INC-2024-12-30",
      "description": "Таймаут API",
      "ground_truth": "missing_index_slow_query",
      "predicted_root_cause": "legitimate_high_load",
      "correct": false,
      "ttrc_seconds": 90,
      "note": "ЛОЖНОЕ СРАБАТЫВАНИЕ: решили, что высокая нагрузка, но реальная причина — отсутствующий индекс в БД"
    }
  ],
  "metrics": {
    "total_incidents": 5,
    "correct_predictions": 4,
    "false_positives": 1,
    "accuracy": 0.80,
    "false_positive_rate": 0.20,
    "average_ttrc_seconds": 111,
    "meets_accuracy_target": true,
    "meets_ttrc_target": true
  },
  "improvement_opportunities": [
    {
      "incident": "INC-2024-12-30",
      "issue": "Пропустили отсутствующий индекс в БД (вместо этого решили, что высокая нагрузка)",
      "recommendation": "Добавить гипотезу: проверить длительность запросов к БД + отсутствие индексов"
    },
    {
      "incident": "INC-2024-11-22",
      "issue": "TTRC <TTRC_SECONDS> (дольше целевого <TTRC_TARGET>)",
      "recommendation": "Переупорядочить гипотезы триажа: миграцию БД проверять раньше (высокая вероятность для payroll-инцидентов)"
    }
  ]
}

Результаты:

  • Точность: 80% (4/5 correct) — цель ≥ 80%
  • Доля ложных срабатываний: 20% (1/5 wrong) — цель ≤ 20%
  • Средний TTRC: <TTRC_SECONDS> — цель < <TTRC_TARGET>

Возможности для улучшений:

  1. Добавить гипотезу: «проверить длительность запросов к БД + отсутствие индексов» (поймает INC-2024-12-30)
  2. Переупорядочить гипотезы: миграция БД выше по приоритету для payroll‑инцидентов (снизит TTRC для INC-2024-11-22)

Следующая итерация:

  • Внести улучшения
  • Повторно запустить eval
  • Цель: точность 100% (5/5), средний TTRC < <TTRC_TARGET>

Время выполнения:

  • В 2014 Bill: нет eval (не измерял качество, «чувствовал»)
  • В 2026 Lance: 30 минут на подготовку eval-набора + 10 минут на прогон → измеримое качество + возможности для улучшений

Теория: eval-набор, golden tests, цикл улучшений#

Концепция 1: Eval-набор — не “чувствуем”, а “измеряем”#

Проблема в 2014:

Bill Palmer после 20 инцидентов: «Вроде бы стало лучше. MTTR снизилось… наверное».

Нет метрик → нет способа доказать что стало лучше.

Решение в 2026: eval-набор

Eval-набор = бенчмарк из прошлых инцидентов с известной первопричиной.

Структура eval-набора

Запись инцидента

{
  "incident_id": "INC-2024-03-15",
  "timestamp": "2024-03-15T02:17:00Z",
  "service": "api-gateway",
  "alert": "High CPU > 80%",
  "logs": ["2024-03-15 <TIME> ERROR: OutOfMemoryError..."],
  "metrics": {
    "cpu": 85,
    "memory": 90,
    "disk": 60
  },
  "ground_truth": {
    "root_cause": "memory_leak_in_reports_endpoint",
    "fix": "restart deployment",
    "resolution_time_human": 1800
  }
}

Метрики eval

Точность:

accuracy = correct_predictions / total_incidents

Цель: ≥ 80% (агент находит правильную первопричину в 80%+ случаев)

Доля ложных срабатываний:

false_positive_rate = wrong_predictions / total_incidents

Цель: ≤ 20% (агент ошибается в ≤ 20% случаев)

Precision (для задач с «позитивным классом»):

precision = true_positives / (true_positives + false_positives)

Recall (полнота):

recall = true_positives / (true_positives + false_negatives)

F1 score (баланс между precision и recall):

f1 = 2 * (precision * recall) / (precision + recall)

Цель: F1 ≥ 0.85

Как использовать:

  1. Baseline: запустить eval на текущем агенте → точность 80%
  2. Улучшение: добавить новую гипотезу в плейбук триажа
  3. Re-eval: снова запустить eval → точность 90%
  4. Измерение: улучшение = +10% (измеримо)

В 2014 Bill “чувствовал” улучшение. В 2026 Lance измеряет улучшение через eval-набор.

Концепция 2: golden tests — защита от регрессий#

Проблема:

Агент v1: точность 90% (18/20 инцидентов — верно).

Разработчик меняет плейбук триажа (оптимизация).

Агент v2: точность 75% (15/20 инцидентов — верно).

Регрессия: новая версия хуже предыдущей.

Как поймать регрессию?

golden tests = подмножество eval-набора, которое должно проходить без исключений.

Richard Hendricks формулирует это как простое правило ревью: — «Если golden tests красные — мы не “спорим”, мы не мёржим. Сначала возвращаем контур в зелёный».

Определение: golden tests — это критичные инциденты, которые агент обязан решать правильно.

Критерии для golden test: инцидент становится golden test, если он:

  • бизнес‑критичный: payroll, billing, пользовательский контур
  • с высокой ценой ошибки: downtime > loss
  • с регуляторными рисками: есть регуляторное влияние (GDPR, PCI DSS)
  • частый: повторяется > 3 раз/год

Пример набора golden tests:

{
  "golden_tests": [
    {
      "id": "GOLDEN-001",
      "incident": "INC-2024-11-22",
      "description": "Payroll‑система недоступна (миграция БД упала)",
      "reason": "Бизнес‑критично + регуляторные риски (нельзя задерживать payroll)",
      "must_pass": true
    },
    {
      "id": "GOLDEN-002",
      "incident": "INC-2024-08-10",
      "description": "Таймаут billing‑системы (payment gateway)",
      "reason": "Высокая цена ошибки (потери выручки в час выше порога)",
      "must_pass": true
    },
    {
      "id": "GOLDEN-003",
      "incident": "INC-2024-05-20",
      "description": "Пул подключений к БД исчерпан (DB connection pool exhausted)",
      "reason": "Частый (случалось 5 раз в 2024)",
      "must_pass": true
    }
  ]
}

Контрольная точка в CI/CD: перед деплоем прогоняем golden tests — все должны пройти.

# Перед деплоем: прогнать golden tests
$ python eval_agent.py --golden-only

Результаты golden tests:
GOLDEN-001: PASS (первопричина правильная)
GOLDEN-002: PASS (первопричина правильная)
GOLDEN-003: FAIL (предсказана неправильная первопричина)

ДЕПЛОЙ ЗАБЛОКИРОВАН: 1 golden test failed

# Нужно исправить до деплоя

Принцип:

Агент может регрессировать на некритичных инцидентах — это допустимо. Но не может регрессировать на golden tests: иначе деплой должен блокироваться.

В 2014 Bill не мог предотвращать регрессии (нет eval). В 2026 Lance вводит golden tests, которые блокируют деплой, если регрессия обнаружена.

Верификатор и тест-раннер: роли, которые делают качество “реальным”#

На практике проблема не только в том, что “нет тестов”. Часто тесты/чеки есть, но:

  • кто-то заявляет, что всё готово (“починил, должно работать”), а реально — нет;
  • тесты “должны запускаться”, но их не запускают стабильно или результаты никто не читает.

Два полезных паттерна для этого контура:

  • Верификатор — независимый “скептик”, который проверяет, что заявленное “готово” действительно работает: запускает релевантные проверки, ищет пропуски и краевые случаи, возвращает отчёт “что прошло / что сломано / что не проверено”.
  • Тест-раннер — исполнитель, который проактивно прогоняет нужные тесты/проверки (в т.ч. golden tests) при изменениях и доводит до зелёного состояния, не переписывая ожидания тестов под “как получилось” и не ломая их исходный смысл.

Важно: это не обязательно “отдельные модели” — это может быть отдельная роль в вашей мультиагентной системе или просто формализованный шаг в CI/CD. Ценность — в разделении ответственности: исполнитель пишет/меняет, верификатор доказывает, тест‑раннер прогоняет и фиксирует результат.

Пример реализации (как это выглядит в инструменте): многие среды позволяют настраивать специализированных исполнителей для “верификации”, “дебага” и “запуска тестов”. Например, см. раздел про эти роли в Cursor Subagents (как один из примеров реализации концепта).

Дополнение: чтобы этот контур был устойчивым, удобно упаковать “верификацию” и “прогон тестов” как Agent Skills (папки с SKILL.md): с чётким описанием “когда применять”, DoD/форматом отчёта и ссылками на команды/скрипты. Тогда проверка становится переносимой процедурой, а не разовой просьбой в чате.1

Концепция 3: Цикл улучшений — систематическое обучение#

Проблема в 2014:

Bill решил 20 инцидентов → “стало лучше” → но не знает что конкретно улучшить.

Решение в 2026: цикл улучшений

Цикл улучшений#

Шаг 1: запуск eval#

Запусти eval на текущем агенте → измерь метрики (пример):

  • Точность: 80% (16/20 correct)
  • Доля ложных срабатываний: 15% (3/20 wrong)
  • Средний TTRC: <TTRC_SECONDS>

Шаг 2: анализ провалов#

Для каждого проваленного инцидента:

  • Почему агент не нашёл первопричину?
  • Какая гипотеза пропущена?
  • Какие диагностические команды не выполнены?

Пример:

{
  "failed_incident": "INC-2024-12-30",
  "ground_truth": "нет индекса в БД",
  "predicted": "легитимная высокая нагрузка",
  "analysis": {
    "missing_hypothesis": "Проверить длительность запросов к БД + план выполнения запроса",
    "missing_diagnostics": "EXPLAIN для запроса (проверить, что используется индекс)",
    "root_cause": "Плейбук триажа не проверяет индексы БД"
  }
}

Шаг 3: внедрение исправления#

Добавь недостающую гипотезу в плейбук триажа:

Пример правки (новая гипотеза):

  • Гипотеза 4: отсутствует индекс в БД (вероятность: средняя)
  • Диагностика:
ssh db-prod-01 'sudo -u postgres psql -c "EXPLAIN ANALYZE <SLOW_QUERY>"'
  • Критерий успеха: запрос использует индекс (нет Seq Scan)
  • Если обнаружен Seq Scan:
    • первопричина: отсутствует индекс
    • исправление: создать индекс
    • остановка: завершить триаж

Шаг 4: повторный запуск eval#

Запусти eval на улучшенном агенте → измерь метрики (пример):

  • Точность: 90% (18/20 correct) — +10% улучшение
  • Доля ложных срабатываний: 10% (2/20 wrong) — -5% улучшение
  • Средний TTRC: <TTRC_SECONDS> — улучшение относительно базового уровня

Шаг 5: деплой улучшенного агента#

Если улучшение измерено → деплой в продакшен.

Шаг 6: мониторинг в продакшене#

Собирай новые инциденты → добавляй в eval-набор → возвращайся к шагу 1.

Результат: цикл непрерывных улучшений.

Метрика улучшений:

  • В 2014 Bill: нет измерений (не знал, что улучшилось)
  • В 2026 цикл улучшений у Lance: точность 80% → 90% (измеримо +10%)

Концепция 4: покрытие тестами — happy path и edge cases#

Проблема:

Eval-набор из 20 инцидентов, но все инциденты — рутинные (например, high CPU, disk full).

Агент проходит eval (точность 90%) → деплой в продакшен → падает на краевом случае (например, разрыв сети network partition или редкий дедлок в БД DB deadlock).

Решение: матрица покрытия тестами

Матрица покрытия тестами

Категории покрытия

Рутинные сценарии — happy path:

  • Высокая загрузка CPU (утечка памяти)
  • Диск заполнен (не работает ротация логов)
  • Пул подключений к БД исчерпан
  • Таймаут API из‑за медленного запроса к БД

Цель покрытия: 80% инцидентов в продакшене

Краевые случаи — edge cases: редкие, но критичные

  • Разрыв сети — network partition / split-brain
  • Дедлок в БД — DB deadlock
  • Каскадный отказ — service A → B → C
  • Инцидент безопасности — security breach

Цель покрытия: 15% инцидентов в продакшене

Неизвестное — unknown: никогда не встречалось

  • Новый тип инцидента
  • Агент должен эскалировать (не может обработать)

Цель покрытия: 5% инцидентов в продакшене

Состав eval-набора

Рекомендуемая пропорция:

{
  "eval_dataset": {
    "total": 20,
    "happy_path": 16,  // 80%
    "edge_cases": 3,   // 15%
    "unknown": 1       // 5%
  }
}

Примеры краевых случаев — edge cases:

  1. Разрыв сети — network partition:

    • Симптомы: service A не может достучаться до service B, но оба сервиса выглядят healthy
    • Первопричина: неверно настроенные правила сети/egress на межсетевом экране
    • Агент должен: обнаружить сетевую проблему и эскалировать к сетевой команде
  2. Дедлок в БД — DB deadlock:

    • Симптомы: таймауты запросов, CPU БД в норме, периодические блокировки
    • Первопричина: circular dependency в транзакциях
    • Агент должен: распознать паттерн deadlock и предложить retry‑логику для транзакций
  3. Каскадный отказ — cascading failure:

    • Симптомы: несколько сервисов «падают» одновременно
    • Первопричина: цепочка зависимостей (A зависит от B, B зависит от C, C упал)
    • Агент должен: построить граф зависимостей и определить корневой сервис

Критерии успеха:

  • happy path: точность ≥ 95%
  • edge cases: точность ≥ 70%
  • unknown: доля эскалаций = 100%

В 2014 Bill тестировал только happy path — рутинные инциденты. В 2026 покрытие тестами включает edge cases → агент становится устойчивее.

Концепция 5: continuous eval (непрерывный eval) — мониторинг качества в продакшене#

Проблема:

Eval на стейджинге: точность 90%.
Продакшен: точность 70% (хуже).

Почему?

Стейджинг ≠ продакшен:

  • Разное распределение данных (staging synthetic, production real): на стейджинге синтетика, в продакшене — реальные данные
  • Разная нагрузка (staging low traffic, production high): низкий трафик на стейджинге, высокий в продакшене
  • Разные режимы отказов (staging controlled, production chaotic): на стейджинге всё контролируемо, в продакшене — хаотично

Решение: continuous eval (непрерывный eval) в продакшене

Как это работает:

  1. Агент решает инцидент в продакшене.
  2. После инцидента человек делает ревью решения агента:
    • первопричина корректна? (да/нет)
    • исправление корректно? (да/нет)
    • TTRC приемлем? (да/нет)
  3. Инцидент добавляется в eval-набор:
{
  "incident_id": "INC-PROD-2026-01-17",
  "timestamp": "2026-01-17T02:17:00Z",
  "agent_prediction": "memory_leak",
  "human_review": {
    "root_cause_correct": true,
    "fix_appropriate": true,
    "ttrc_seconds": 180,
    "comments": "Агент корректно определил утечку памяти. Исправление сработало."
  },
  "added_to_eval": true
}
  1. Раз в неделю перезапускать eval (eval-набор растёт со временем).

Дашборд метрик#

Метрики eval в продакшене: в реальном времени

Точность (accuracy) агента (последние 30 дней): 87%
Доля ложных срабатываний (false positive rate): 8%
Средний TTRC: 6.5 минут
Доля эскалаций: 12%

Тренд: +3% (по сравнению с прошлым месяцем)

Триггеры действий:

  • Если точность < 80% в течение 7 дней → расследовать регрессию
  • Если доля ложных срабатываний > 15% → пересмотреть плейбук триажа
  • Если TTRC > 10 минут → оптимизировать диагностические команды

В 2014 Bill не мог измерять качество в продакшене (нет eval-контурa). В 2026 Lance добавляет continuous eval → мониторинг качества в реальном времени.


Практика: создание eval-набора из 20 прошлых инцидентов#

Задача#

Создать eval-набор из 20 инцидентов (2024–2025) и запустить eval агента.

Шаг 1: собрать инциденты#

Источник: логи инцидентов в продакшене (2024–2025)

Примечание: ниже — пример/псевдокод. Не запускайте команды в продакшене «как есть»: адаптируйте под вашу систему инцидентов и доступы (read‑only по умолчанию, разрешённый список команд).

# Экспортировать инциденты из системы управления инцидентами
$ sudo journalctl -u incident-tracker --since="365 days ago" > incidents_2024_2025.log

# Разобрать инциденты (извлечь incident_id, timestamp, logs, metrics, resolution)
$ python parse_incidents.py incidents_2024_2025.log > eval_dataset.json

Eval-набор (20 инцидентов):

{
  "eval_dataset": [
    {
      "incident_id": "INC-2024-01-15",
      "timestamp": "2024-01-15T03:20:00Z",
      "service": "api-gateway",
      "alert": "Высокая загрузка CPU",
      "logs": ["ERROR: OutOfMemoryError..."],
      "metrics": {"cpu": 85, "memory": 92},
      "ground_truth": {
        "root_cause": "memory_leak_reports_endpoint",
        "fix": "restart_deployment",
        "resolution_time_human_seconds": 1800
      }
    },
    // ... 19 more incidents
  ]
}

Шаг 2: запустить eval#

Eval script:

# eval_agent.py

import json
import time

def run_eval(agent, eval_dataset):
    results = []
    
    for incident in eval_dataset:
        print(f"Проверяем {incident['incident_id']}...")
        
        # Запустить triage агента
        start_time = time.time()
        prediction = agent.triage(
            logs=incident['logs'],
            metrics=incident['metrics']
        )
        ttrc = time.time() - start_time
        
        # Сравни с эталоном
        correct = (prediction['root_cause'] == incident['ground_truth']['root_cause'])
        
        results.append({
            'incident_id': incident['incident_id'],
            'ground_truth': incident['ground_truth']['root_cause'],
            'predicted': prediction['root_cause'],
            'correct': correct,
            'ttrc_seconds': ttrc
        })
    
    # Посчитать метрики
    accuracy = sum(r['correct'] for r in results) / len(results)
    avg_ttrc = sum(r['ttrc_seconds'] for r in results) / len(results)
    
    return {
        'results': results,
        'metrics': {
            'accuracy': accuracy,
            'average_ttrc_seconds': avg_ttrc
        }
    }

# Запустить eval
with open('eval_dataset.json') as f:
    dataset = json.load(f)['eval_dataset']

eval_results = run_eval(agent, dataset)

print(f"Точность: {eval_results['metrics']['accuracy']:.2%}")
print(f"Средний TTRC: {eval_results['metrics']['average_ttrc_seconds']:.1f}s")

Вывод (пример):

Проверяем INC-2024-01-15... OK (верно, <TTRC_SECONDS>)
Проверяем INC-2024-02-10... OK (верно, <TTRC_SECONDS>)
Проверяем INC-2024-03-15... OK (верно, <TTRC_SECONDS>)
Проверяем INC-2024-04-20... FAIL (неверно, <TTRC_SECONDS>)
...
Проверяем INC-2025-12-30... OK (верно, <TTRC_SECONDS>)

Результаты:
Точность: 90% (18/20 верно)
Доля ложных срабатываний (false positive rate): 10% (2/20 неверно)
Средний TTRC: <TTRC_SECONDS>

Шаг 3: анализ провалов#

2 проваленных инцидента:

{
  "failures": [
    {
      "incident_id": "INC-2024-04-20",
      "ground_truth": "network_partition_between_services",
      "predicted": "service_down",
      "analysis": {
        "reason": "Агент не проверил сетевую связность между сервисами",
        "missing_diagnostic": "ping/curl между сервисами",
        "fix": "Добавить гипотезу: network partition"
      }
    },
    {
      "incident_id": "INC-2024-08-12",
      "ground_truth": "db_deadlock",
      "predicted": "db_slow_query",
      "analysis": {
        "reason": "Агент не распознал паттерн deadlock в логах БД",
        "missing_diagnostic": "парсинг логов БД по 'deadlock detected'",
        "fix": "Добавить regex-паттерн для детектирования deadlock"
      }
    }
  ]
}

Шаг 4: внедрить исправления#

Исправление 1: добавить гипотезу разрыва сети (network partition)

+### Гипотеза 5: разрыв сети — `network partition`
+
+**Диагностика:**
+```bash
+ssh service-a-01 "curl -s http://service-b/health"
+```
+
+**IF timeout:**
+  - Первопричина: network partition
+  - ESCALATE к сетевой команде

Исправление 2: добавить детектирование deadlock

+# Проверить логи БД на паттерн deadlock
+if "deadlock detected" in db_logs:
+    root_cause = "db_deadlock"
+    fix = "restart transactions + review transaction isolation"

Шаг 5: повторный запуск eval#

Точность: 95% (19/20 верно) — +5% улучшение
Средний TTRC: <TTRC_SECONDS> — улучшение относительно базового уровня

Оставшийся провал: INC-2024-11-05 (unknown edge case)

Шаг 6: создать golden tests#

Выбери 5 критичных инцидентов как golden tests:

{
  "golden_tests": [
    "INC-2024-01-15",  // High CPU (часто)
    "INC-2024-03-20",  // Payroll down (business-critical)
    "INC-2024-05-10",  // Billing timeout (high cost)
    "INC-2024-07-22",  // DB connection pool (часто)
    "INC-2024-09-15"   // Disk full (часто)
  ]
}

Контрольная точка в CI/CD:

# Перед деплоем
$ python eval_agent.py --golden-only

Результаты golden tests:
Все 5 golden tests PASSED

ДЕПЛОЙ РАЗРЕШЁН

Время выполнения:

  • В 2014 Bill: нет eval setup (не измерял качество)
  • В 2026 настройка eval у Lance: 2 часа (собрать инциденты, написать eval script) → переиспользуемо для всех будущих прогонов

Типовые ошибки#

Ошибка 1: Eval только на happy path → агент падает на edge cases#

Сценарий:

Eval-набор из 20 инцидентов — все рутинные (например, high CPU, disk full).
Точность агента 95% → деплой в продакшен → падает на network partition (не было в eval).

Проблема: eval-набор не репрезентативен (нет edge cases).

Вывод:

В 2014 Parts Unlimited тестировали Phoenix Project на стейджинге только на рутинных сценариях. Продакшен выявил edge cases → серия сбоев.

В 2026 Lance фиксирует правило: eval-набор должен включать edge cases:

Состав eval-набора (рекомендуемая пропорция):

  • happy path: 80% (например, high CPU, disk full)
  • edge cases: 15% (например, network partition, deadlock, cascading failure)
  • unknown: 5% — агент должен эскалировать

Примеры краевых случаев (edge cases), которые стоит включить:

  1. Network partition (2 incidents)
  2. DB deadlock (1 incident)
  3. Cascading failure (1 incident)

Проверка: точность агента на edge cases ≥ 70% + доля эскалаций по unknown = 100%.

Как избежать:

Чеклист eval-набора:

  • 80% — happy path
  • 15% — edge cases
  • 5% — unknown (эскалация)
  • Репрезентативность распределению инцидентов в продакшене

Ошибка 2: golden tests не обновляются → регрессия не ловится#

Сценарий:

golden tests создали в 2024 (5 инцидентов).
В 2025 появился новый критичный тип инцидентов (например, падение хоста/юнита systemd или проблема с BGP‑анонсом VIP).
Агент v2 задеплоили → падает на “VIP не анонсируется / агент недоступен” → этого не было в golden tests.

Проблема: golden tests статичны, а продакшен меняется.

Вывод:

В 2014 Parts Unlimited CAB checklist был статичным и не обновлялся годами (создан в 2012). В 2014 началась миграция в облако → появились новые режимы отказов → checklist устарел.

В 2026 Lance фиксирует правило: golden tests должны эволюционировать:

Поддержка golden tests#

Квартальный обзор (каждые 3 месяца):

  1. Проанализировать инциденты в продакшене за последние 3 месяца
  2. Найти новые критичные типы инцидентов
  3. Добавить в golden tests, если:
    • бизнес‑критично, или
    • высокая цена ошибки (> ), или
    • часто (> 3 раза за квартал)

Пример:

Q1 2025: 3 инцидента “VIP не анонсируется / хост умер” — критично → добавить в golden tests Q2 2025: 5 проблем с инвалидацией Redis‑кэша — часто → добавить в golden tests

Результат: golden tests растут со временем: 5 → 7 → 10 (репрезентативно текущему продакшену).

Как избежать:

Чеклист поддержки golden tests:

  • Квартальный обзор запланирован
  • Новые критичные инциденты выявлены
  • golden tests обновлены: добавлены новые, удалены устаревшие
  • Контрольная точка в CI/CD включает обновлённые golden tests

Ошибка 3: Eval не запускается перед deployment → регрессия уезжает в продакшен#

Сценарий:

Разработчик меняет плейбук триажа → коммитит → деплоит без eval → в продакшене точность агента падает 90% → 75%.

Проблема: нет CI/CD gate для eval.

Вывод:

В 2014 Wes Davis деплоил без тестирования (Cowboy change) → авария в продакшене.

В 2026 Lance закрепляет: eval обязателен в CI/CD:

# .github/workflows/deploy.yml

name: Deploy Agent

on:
  push:
    branches: [main]

jobs:
  eval:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@v4

      - name: Setup Python
        uses: actions/setup-python@v5
        with:
          python-version: "3.11"

      # Если у вас есть зависимости:
      # - name: Install dependencies
      #   run: pip install -r requirements.txt

      - name: Run Eval
        run: |
          python eval_agent.py --full-dataset
          
      - name: Check Metrics
        run: |
          python - <<'PY'
          import json
          with open("eval_results.json", "r", encoding="utf-8") as f:
              data = json.load(f)
          accuracy = float(data["metrics"]["accuracy"])
          threshold = 0.80
          if accuracy < threshold:
              raise SystemExit(f"Eval FAILED: accuracy {accuracy:.4f} < {threshold:.2%}")
          print(f"Eval PASSED: accuracy {accuracy:.4f} >= {threshold:.2%}")
          PY
      
      - name: Golden Tests
        run: |
          python eval_agent.py --golden-only
          # All golden tests must pass (exit 0)

  deploy:
    needs: eval  # Deploy only if eval passed
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@v4

      - name: Install Ansible
        run: |
          python -m pip install --upgrade pip
          pip install "ansible-core>=2.16"

      # В реальном деплое здесь понадобятся секреты/ключи/known_hosts и окружение.
      - name: Dry-run Deploy (required)
        run: |
          ansible-playbook -i inventories/production playbooks/agent.yml \
            --tags deploy \
            --check --diff \
            --extra-vars "agent_version=v2"

      - name: Deploy to Production
        run: |
          ansible-playbook -i inventories/production playbooks/agent.yml \
            --tags deploy \
            --extra-vars "agent_version=v2"

Как избежать:

CI/CD gate checklist:

  • Eval запускается автоматически на каждый коммит
  • Деплой блокируется, если точность ниже порога
  • golden tests должны проходить без исключений
  • Метрики логируются для отслеживания динамики

Резюме#

Что мы сделали#

Создали системный способ измерять качество агента:

  1. Eval-набор: 20 прошлых инцидентов (логи + метрики + эталон)
  2. Метрики: точность, доля ложных срабатываний, средний TTRC
  3. golden tests: 5 критичных инцидентов, которые должны проходить (защита от регрессий)
  4. Цикл улучшений: измеряем → разбираем провалы → исправляем → измеряем снова
  5. Непрерывный eval: инциденты из продакшена добавляются в eval-набор (он растёт со временем)

Артефакты#

  • Шаблон eval-набора (структура записи инцидента)
  • Скрипт для eval (посчитать метрики: точность, доля ложных срабатываний, TTRC)
  • Определение golden tests (критерии + контрольная точка в CI/CD)
  • Процесс цикла улучшений (измеряем → анализируем → исправляем → измеряем снова)
  • Матрица покрытия тестами: happy path 80%, edge cases 15%, unknown 5%

Ключевые принципы#

В 2014: Bill Palmer «чувствовал», что стало лучше. Нет метрик → нет доказательства и нет системного улучшения.

В 2026: eval-набор делает качество измеримым. Точность 80% → улучшения → 90% → 95%. Каждое улучшение можно проверить.

Главное:

  • Eval-набор = эталон (известные первопричины)
  • Метрики = измеримое качество (не “чувствуем”)
  • Цикл улучшений = системное обучение (не случайная «проба‑ошибка»)

Метрики успеха:

  • Bill Palmer (2014): измерение качества субъективно, отслеживания нет, защиты от регрессий нет
  • С eval-набором (2026): точность 90%, TTRC <TTRC_SECONDS>; отслеживание улучшений есть; golden tests блокируют деплой при регрессии

Критерии приёмки главы#

Вы успешно освоили материал, если можете:

Уровень 1: Понимание

  • Объяснить, что такое eval-набор и зачем он нужен
  • Объяснить разницу между точностью и долей ложных срабатываний
  • Перечислить 3 типа инцидентов в eval-наборе: happy path, edge cases, unknown

Уровень 2: Применение

  • Создать eval-набор из 5–10 прошлых инцидентов
  • Написать скрипт для eval (запуск агента на eval-наборе + расчёт метрик)
  • Определить golden tests (3–5 критичных инцидентов)

Уровень 3: Воспроизводимость

  • Eval успешно запущен (метрики посчитаны)
  • Провалы проанализированы (первопричина определена)
  • Улучшения внесены, повторный eval показывает улучшение

Уровень 4: Интеграция в продакшен

  • Eval интегрирован в CI/CD (запускается перед деплоем)
  • golden tests блокируют деплой, если не пройдены
  • Continuous eval в продакшене (реальные инциденты добавляются в eval-набор)
  • Цикл улучшений работает (точность растёт со временем)

Следующие шаги#

Глава 9: Команда агентов + управление практикой — как масштабировать от 1 агента к команде агентов: Analyst/Triage/SRE/Orchestrator.

Связь с Главой 8: Eval измеряет качество 1 агента. Но что если задача требует координации нескольких агентов? Глава 9 покажет, как организовать команду.



  1. Открытый формат “Agent Skills”: skills как переносимые пакеты знаний и принцип progressive disclosure: Agent Skills Overview↩︎