Оценка AI-агентов: как построить evals и измерять надёжность, а не вайб
Оценка AI-агентов — это регулярный прогон фиксированного набора реальных задач с автоматической проверкой результата и подсчётом метрик, а не впечатление от пары удачных сессий. Главное — мерить не только pass@k (хотя бы одна из k попыток успешна), но и pass^k (успешны все k попыток), потому что именно стабильность определяет, можно ли доверить агенту работу без присмотра.
Зачем нужна оценка AI-агентов, если и так видно, что он работает
Видно как раз плохо. Агент за неделю ни разу не застрял после правки промпта — это починка или просто неделя простых задач? Один инженер уверен, что новый шаблон всё изменил, второй смотрел тот же лог и разницы не заметил. Оба правы ровно настолько, насколько ничего не посчитали.
Оценка AI-агентов (evals) заменяет ощущение числом. Вы берёте фиксированный набор задач, прогоняете агента, автоматически проверяете результат и сравниваете метрики до и после изменения. Тогда спор «стало ли лучше» решается таблицей, а не авторитетом.
Без evals любое изменение харнеса — промпта, AGENTS.md, набора инструментов, модели — это ставка вслепую. С ними — эксперимент с измеримым результатом.
Почему «вайб» обманывает
У впечатления от работы агента три систематические ошибки, и все они толкают к ложной уверенности.
- Ошибка выжившего. Удачные прогоны запоминаются и показываются коллегам, неудачные — перезапускаются и забываются. Надёжность, посчитанная по успешным прогонам, ничего не говорит о том, где агент реально ломается.
- Маленькая выборка. Три удачных задачи подряд — нормальное событие даже для агента, который справляется в половине случаев. Вероятность этого 0,5³ = 12,5%, то есть примерно каждая восьмая серия.
- Уверенный тон. Агент пишет «готово, все тесты проходят» одинаково уверенно и когда это правда, и когда нет. Уверенность модели не откалибрована: тон ответа — не сигнал корректности.
Отсюда правило: всё, что не записано и не пересчитано, считается неизмеренным.
pass@k и pass^k: две метрики, которые путают
Агент недетерминирован: одна и та же задача при повторе может пройти, а может провалиться. Поэтому одну задачу прогоняют несколько раз и считают одну из двух метрик.
pass@k — хотя бы одна попытка из k успешна
Метрика пришла из оценки генерации кода. Она отвечает на вопрос «способна ли система в принципе решить задачу, если дать ей несколько попыток». Это честная метрика для сценария, где человек выбирает лучший из нескольких вариантов или где есть надёжная автоматическая проверка, отсекающая неудачные.
pass^k — все k попыток успешны
Метрика отвечает на другой вопрос: «насколько стабильно агент решает задачу». Она описывает работу без присмотра — когда агент запускается много раз и каждый провал стоит денег или времени.
Разница между ними огромная. Пусть агент решает задачу с вероятностью 70% на одну попытку и попытки независимы:
| k | pass@k = 1 − 0,3^k | pass^k = 0,7^k |
|---|---|---|
| 1 | 70% | 70% |
| 3 | 97,3% | 34,3% |
| 5 | 99,8% | 16,8% |
| 10 | ~100% | 2,8% |
По pass@5 агент выглядит почти идеальным, по pass^5 — ненадёжным. Обе цифры верны, просто описывают разные вещи. Если вы собираетесь отпустить агента работать автономно, смотрите на pass^k: он ближе к тому, что вы увидите в реальной работе.
Как посчитать на практике
Прогоните каждую задачу n раз (например, 5) и запишите число успехов c. Для задачи pass^k приближённо равен доле запусков, где успешны все k подряд, а проще — оцените вероятность успеха p = c / n и возведите в степень. Для набора задач усредните по задачам.
import json
from statistics import mean
# results.jsonl: {"task": "add-pagination", "runs": [true, true, false, true, true]}
rows = [json.loads(line) for line in open("results.jsonl")]
def pass_at_k(runs, k):
p = sum(runs) / len(runs)
return 1 - (1 - p) ** k
def pass_pow_k(runs, k):
p = sum(runs) / len(runs)
return p ** k
for k in (1, 3, 5):
print(k,
round(mean(pass_at_k(r["runs"], k) for r in rows), 3),
round(mean(pass_pow_k(r["runs"], k) for r in rows), 3))
Оценка через p в степени k грубая при малом n, но для сравнения версий харнеса между собой её обычно достаточно. Важнее, чтобы n и набор задач не менялись между сравнениями.
Как собрать набор задач для evals
Набор задач — главный актив. Хорошая метрика на плохих задачах измеряет не то.
Берите задачи из своей реальности
Публичные бенчмарки вроде SWE-bench полезны для сравнения моделей, но их задачи отобраны: внятное описание, готовые тесты. В вашем репозитории требования размыты, а тестов местами нет, поэтому цифра бенчмарка почти всегда выше того, что агент покажет у вас. Лучший источник задач — закрытые тикеты и реальные провалы агента из журнала.
Состав набора
- 20–50 задач для начала. Меньше — шум съест разницу между версиями. Больше — прогон станет дорогим, и его перестанут запускать.
- Разные типы: новая фича, исправление бага, рефакторинг, задача на чужой модуль, задача с ловушкой (где правильный ответ — «так делать нельзя» или «нужно уточнение»).
- Каждая провалившаяся в работе задача становится тестом. Так набор растёт вместе с пониманием, где агент слаб, и регрессии не возвращаются незамеченными.
- Фиксированное начальное состояние: конкретный коммит, зафиксированные зависимости, одинаковое окружение. Иначе вы мерите не агента, а дрейф среды.
Формат задачи
Каждая задача — это описание, начальное состояние и критерий успеха, который можно проверить без человека.
id: add-pagination
repo_ref: 3f2c1ab
prompt: |
Добавь пагинацию в GET /payments: параметры limit (по умолчанию 20, максимум 100)
и cursor. Ответ содержит next_cursor.
check:
- pytest tests/test_payments_pagination.py
- mypy app/
limits:
max_changed_files: 6
Обратите внимание: критерий успеха — это Definition of Done, записанный командами. Если вы не можете записать критерий для задачи, это сигнал, что и агенту она поставлена неоднозначно.
Автопроверки или LLM-судья
Проверять результат можно кодом или другой моделью. У каждого способа своя зона.
| Автопроверки (код) | LLM-судья | |
|---|---|---|
| Что проверяет | Тесты, типы, линтер, E2E, размер diff, запрещённые файлы | Соответствие требованиям, читаемость, стиль, качество объяснения |
| Детерминизм | Полный | Нет: оценка плавает от запуска к запуску |
| Стоимость | Почти бесплатно | Ещё один вызов модели на каждую задачу |
| Главный риск | Проверяет не всё: тесты зелёные, а фича не та | Судья соглашается с уверенным, но неверным решением |
Правило: сначала код, судья — поверх
Всё, что можно проверить детерминированно, проверяйте детерминированно. LLM-судью добавляйте только для свойств, которые кодом не выразить, и никогда — вместо тестов.
Как сделать LLM-судью полезным
- Разный контекст с генератором. Судья не должен видеть рассуждения агента — только задачу, критерии и итоговый diff. Иначе он повторит те же ошибки: это и есть N-версионность, два независимых взгляда вместо одного.
- Рубрика вместо «оцени по десятибалльной шкале». Конкретные вопросы с ответом да/нет: «есть ли валидация limit > 100», «изменены ли файлы вне app/payments».
- Калибровка на ручной разметке. Разметьте вручную 20–30 результатов и сравните с вердиктами судьи. Если совпадение низкое, менять нужно рубрику, а не доверять судье больше.
- Асимметрия ошибок. Ложное «готово» дороже ложного «не готово»: первое уходит в прод, второе стоит одного перезапуска. Настраивайте судью так, чтобы при сомнении он отклонял.
Какие метрики считать кроме доли успехов
Доля решённых задач — только верхушка. Чтобы понять, где именно чинить, нужны метрики надёжности из классической инженерии.
- MTBF — сколько шагов или задач агент проходит до застревания. Показывает, как редко он ломается.
- MTTR — сколько времени уходит на возврат в рабочее состояние после сбоя. Снижать его почти всегда дешевле, чем повышать MTBF: хороший файл прогресса и воспроизводимая среда сокращают восстановление в разы.
- Радиус поражения — сколько кода портит один неудачный шаг. Ограничивается лимитом на diff и изоляцией рабочей копии.
- Доля ложных «готово» — задачи, где агент заявил успех, а проверка упала. Самая опасная метрика: именно она показывает, можно ли верить отчёту агента.
- Стоимость и время — токены и минуты на задачу. Харнес, который поднял успех на 5%, но удвоил стоимость, не всегда выигрывает.
Журнал провалов: evals из повседневной работы
Не каждая команда готова сразу строить прогонщик. Минимальная версия оценки — журнал провалов из трёх колонок.
| Задача | Результат | Слой |
|-------------------------|------------------------------------|---------------|
| пагинация /payments | «готово», 2 теста красные | верификация |
| фикс таймаута webhook | 30 мин на pip install | среда |
| рефакторинг refunds | старый синтаксис ORM | контекст |
Слой — это одно из пяти мест, где ломается агент: спецификация задачи, контекст, среда исполнения, верификационная обратная связь, управление состоянием. Через двадцать записей видно настоящее бутылочное горлышко, и почти никогда оно не там, где вы думали. Каждая строка журнала — кандидат в набор задач для evals.
Почему мощная модель не спасает
Первая реакция на провалы агента — взять модель подороже. Иногда это помогает, но чаще разрыв между бенчмарком и вашей реальностью закрывает не модель, а среда вокруг неё.
Если агент полчаса поднимает окружение — это слой среды, и флагманская модель будет поднимать его те же полчаса. Если агент говорит «готово» при красных тестах — это слой верификации: у него нет команды, которая скажет правду. Если он пишет код в чужом стиле — ему не дали конвенции проекта. Ни одна из этих проблем не про интеллект модели.
Evals позволяют проверить это честно. Прогоните набор с текущей моделью и с моделью дороже, затем — с текущей моделью и добавленным AGENTS.md с командами проверки. Часто второе изменение даёт больше прироста, чем первое, при нулевой цене. Но не верьте этому на слово: именно для таких сравнений набор и нужен.
Пошаговый план: оценка AI-агентов за неделю
- День 1. Заведите журнал провалов и пишите в него каждый сбой агента с указанием слоя.
- День 2. Отберите 20 задач из закрытых тикетов и журнала. Для каждой зафиксируйте коммит и запишите критерий успеха командами.
- День 3. Напишите прогонщик: чистая рабочая копия, запуск агента с промптом задачи, запуск проверок, запись результата в JSONL.
- День 4. Прогоните каждую задачу 3–5 раз. Посчитайте pass@1, pass^3, долю ложных «готово», среднюю стоимость.
- День 5. Поменяйте одну вещь в харнесе и прогоните заново. Сравнивайте только при одинаковом наборе и числе повторов.
Меняйте по одной переменной за раз. Если в одном эксперименте сменить модель, промпт и инструменты, вы не узнаете, что сработало.
Типичные ошибки при оценке агентов
- Один прогон на задачу. Недетерминизм агента делает такой результат лотереей. Минимум три повтора.
- Набор меняется между сравнениями. Добавили новые задачи и одновременно поменяли промпт — цифры несравнимы.
- Агент видит проверочные тесты. Если тесты лежат в репозитории, агент может подогнать под них код или просто их изменить. Храните скрытые проверки вне рабочей копии и проверяйте, что агент не трогал тестовые файлы.
- Отчёт агента вместо проверки. «Все тесты прошли» в ответе модели — не результат. Результат — код возврата команды, запущенной прогонщиком.
- Только pass@k. Красивая цифра для презентации и плохой прогноз для автономной работы.
Итог
Оценка AI-агентов — это фиксированный набор реальных задач, автоматические проверки и метрики, которые пересчитываются после каждого изменения харнеса. pass@k показывает, на что агент способен, pass^k — можно ли ему доверять без присмотра. Автопроверки дают правду, LLM-судья с рубрикой и отдельным контекстом дополняет их там, где код бессилен. А журнал провалов по пяти слоям показывает, что чинить в первую очередь, и чаще всего это не модель. Метрики надёжности и диагностическая петля разобраны на практике в курсе по harness engineering.
Частые вопросы
Сколько задач нужно для evals агента?
Для начала хватает 20–50 реальных задач с автоматическим критерием успеха и 3–5 повторами каждой. Меньший набор не отличит улучшение от шума, больший станет дорогим и его перестанут запускать.
Можно ли оценивать агента только по публичным бенчмаркам?
Нет. Публичные бенчмарки сравнивают модели на отобранных задачах с готовыми тестами, а в вашем репозитории требования и тесты другие. Для решений о своём харнесе нужен собственный набор задач.
Чем pass@1 отличается от pass^1?
Ничем: при одной попытке обе метрики равны доле успешных прогонов. Различаться они начинают с k больше единицы.
Может ли LLM-судья быть той же моделью, что и агент?
Может, если у него отдельный контекст: он видит только задачу, рубрику и итоговый diff, без рассуждений агента. Но его вердикты всё равно нужно сверить с ручной разметкой.
Как часто прогонять evals?
После каждого заметного изменения харнеса: промпта, файла инструкций, набора инструментов, модели. Полезно также запускать набор по расписанию, чтобы замечать дрейф окружения и зависимостей.
Эта тема подробно разобрана с практикой в курсе «Harness Engineering: инженерия надёжности AI-агентов».
- Метрики надёжности AI-агента: MTBF, MTTR, радиус поражения и закон Литтла
- Почему AI-агент ломается в проде: пять слоёв отказа и диагностическая петля