# ГЛАВА 14. ОЦЕНКА КАЧЕСТВА LLM-СИСТЕМ: EVALS, ТЕСТОВЫЕ НАБОРЫ И РЕГРЕССИИ

---

В 2005 году инженер-новичок мог выпустить код без единого теста и сказать: «у меня на машине работает». В 2015-м такое было уже неприлично — CI/CD, юнит-тесты, линтеры стали частью ремесла. В 2026 году LLM-системы находятся на том же перекрёстке. Команды меняют промпт, переключают модель, обновляют retriever — и оценивают результат фразой «вроде стало лучше». Это тот самый «works on my machine» для эпохи фронтирных моделей.

**Evals — это юнит-тесты LLM-инженерии.** Не в метафорическом смысле: eval-набор фиксирует ожидаемое поведение, автоматически прогоняется при каждом изменении и блокирует деплой, если качество упало. Без evals вы ведёте машину с заклеенным спидометром — может быть, хорошо, а может быть, не очень.

В предыдущих главах мы уже касались отдельных элементов оценки: агентные бенчмарки ([Глава 10](10_agent_not_chat.md), §10.7), eval-метрики для RAG ([Глава 12](12_rag.md), §12.8), CoVe как верификация на лету ([Глава 13](13_anti_hallucination_loop.md)), LDD как инфраструктура для сбора eval-данных из production ([Глава 13](13_anti_hallucination_loop.md), §13.3). Эта глава собирает разрозненные элементы в **единую eval-дисциплину** — от golden dataset до regression gate в CI/CD.

---

## 14.1. Зачем нужна eval-дисциплина

### Проблема: оценка «на глаз»

Типичный сценарий: техлид просит «улучшить промпт для суммаризации». Инженер меняет системный промпт, прогоняет три примера вручную, видит, что ответы стали длиннее и подробнее, и деплоит. Через неделю приходят жалобы: модель начала галлюцинировать имена авторов. Три ручных примера не покрывали этот кейс.

Без evals вы не можете:
- **Детектировать регрессии**: изменение, улучшившее одну метрику, может ухудшить другую.
- **Сравнивать эксперименты**: «промпт A vs промпт B» без общего набора данных — субъективная оценка.
- **Обосновать выбор модели**: «Claude лучше GPT для нашей задачи» — голословно, если нет числа.

### Eval как контракт

Каждый eval определяет **«что значит хорошо»** для конкретной задачи. Это контракт между командой и системой — аналог интерфейса в typed-языке. Контракт содержит:

1. **Входные данные** — набор примеров (golden dataset).
2. **Ожидаемое поведение** — метрики и пороги (faithfulness ≥ 0.85, format validity = 100%).
3. **Способ проверки** — автоматический скоринг (LLM-judge, regex, schema validation).

### Три уровня оценки

| Уровень | Когда | Что проверяет | Пример |
|---------|-------|--------------|--------|
| **Offline** | До деплоя | Качество на фиксированном наборе | CI/CD regression gate |
| **Online** | В production | Качество на реальном трафике | Мониторинг faithfulness в дашборде |
| **Human review** | По триггеру | Экспертная оценка сложных кейсов | Энтропия LLM-judge > порог → эскалация |

Offline evals ловят грубые регрессии до того, как пользователи их увидят. Online evals обнаруживают drift — медленное ухудшение, незаметное на статичном наборе. Human review замыкает контур: ошибки, пойманные людьми, возвращаются в golden dataset.

---

## 14.2. Golden dataset: строительный материал evals

### Структура golden dataset

Golden dataset — это набор троек `(input, expected_output, context)`, где:

- **input** — запрос пользователя или входные данные для пайплайна.
- **expected_output** — эталонный ответ или набор допустимых ответов.
- **context** — (опционально) документы, которые модель должна использовать (для RAG-сценариев).

Формат хранения — JSONL (каждая строка — один пример). Каждый пример содержит поля: `input`, `expected_output`, `context`, а также метаданные — `tags` (категория теста: factual, edge_case, multi-turn) и `difficulty`.

### Как собирать: правило 50–100

