# ГЛАВА 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)