Построй CI-эваль для агента {{agent}} на {{ci}}. Цель: ни один PR с изменением промта не проходит без оценки.
0. Чем это отличается от ручных эвалов
- Запускается автоматически на каждом PR
- Имеет threshold'ы и блокирует merge при регрессии
- Хранит историю в timeseries
- Снимает решение "лучше / хуже" из субъективного в измеримое
1. Состав эваль-набора
Стартуй с 30-50 задач, цель — 200+. Источники:
- Production-логи реальных пользователей (с редакцией)
- Зафиксированные баги → каждый бывший баг = тест
- Edge cases команды
- Synthetic adversarial (prompt injection, ambiguity)
Распределение по категориям:
| Категория | % | Зачем |
|---|---|---|
| Typical | 50 | Не сломать частые случаи |
| Edge | 20 | Empty / huge / weird inputs |
| Adversarial | 15 | Защита от попыток вывести из роли |
| Regression | 10 | Каждый прошлый баг |
| Safety | 5 | Отказ от вредного |
2. Структура задачи
- id: typ-014
category: typical
prompt_input:
user_message: "..."
context: { ... }
expected:
structural:
- has_section: "## Plan"
- format: markdown
- max_tokens: 1500
behavioral:
- did_not_call_tool: Bash
- called_tool_at_least_once: Read
semantic:
- judge: "Does the answer correctly identify the root cause?"
score_min: 4 # из 5
weight: 1.0
added_at: 2026-05-01
source: "ticket #482"
3. Виды проверок
| Тип | Когда |
|---|---|
| Structural | Формат, длина, наличие заголовков, валидный JSON |
| Behavioral | Какие tools вызвал/не вызвал, в каком порядке |
| Semantic (LLM judge) | Качество, factuality, helpfulness |
| Reference | Точное совпадение (только для классификации/коротких ответов) |
LLM-judge должен использовать более сильную модель чем тестируемый агент. Иначе судит хуже чем работает.
4. Метрики, которые считаем
На каждый прогон:
pass_rate— % задач которые прошли все assertionscategory_pass_rate— то же по категориямsemantic_score_mean— среднее по LLM-judge (1-5)avg_tokens,avg_cost_usd,avg_latency_mstool_misuse_rate— где вызвал не тот tool
5. Threshold'ы и регрессия
В evals.config.yaml:
thresholds:
pass_rate:
typical: { min: 0.95, max_regression: 0.02 }
edge: { min: 0.80, max_regression: 0.05 }
adversarial: { min: 0.90, max_regression: 0.02 }
regression: { min: 1.00, max_regression: 0.00 }
avg_cost_usd: { max_increase: 0.20 } # +20% — звонок будильника
avg_latency_ms: { max_increase: 0.30 }
Регрессия = новый прогон хуже baseline за пределами max_regression.
6. CI-пайплайн
Псевдокод для {{ci}}:
on: pull_request
jobs:
agent-evals:
runs-on: ubuntu-latest
steps:
- checkout
- install deps
- cache eval cache
- run: eval-runner --agent={{agent}} --set=full --baseline=main
- on failure:
post PR comment with diff table
fail the check
- on success:
update baseline timeseries
Параллель: разбей задачи по shards (cost vs время).
7. PR-комментарий
Бот должен оставить:
## Eval results vs main
| Metric | main | PR | Δ | Verdict |
|---|---|---|---|---|
| pass_rate (typical) | 0.96 | 0.93 | -0.03 | ❌ above max_regression |
| pass_rate (edge) | 0.82 | 0.88 | +0.06 | ✅ |
| avg_cost_usd | 0.012 | 0.018 | +50% | ❌ |
| semantic (1-5) | 4.4 | 4.5 | +0.1 | ✅ |
Failed tests:
- typ-007: missing "## Plan" section
- typ-019: judge score 2/5 — answer hallucinated file path
[View full report]
Без этого мердж блокируется.
8. Кэширование
LLM-вызовы — самая большая статья расходов. Кэш по (prompt_hash, model):
- В PR без изменения промта кэш помогает
- При изменении промта кэш промахивается → перепрогон
9. Стоимость и расписание
- Полный прогон на PR: только если diff трогает промт / агента / tools
- На обычном diff — smoke (10-20 быстрых задач)
- Полный nightly на main — для baseline
- Раз в неделю — adversarial + safety на свежих кейсах
10. Жизненный цикл задач
- Новая задача проходит этап "verified" (запущена локально, expected подтверждены)
- Permanent — в постоянном наборе
- Stale → archive (объясни в комменте почему убрана)
- Flaky → почини промт или почини тест, не убирай молча
Анти-паттерны
- Прогон без baseline — нет сравнения
- LLM-judge той же моделью, что и агент — сговор
- Pass/fail без semantic — пропустишь падение качества
- Не блокирует merge — PR катятся без оглядки
- Все задачи "typical" — слепое пятно по edge/adversarial
- Один большой test без разбиения по категориям — непонятно где регрессия
На выходе
evals/папка с задачами по категориям- Runner с поддержкой parallel + cache
- Config с threshold'ами
- CI-workflow с блокировкой при регрессии
- Бот-комментарий на PR
- Дашборд timeseries по основным метрикам
CI/CD-пайплайн
Шаги от пуша до прода: lint, типы, тесты, превью, прод, нотификации.
Performance budget по типам страниц
Бюджеты JS/CSS/images для разных типов страниц, целевые Web Vitals, enforcement в CI с конкретными порогами.
Матрица функциональной регрессии
Exhaustive матрица: список ВСЕХ интерактивных функций × текущий статус (pass / partial / fail / not tested) × cross-browser/device. Не выборка — система.