Начните с **50–100 примеров** для каждого core use case. Это минимум, позволяющий получить осмысленную статистику (см. §14.9). Источники:

1. **Ручная курация** — эксперт пишет примеры, покрывающие основные сценарии и edge-кейсы. Это самый дорогой, но самый точный способ.
2. **Production-логи** — берёте реальные запросы из LDD-логов ([Глава 13](13_anti_hallucination_loop.md), §13.3), фильтруете по success/failure, добавляете эталонные ответы. Это feedback loop: production → golden dataset → eval → production.
3. **Синтетическая генерация** — используете LLM для создания вариаций существующих примеров. DeepEval и RAGAS поддерживают генерацию синтетических тестовых данных из источников (документов, FAQ).

### Версионирование

Golden dataset эволюционирует вместе с продуктом. Новые кейсы появляются, старые становятся нерелевантными. **Версионируйте golden dataset рядом с промптами** — в том же репозитории, в том же коммите. Diff промпта без diff'а eval-набора — полуслепое изменение.

```
prompts/
├── summarization_v3.txt
├── summarization_v4.txt     # новая версия промпта
evals/
├── summarization_golden_v3.jsonl
├── summarization_golden_v4.jsonl  # обновлённый eval-набор
```

### Анти-паттерн: overfitting на eval-данные

Если вы используете golden dataset для итеративной подстройки промпта, вы рискуете **overfitting'ом**: промпт оптимизирован под конкретные 100 примеров, а не под задачу в целом. Решение: разделите набор на **dev-split** (для итерации) и **test-split** (для финальной оценки). Тестовый split трогается только один раз перед деплоем — точно как в ML.

---

## 14.3. Метрики: что измерять

### Метрики по типам задач

| Тип задачи | Ключевые метрики | Инструмент / источник |
|------------|------------------|----------------------|
| Open-ended QA | G-Eval, Faithfulness, Answer Relevancy | DeepEval, RAGAS |
| RAG | Context Precision, Context Recall, Faithfulness | RAGAS (см. [Главу 12](12_rag.md)) |
| Кодогенерация | pass@k | Chen et al. 2021 |
| Классификация / Извлечение | Exact match, F1, Precision, Recall | Стандартные ML-метрики |
| Суммаризация | Factuality, Conciseness (G-Eval с кастомными критериями) | Liu et al. 2023 |

**Правило**: минимум **одна LLM-judge метрика + одна детерминированная метрика** на каждый eval-прогон. LLM-judge ловит семантические проблемы (ответ не по теме, неполный). Детерминированная метрика ловит структурные (невалидный JSON, превышение длины, отсутствие обязательных полей).

### Классические NLP-метрики: почему их недостаточно

BLEU, ROUGE и BERTScore долго были стандартом для оценки генерации текста. Для современных LLM-систем их **недостаточно** по трём причинам:

1. **Низкая корреляция с человеческой оценкой на открытых задачах.** BLEU измеряет n-gram overlap — два семантически эквивалентных, но по-разному сформулированных ответа получат низкий BLEU.
2. **Неприменимость к задачам с множественными правильными ответами.** На вопрос «Объясни, что такое attention» существует бесконечно много хороших ответов.
3. **Отсутствие оценки фактуальности.** ROUGE не различает грамотный текст с правильными фактами и грамотный текст с галлюцинациями.

Используйте BLEU/ROUGE/BERTScore как **baseline** или для задач с жёстким reference (перевод, извлечение данных), но не как основную метрику для open-ended генерации.

### Пример: комбинированный eval для RAG-пайплайна

Комбинированный eval объединяет детерминированную проверку формата (валидность JSON, наличие обязательных полей) и LLM-judge метрики (faithfulness ≥ 0.85, answer relevancy ≥ 0.8, conciseness через G-Eval с кастомными критериями). Все метрики запускаются за один прогон.

> **Промпт для генерации:** «Напиши Python-скрипт с использованием DeepEval, который: 1) проверяет формат JSON-ответа на наличие полей "answer" и "sources"; 2) оценивает faithfulness (порог 0.85), answer relevancy (порог 0.8) и conciseness через G-Eval (порог 0.7); 3) запускает все метрики через `evaluate()` на одном test case с retrieval context. Модель-судья — GPT-5.4.»

