init
This commit is contained in:
456
book/14_llm_system_quality_evaluation.md
Normal file
456
book/14_llm_system_quality_evaluation.md
Normal file
@@ -0,0 +1,456 @@
|
||||
# ГЛАВА 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)
|
||||
Reference in New Issue
Block a user