---

## 14.4. LLM-as-Judge

### Принцип: модель оценивает модель

LLM-as-Judge — подход, при котором сильная модель оценивает output другой модели. Это как рецензирование в науке: автор (generator) пишет статью, рецензент (judge) оценивает качество. Рецензент не обязан быть соавтором — он должен быть **компетентным и незаинтересованным**.

Zheng et al. (2023) показали, что GPT-4 как judge достигает **>80% согласия с человеческими оценками** на MT-Bench — сопоставимо с inter-annotator agreement между людьми-экспертами. Это превратило LLM-as-Judge из эксперимента в production-инструмент.

### G-Eval: CoT + form-filling

**G-Eval** (Liu et al., 2023) — метод, объединяющий chain-of-thought рассуждение с оценкой по заданным критериям. Судья получает набор критериев и инструкцию сначала пошагово обосновать оценку (CoT), затем — выставить числовой балл. G-Eval показал лучшую корреляцию с человеческими оценками на задачах суммаризации по сравнению с предшествующими автоматическими метриками.

Промпт G-Eval для оценки суммаризации:

```
You will be given a source document and a summary.

Evaluation criteria:
- Coherence (1-5): Is the summary well-organized and logically structured?
- Consistency (1-5): Does the summary contain only facts from the source?
- Fluency (1-3): Is the summary grammatically correct and readable?
- Relevance (1-5): Does the summary capture the key information?

Steps:
1. Read the source document carefully.
2. Read the summary.
3. For each criterion, provide a brief justification.
4. Assign a score for each criterion.

Output format: JSON with "coherence", "consistency", "fluency", "relevance" keys.
```

### Известные bias'ы LLM-judge

Использовать модель как судью — мощно, но не безопасно без калибровки. Три основных bias'а задокументированы в литературе:

**1. Position bias.** Порядок ответов влияет на оценку. Wang et al. (2023) показали, что при pairwise comparison результат существенно зависит от порядка: модель, чей ответ стоит первым, систематически получает предпочтение. Судья непропорционально предпочитает первый (или последний) ответ.

**2. Verbosity bias.** Судья предпочитает более длинные ответы, даже если короткий ответ точнее и полнее. Длина воспринимается как «полнота».

**3. Self-enhancement bias.** Модель предпочитает собственные ответы. Если GPT-5.4 оценивает output GPT-5.4 vs Claude Opus 4.6, есть систематический сдвиг в пользу своих текстов.

### Стратегии калибровки

**Balanced Position.** Запускайте каждое pairwise сравнение дважды — с обоими порядками ответов. Агрегируйте: если результат меняется при смене порядка — помечайте как tie. Если A побеждает в обоих позициях — победа надёжна.

> **Промпт для генерации:** «Напиши асинхронную Python-функцию `balanced_pairwise(judge, answer_a, answer_b)`, которая запускает pairwise comparison в обоих порядках (A/B и B/A). Если одна сторона побеждает в обоих случаях — возвращай победителя, иначе — tie (обнаружен position bias).»

**Multiple Evidence.** Генерируйте несколько rationale перед выставлением оценки. Среднее по нескольким «мнениям» одной модели более стабильно, чем единичное (аналог self-consistency из [Главы 8](08_multiple_hypotheses.md)).

**Human-in-the-Loop.** Вычисляйте entropy оценок судьи. Если для конкретного примера оценки нестабильны (высокая энтропия при multiple evidence) — маршрутизируйте к человеку. Судья обрабатывает 90% случаев, человек — оставшиеся 10% сложных.

### Анти-паттерн: circular validation

Использовать **одну и ту же модель как генератор и как судью** — circular validation. Модель склонна подтверждать собственные ответы (self-enhancement bias). Правило: судья должен быть **другой моделью** или **более сильной версией** того же семейства. В production-пайплайне 2026 года типичная пара: generator = GPT-5.3 Instant (быстрый, дешёвый), judge = Claude Opus 4.6 (точный, дорогой).

---

## 14.5. Pairwise comparison и Elo-ranking

### Chatbot Arena: 240K+ голосов

Chatbot Arena (lmarena.ai) — де-факто стандарт для сравнения LLM по человеческим предпочтениям. Пользователи получают анонимные ответы двух моделей и выбирают лучший. В оригинальной публикации (Chiang et al., 2024) платформа насчитывала свыше 240 000 голосов; к 2026 году объём значительно вырос. Это самая масштабная crowd-sourced оценка LLM в мире.

Почему pairwise comparison надёжнее абсолютных оценок? Людям сложно дать стабильную оценку по 5-балльной шкале: один эксперт ставит 4, другой — 3 за один и тот же ответ. Но при выборе «A лучше B» согласие экспертов значительно выше. Сравнение — более естественная операция для человеческого суждения.

### Elo и Bradley-Terry

Результаты pairwise-сравнений агрегируются в рейтинг через **Elo** — систему, изначально разработанную для шахмат. Интуиция простая: чем больше разрыв в рейтинге между двумя моделями, тем ожидаемее победа сильной стороны и тем меньше меняется рейтинг после такой победы. Если же побеждает аутсайдер, рейтинг сдвигается заметно сильнее.

**Bradley-Terry** — статистически более строгая модель с той же базовой идеей: у каждой модели есть скрытая «сила», и вероятность победы определяется сравнением сил двух участников. Bradley-Terry удобен тем, что даёт confidence intervals для рейтингов — критически важно при малом количестве сравнений.

### Production-применение: A/B-тестирование промптов

Тот же принцип — в вашем проекте. Вместо абсолютных оценок промптов спрашивайте: «какой из двух ответов лучше?» Это работает как для человеческой оценки, так и для LLM-as-Judge.

> **Промпт для генерации:** «Напиши асинхронный Python-скрипт для pairwise eval двух версий промпта (v3 и v4). Для каждого примера из golden dataset сгенерируй ответы обеих версий, передай их LLM-judge для сравнения по критериям accuracy, completeness, conciseness. Подсчитай wins/ties и выведи процент побед каждой версии.»

### Evalica: open-source toolkit для pairwise ranking

**Evalica** (Ustalov, COLING 2025) — библиотека на Python и Rust для агрегации pairwise-сравнений. Реализует Elo, Bradley-Terry, PageRank и другие алгоритмы ранжирования. Для небольших eval-наборов — достаточно Elo; для статистической строгости — Bradley-Terry с confidence intervals.

> **Промпт для генерации:** «Напиши Python-скрипт, который загружает CSV с pairwise-результатами (столбцы: winner, loser, tie) через библиотеку Evalica, вычисляет Bradley-Terry ранжирование и выводит рейтинг моделей.»

---

## 14.6. Failure taxonomy

### Девять категорий ошибок

Без классификации ошибок eval — это просто число. «Faithfulness = 0.72» — и что? Какие именно ошибки составляют те 28%? Failure taxonomy превращает число в actionable insights: вы видите, что 15% — hallucinations, 8% — format failures, 5% — retrieval failures, и знаете, что чинить в первую очередь.

| Категория | Описание | Метод детекции |
|-----------|----------|---------------|
| **Hallucination** | Фактически неверный контент | Faithfulness, Factuality |
| **Format non-compliance** | Невалидный JSON/XML/schema | Schema validation, regex |
| **Instruction non-following** | Игнорирование части инструкций | G-Eval с кастомными критериями |
| **Retrieval failure** | Неверный или неполный контекст | Context Precision, Context Recall |
| **Attribution error** | Ответ не подтверждается источниками | Faithfulness, entity recall |
| **Safety violation** | Токсичный, вредный, предвзятый контент | Moderation API, LLM Guard |
| **Verbosity / compression** | Слишком длинный или слишком короткий ответ | Token count, Conciseness |
| **Reasoning error** | Неверная цепочка рассуждений | pass@k, CoT verification |
| **Multi-turn inconsistency** | Противоречия между репликами в диалоге | Conversational metrics |

### Как использовать таксономию

1. **Тегируйте ошибки** в golden dataset — каждый пример с `"expected_failure_types"`.
2. **Классифицируйте отказы** при eval-прогоне — не просто «fail», а «fail: hallucination».
3. **Трекайте распределение** ошибок по категориям во времени. Если после обновления промпта hallucinations снизились, но instruction non-following вырос — это не улучшение, а перераспределение.
4. **Приоритизируйте** по impact: safety violation > hallucination > format non-compliance > всё остальное.

### Пример: тегирование при eval-прогоне

Алгоритм классификации: для каждого eval-результата проверяются пороги по метрикам (faithfulness < 0.7 → hallucination, невалидная schema → format non-compliance, answer relevancy < 0.5 → instruction non-following, длина > 2× ожидаемой → verbosity) и формируется список категорий ошибок.

> **Промпт для генерации:** «Напиши Python-функцию `classify_failure(test_case, actual_output, metrics)`, которая возвращает список категорий ошибок из таксономии (hallucination, format_non_compliance, instruction_non_following, verbosity и др.) на основе пороговых значений метрик. Также создай dataclass `EvalResult` с полями: test_case_id, passed, score, failure_categories, details.»

---

## 14.7. Eval-фреймворки: ландшафт 2026

### Обзор инструментов

К 2026 году eval-фреймворки стали зрелой категорией. Выбор зависит от задачи: RAG-specific eval, general-purpose eval + CI/CD, pairwise ranking.

| Фреймворк | Язык | Фокус | Stars (GitHub) | Ключевая особенность |
|-----------|------|-------|----------------|---------------------|
| **Promptfoo** | TypeScript | Red-team + eval | ~20K | CI/CD gates, YAML-конфиг. Стал частью OpenAI (март 2026) |
| **DeepEval** | Python | Full eval | ~14.7K | Pytest-интеграция, G-Eval, DAG, agentic metrics |
| **RAGAS** | Python | RAG eval | ~13.3K | Context precision/recall, faithfulness |
| **Braintrust (Autoevals)** | Python/JS | Eval + observability | — | LLM-as-judge, heuristic, statistical scorers |
| **Evalica** | Python/Rust | Pairwise ranking | 62 | Elo, Bradley-Terry, PageRank. Быстрый (Rust core) |

### Promptfoo

Самый популярный eval-фреймворк общего назначения. Конфигурация через YAML, встроенная поддержка CI/CD gates, red-teaming (генерация adversarial-примеров). В марте 2026 года стал частью OpenAI, что не помешало ему остаться open-source. Подходит для команд, которым нужен eval + red-team в одном инструменте.

Репозиторий: https://github.com/promptfoo/promptfoo

### DeepEval

Python-native eval-фреймворк с интеграцией в pytest — eval-тесты запускаются так же, как обычные тесты. Поддерживает G-Eval, faithfulness, answer relevancy, DAG-based metrics для агентных пайплайнов. Запуск через `deepeval test run test_eval.py` — exit code = количество failures, что даёт CI/CD gate из коробки. Идеален для Python-команд, которые хотят evals как часть тестового пайплайна.

Репозиторий: https://github.com/confident-ai/deepeval

### RAGAS

Специализированный фреймворк для оценки RAG-пайплайнов. Реализует context precision, context recall, faithfulness, answer relevancy. Подробно разобран в [Главе 12](12_rag.md), §12.8. Для RAG-системы RAGAS — обязательный инструмент.

Репозиторий: https://github.com/vibrantlabsai/ragas

### Braintrust (Autoevals)

Библиотека eval-scorer'ов от Braintrust: LLM-as-judge, heuristic (Levenshtein, JSON diff), statistical. Лёгкий, без фреймворк-оверхеда — можно интегрировать отдельные scorer'ы в любой пайплайн.

Репозиторий: https://github.com/braintrustdata/autoevals

### Evalica

Минималистичная библиотека для pairwise ranking, описанная в §14.5. Rust-core обеспечивает скорость на больших наборах сравнений.

Репозиторий: https://github.com/dustalov/evalica

---

## 14.8. Regression gates: evals в CI/CD

### Паттерн: prompt change → eval → gate → deploy

Regression gate — это автоматическая проверка качества, блокирующая деплой при падении метрик. Точно как CI/CD pipeline не пропускает код с красными тестами, regression gate не пропускает промпт с упавшим faithfulness.

```
[Prompt change] → [Git push] → [CI: run evals] → [JSON results]
                                                      │
                                               ┌──────┴──────┐
                                               │             │
                                          [Pass ✓]     [Fail ✗]
                                               │             │
                                          [Deploy]    [Block + alert]
```

### Три стратегии gate'ов

**1. Hard fail.** Любой провалившийся тест блокирует деплой. Подходит для safety-критичных кейсов (медицина, финансы). Строго, но хрупко — один flaky-тест блокирует всё.

**2. Threshold.** Pass rate < X% блокирует деплой. Например: «не менее 90% test cases проходят faithfulness ≥ 0.8». Устойчивее к шуму, но требует калибровки порога.

**3. Comparison.** Текущий эксперимент сравнивается с baseline (предыдущая версия). Блокировка, если метрики ухудшились статистически значимо. Самая надёжная стратегия, но требует хранения baseline-результатов.

### GitHub Actions: Promptfoo

Общий паттерн CI/CD gate: при изменении файлов в `prompts/` или `evals/` запускается eval-прогон, результат парсится, при наличии failures — PR блокируется.

> **Промпт для генерации:** «Напиши GitHub Actions workflow (YAML), который при pull request с изменениями в `prompts/**` или `evals/**` устанавливает Promptfoo, запускает `npx promptfoo eval --output results.json`, парсит количество failures через jq и блокирует PR при failures > 0. API-ключ — из GitHub Secrets.»

### DeepEval: pytest-интеграция

DeepEval работает через pytest: команда `deepeval test run tests/test_eval.py` возвращает exit code, равный количеству провалившихся тестов. Стандартный pytest + CI/CD = regression gate без дополнительной обвязки.

> **Промпт для генерации:** «Добавь в существующий GitHub Actions workflow шаг, запускающий `deepeval test run` для файлов `tests/test_eval.py`. Exit code != 0 должен блокировать PR.»

### Анти-паттерн: evals только в production

Запускать evals только на production-трафике — значит ждать, пока регрессия дойдёт до пользователей. Evals в CI/CD ловят проблему **до** деплоя. Production evals — дополнительный слой, а не замена.

---

## 14.9. Статистическая значимость

### Проблема малых выборок

С eval-набором из 30 примеров вы получаете «90% accuracy». Звучит хорошо. Но какова реальная точность? На малых выборках разброс слишком широк, чтобы принимать серьёзные решения по одному числу.

Если наблюдаемое качество равно 90%, картина примерно такая:

| Размер eval-набора | Примерный 95% диапазон | Что это значит |
|--------------------|------------------------|----------------|
| 30 примеров | ~79–100% | Разброс слишком широкий, gate по такому числу хрупкий |
| 100 примеров | ~84–96% | Уже можно принимать решения, но пороги стоит ставить аккуратно |
| 200 примеров | ~86–94% | Достаточно узкий диапазон для CI/CD gate и финальной валидации |

### Практические рекомендации

| Контекст использования | Минимум примеров | Почему |
|-----------------------|-----------------|--------|
| Локальная итерация (dev) | 50 | Быстрая обратная связь, допустим широкий CI |
| CI/CD regression gate | 100–200 | Actionable CI, блокировка по порогу |
| Финальная валидация / A/B-тест | 200+ | Узкий CI, статистическая значимость |

### Сравнение двух промптов

Для сравнения двух промптов (A vs B) используйте McNemar's test или paired proportions test. При 100 примерах разница в 5% (90% vs 85%) может быть незначимой — нужно 200+ примеров, чтобы различить с уверенностью 95%.

**Правило большого пальца**: если вы не можете позволить себе 100+ примеров — используйте pairwise comparison (§14.5) вместо абсолютных метрик. Pairwise более чувствителен к различиям при малых выборках.

---

## 14.10. Trace-level и workflow-level оценка агентов

Всё, что описано выше (golden sets, LLM-as-Judge, метрики, regression gates), оценивает *результат*: текст ответа, извлечённые факты, классификацию. Агентные системы требуют другого уровня оценки — *поведения*. Агент может вернуть правильный финальный ответ, но использовать неправильные инструменты, нарушить workflow-политику или потратить десять шагов на то, что решается за два. OpenAI Evals (2025–2026) формализует traces и graders как отдельную поверхность оценки.

### Что такое trace в контексте eval

Trace — полная запись поведения агента: последовательность LLM-вызовов, tool calls, решения о маршрутизации, handoff'ы между агентами, промежуточные результаты. В классическом eval оценивается пара input → output. В trace eval оценивается цепочка input → [step₁, step₂, …, step_n] → output.

Trace eval позволяет оценить четыре измерения, невидимых для output-only подхода:

1. **Эффективность** — сколько шагов потребовалось и сколько было оптимально.
2. **Корректность маршрута** — правильные ли инструменты выбрал агент на каждом этапе.
3. **Соответствие политике** — не нарушил ли агент workflow constraints (порядок действий, обязательные approval checkpoints).
4. **Качество tool use** — правильные ли параметры передал, получил ли ожидаемый результат от инструмента.

### Graders для агентных trace'ов

| Grader | Что оценивает | Пример |
|--------|--------------|--------|
| Tool choice correctness | Правильный ли инструмент выбран на каждом шаге | Агент вызвал search вместо calculator для арифметики → штраф |
| Handoff correctness | Корректность передачи между агентами в multi-agent системе | Задача передана triage-агенту вместо specialist → штраф |
| Step efficiency | Количество шагов относительно оптимального | Задача решена за 8 шагов при оптимуме 3 |
| Policy compliance | Соответствие workflow-правилам | Агент сделал внешний API-вызов без approval checkpoint → нарушение |
| Environment outcome | Результат действий в среде (не только текст) | Файл создан, тест прошёл, ticket закрыт |
| Long-horizon success | Конечный результат задачи, требующей десятков шагов | Код-агент: PR merged и тесты зелёные |

### Workflow policy violations

В production агент работает в рамках политик: какие инструменты доступны, какие действия требуют подтверждения, в каком порядке должны вызываться этапы. Policy violation — это не ошибка модели в привычном смысле, а нарушение контракта. Пример: агент отправил email клиенту без прохождения через approval-этап.

Eval для policy violations строится как проверка trace на соответствие workflow DAG. Каждый шаг агента — узел графа; допустимые переходы — рёбра. Если шаг выполнен вне допустимого порядка или без required precondition — это violation, и grader фиксирует нарушение.

### Практическая реализация

Trace eval требует structured logging: каждый шаг агента записывается с типом (`llm_call`, `tool_call`, `handoff`, `decision`), входными и выходными данными, timestamp. Grader может быть двух типов:

- **Rule-based** — определить состояния workflow как конечный автомат, проверить trace на допустимые переходы, наличие обязательных этапов, ограничения на количество шагов. Быстро, детерминированно, но хрупко при изменении workflow.
- **LLM-based** — модель-судья получает trace как структурированный лог и оценивает по критериям (эффективность, корректность маршрута, policy compliance). Гибче, но дороже и с inherent стохастичностью.

На практике оба типа комбинируются: rule-based graders проверяют hard constraints (policy violations, обязательные шаги), LLM-based — soft quality (выбор оптимального инструмента, качество промежуточных решений).

Trace eval естественно интегрируется с OpenTelemetry spans (см. [Главу 17](17_observability_and_operations.md)) — тот же trace, другая функция: не мониторинг, а оценка. Span'ы, собранные для observability, переиспользуются как input для eval graders.

---

## Практический вывод

### Чек-лист eval-дисциплины

| # | Действие | Детали |
|---|----------|--------|
| 1 | **Соберите golden dataset** | 50–100 примеров на каждый core use case. Dev-split + test-split |
| 2 | **Выберите метрики** | Минимум 1 LLM-judge + 1 детерминированная метрика на задачу |
| 3 | **Автоматизируйте** | Evals в CI/CD как regression gates. Promptfoo или DeepEval |
| 4 | **Классифицируйте ошибки** | 9 категорий failure taxonomy. Трекайте распределение |
| 5 | **Версионируйте всё** | Промпты, golden datasets, eval-конфиги — в Git, в одном коммите |
| 6 | **Калибруйте судей** | Balanced position, multiple evidence для LLM-as-Judge |
| 7 | **Обеспечьте значимость** | Не меньше 100 примеров для CI gates и не меньше 200 для финальной валидации |
| 8 | **Замкните feedback loop** | Production failures → golden dataset → eval → production |

### Минимальный старт за один день

Если вы ещё не используете evals — начните с малого:

1. Вручную соберите 50 примеров из production-логов.
2. Добавьте одну метрику (faithfulness или exact match).
3. Напишите один `test_eval.py` для DeepEval.
4. Добавьте `deepeval test run` в CI.

Это не идеальная система — но это уже лучше, чем «вроде стало лучше».

### Задания

**Задание 1.** Соберите golden dataset из 50 примеров для вашего основного use case. Используйте production-логи как источник, разделите на dev-split (35) и test-split (15). Добавьте теги по категориям (factual, edge_case, multi-turn, format) и уровню сложности. Ожидаемый результат: версионированный JSONL-файл в репозитории рядом с промптами.

**Задание 2.** Настройте regression gate в CI/CD: напишите eval-тест с DeepEval (одна LLM-judge метрика + одна детерминированная), добавьте `deepeval test run` в CI-пайплайн. Проверьте, что изменение промпта с намеренной регрессией блокирует PR. Ожидаемый результат: работающий eval gate, Который ловит регрессию при каждом PR.

**Задание 3.** Проведите A/B-тест двух вариантов промпта через pairwise comparison (§14.5) на вашем golden dataset. Используйте balanced position (оба порядка ответов) и LLM-judge. Оцените (сс.§14.9): достаточен ли размер выборки, чтобы различить промпты. Ожидаемый результат: числовое сравнение двух промптов с оценкой статистической значимости различий.

**Задание 4.** Возьмите агента из вашего проекта (или демо-агента). Записывайте structured trace каждого запуска в течение недели. Определите 3–5 workflow-правил (порядок инструментов, обязательные approval checkpoints, максимальное число шагов). Напишите rule-based grader, который проверяет trace на соответствие. Подсчитайте долю нарушений. Ожидаемый результат: набор workflow-правил, grader и статистика policy violations за неделю.

---

## Источники

- Zheng, L., et al. (2023). "Judging LLM-as-a-Judge with MT-Bench and Chatbot Arena." NeurIPS 2023. arXiv:2306.05685
- Liu, Y., et al. (2023). "G-Eval: NLG Evaluation using GPT-4 with Better Human Alignment." arXiv:2303.16634
- Wang, P., et al. (2023). "Large Language Models are not Fair Evaluators." arXiv:2305.17926
- Shankar, S., et al. (2024). "Who Validates the Validators? Aligning LLM-Assisted Evaluation of LLM Outputs." arXiv:2404.12272
- Yu, T., et al. (2025). "Self-Generated Critiques Boost Reward Modeling." NAACL 2025. arXiv:2411.16646
- Chiang, W., et al. (2024). "Chatbot Arena: An Open Platform for Evaluating LLMs by Human Preference." arXiv:2403.04132
- Ustalov, D. (2025). "Reliable, Reproducible, and Really Fast Leaderboards with Evalica." COLING 2025. arXiv:2412.11314
- Chen, M., et al. (2021). "Evaluating Large Language Models Trained on Code." arXiv:2107.03374
- Promptfoo. https://github.com/promptfoo/promptfoo
- DeepEval. https://github.com/confident-ai/deepeval
- RAGAS. https://github.com/vibrantlabsai/ragas
- Braintrust Autoevals. https://github.com/braintrustdata/autoevals

---

Evals отвечают на вопрос «насколько хорошо система решает задачу». Но качество — не единственное измерение: safety eval — подмножество quality eval, но безопасность выходит за рамки оценок к архитектурным решениям. Следующая глава — о том, как защитить LLM-систему от атак, утечек и непредвиденного поведения.

**Навигация:**
- Назад: [Глава 13. Антигаллюцинационный контур и защита от деградации](13_anti_hallucination_loop.md)
- Далее: [Глава 15. Безопасность LLM-систем](15_llm_system_security.md)
