Модуль 5 · Оценка и управление · урок 20 из 24
Свои evals для кодинг-агентов
Публичный бенчмарк отвечает на вопрос «как модель решает чужие задачи в чужом harness». Вам нужен другой ответ: станет ли агенту лучше или хуже в вашем репозитории после смены модели, правки AGENTS.md или нового навыка. Без своего набора задач (evals) любое изменение обвязки агента (harness) — это вкусовщина и пара удачных сессий. В этом уроке — как собрать такой набор за несколько дней и встроить его в работу.
Что именно вы оцениваете
Anthropic в статье Demystifying evals for AI agents вводит словарь, которым удобно пользоваться и нам:
- задача (task) — постановка плюс исходное состояние репозитория;
- прогон (trial) — одна попытка агента решить задачу;
- оракул, или грейдер (grader) — то, что выносит вердикт;
- транскрипт — полная запись действий агента;
- результат (outcome) — итоговое состояние среды, а не то, что агент сказал о своей работе.
Главное следствие из той же статьи: оценивая «агента», вы оцениваете связку harness и модели. Поменяли AGENTS.md, набор MCP-серверов или версию Codex CLI — это уже другой испытуемый, даже если модель та же.
Второе различие — назначение набора. Capability evals проверяют то, что агент пока делает плохо, и начинаются с низкой доли успеха: это холм, на который вы взбираетесь. Regression evals проверяют то, что уже работает, и должны держаться около 100%. Задачи, которые стабильно проходят, со временем переезжают из первой группы во вторую.
Откуда брать задачи
Не придумывайте задачи с нуля. Anthropic советует начинать с 20–50 простых задач из реальных провалов. В агентстве их источник очевиден: MR, которые ревьюер переписал; баги, пойманные QA после агентского коммита; задачи из Kaiten, где агент трижды не попал в макет или сломал e2e.
Каждая задача должна быть однозначной: два инженера, глядя на результат, выносят одинаковый вердикт. Если для этого нужен разговор с автором задачи, постановку надо дописать. И у каждой задачи должно быть эталонное решение — хотя бы коммит, которым человек закрыл её на самом деле.
Структура набора, которая хорошо живёт рядом с кодом:
evals/
run.sh
tasks/
form-phone-validation/
task.json # базовый коммит, тип, таймаут
prompt.md # постановка, как её получил бы агент
setup.sh # подготовка worktree: composer install, npm ci
check.sh # оракул: выносит вердикт по итоговому состоянию
oracle/ # скрытые тесты, агент их не видит
results/{
"id": "form-phone-validation",
"base": "a1b2c3d",
"kind": "regression",
"source": "MR !412, правка ревьюера: валидация телефона на сервере"
}Базовый коммит — состояние репозитория до того, как человек решил задачу. Промпт — формулировка из трекера, а не пересказ решения.
Оракулы: от дешёвых к дорогим
Anthropic делит грейдеры на три вида: код (быстро и воспроизводимо, но хрупко), модель (гибко, но требует калибровки) и человек (эталон, но дорого и медленно). Совет статьи — детерминированные проверки везде, где возможно, LLM-судья там, где без него никак.
Тесты. Основной оракул для кода. Принципиально, чтобы тесты оракула были скрыты от агента: если он их видит, он решает «пройти тесты», а не задачу. Тесты кладутся в worktree только после того, как codex exec закончил работу.
Проверки поведения. Playwright-сценарий на поднятом стенде, состояние базы после миграции, HTTP-ответ формы. Это ближе к тому, что проверит QA. Почему проверка поведения — самое слабое место harness, разобрано в уроке о проверке поведения.
Проверки отсутствия. Агент не правил существующие тесты, не добавил зависимость, не тронул чужой модуль. Anthropic отдельно советует проверять и наличие нужного поведения, и отсутствие нежелательного.
#!/usr/bin/env bash
# Запускается в worktree после агента; $1 — каталог oracle/
set -euo pipefail
ORACLE="$1"
# Агент не должен был править существующие тесты
if ! git diff --quiet HEAD -- tests/; then
echo "FAIL: изменены существующие тесты"; exit 1
fi
# Агент не должен был добавлять зависимости
if ! git diff --quiet HEAD -- composer.json composer.lock; then
echo "FAIL: изменены зависимости"; exit 1
fi
# Скрытые тесты оракула
cp "$ORACLE"/PhoneValidationTest.php tests/Feature/
vendor/bin/phpunit tests/Feature/PhoneValidationTest.phpLLM-судья. Нужен для того, что не формализуется: понятно ли описание MR, соблюдены ли соглашения из docs/, не размазана ли логика по слоям. Рекомендации Anthropic: калибровать судью по оценкам людей, давать ему вариант ответа «не знаю», а каждое измерение оценивать отдельным судьёй, а не одним на всё. На практике: два человека размечают 20–30 результатов, судья размечает те же, и вы смотрите, где он расходится с людьми. Пока не совпадает — правьте рубрику, а не доверяйте цифре. Вердикт удобно получать по схеме через --output-schema (см. урок об ответе по схеме).
И последнее правило оракулов: оценивайте результат, а не путь. Проверка «агент сначала прочитал README, потом запустил тесты» ломается на первом же разумном отклонении.
Скрипт прогона на codex exec
Каждый прогон — в чистом окружении: Anthropic прямо предупреждает, что общее состояние между прогонами даёт коррелированные провалы или завышает результат. Отдельный git worktree на прогон — самый дешёвый способ.
#!/usr/bin/env bash
# Использование: ./evals/run.sh <прогонов на задачу> <метка> [модель]
set -euo pipefail
K="${1:-3}"; LABEL="${2:-baseline}"; MODEL="${3:-}"
ROOT="$(git rev-parse --show-toplevel)"
OUT="$ROOT/evals/results/$LABEL-$(date +%Y%m%d-%H%M)"
mkdir -p "$OUT"
codex --version > "$OUT/codex-version.txt"
MODEL_ARGS=(); [ -n "$MODEL" ] && MODEL_ARGS=(-m "$MODEL")
for dir in "$ROOT"/evals/tasks/*/; do
id="$(basename "$dir")"
base="$(jq -r .base "$dir/task.json")"
for i in $(seq 1 "$K"); do
wt="$(mktemp -d)/wt"
git -C "$ROOT" worktree add --detach "$wt" "$base" >/dev/null 2>&1
[ -f "$dir/setup.sh" ] && (cd "$wt" && bash "$dir/setup.sh") >/dev/null 2>&1
codex exec --ephemeral --ignore-user-config -C "$wt" \
--sandbox workspace-write ${MODEL_ARGS[@]+"${MODEL_ARGS[@]}"} --json \
-o "$OUT/$id.$i.last.md" - < "$dir/prompt.md" \
> "$OUT/$id.$i.jsonl" 2> "$OUT/$id.$i.stderr" || true
if (cd "$wt" && bash "$dir/check.sh" "$dir/oracle") > "$OUT/$id.$i.check.log" 2>&1
then r=1; else r=0; fi
echo "$id,$i,$r" >> "$OUT/results.csv"
git -C "$ROOT" worktree remove --force "$wt"
done
done
echo "Результаты: $OUT"Что здесь важно. --ignore-user-config убирает влияние личного ~/.codex/config.toml, иначе результат зависит от того, чей это ноутбук. --json сохраняет транскрипт событий для разбора. Промпт подаётся через stdin (-). Версия Codex CLI записывается рядом с результатами: без неё сравнение двух прогонов бессмысленно.
Оговорка о скрытности оракула: песочница по умолчанию даёт агенту читать почти всю файловую систему, так что каталог evals/ в соседнем checkout формально ему доступен. Для строгого режима гоняйте набор в контейнере или CI-раннере, куда оракул попадает только после завершения агента. В примере OpenAI Cookbook для того же используют профиль прав, запрещающий чтение эталонных ответов.
Повторяемость: pass@k и pass^k
Агент недетерминирован, поэтому один прогон на задачу ничего не доказывает. В том же примере Cookbook три повторных прогона дали почти одинаковую общую долю успеха — разница в 3 пункта, — но 23 вопроса из 108 переключались между «прошёл» и «не прошёл». Средняя цифра скрывает нестабильность.
Две метрики из статьи Anthropic:
- pass@k — вероятность, что хотя бы одна из k попыток успешна;
- pass^k — вероятность, что успешны все k попыток.
При 75% успеха на попытку все три попытки проходят примерно в 42% случаев. Если результат агента всё равно смотрит человек и можно выбрать лучший из нескольких вариантов, ориентируйтесь на pass@k. Если агент работает без присмотра — автопочинка CI, фоновая уборка, — важна pass^k.
# Доля задач, решённых хотя бы раз и во всех прогонах
awk -F, '{n[$1]++; s[$1]+=$3}
END {for (t in n) {c++; any+=(s[t]>0); all+=(s[t]==n[t])}
printf "задач=%d pass@k=%.2f pass^k=%.2f\n", c, any/c, all/c}' results.csvЗадача с нулём из многих прогонов — по наблюдению Anthropic, чаще сломанная задача, чем неспособный агент. Перечитайте постановку и оракул.
Регресс harness
Набор нужен в момент, когда вы что-то меняете:
- смена модели — линейка моделей в Codex регулярно обновляется, а старые модели выводятся;
- правка AGENTS.md, docs/ или навыка;
- новый MCP-сервер или обновление Codex CLI.
Порядок простой: прогон с меткой baseline до изменения, прогон с меткой кандидата после, сравнение по задачам, а не только по итоговой цифре. И обязательно читайте транскрипты. Anthropic приводит пример с CORE-Bench: результат Opus 4.5 вырос с 42% примерно до 95% после того, как исправили оценку и постановки задач, — дефекты были в самом наборе. Ваши evals — такой же код, и в них тоже бывают баги.
Задачи из вашего репозитория приватны, а значит, защищены от главной болезни публичных бенчмарков — попадания в обучающие данные. Почему это важно, разберём в следующем уроке, о бенчмарках.
Попробуйте сами
10–15 мин на рабочем месте- Пролистайте последние 20 MR пилотного проекта и выпишите 5 задач, где агентский результат пришлось заметно переделывать. Для каждой запишите базовый коммит и коммит человека.
- Оформите одну задачу по структуре выше:
task.json,prompt.md,check.shс проверкой отсутствия изменений в тестах. Прогоните./evals/run.sh 3 baselineи посчитайте pass@k и pass^k. - Измените одну строку в AGENTS.md, которая касается этой задачи, и прогоните набор с другой меткой. Сравните не только цифры, но и два транскрипта.
Коротко
- Оценивается связка harness и модели: любая правка обвязки — новый испытуемый.
- Задачи берите из реальных провалов, начните с 20–50; каждой нужен однозначный критерий и эталонное решение.
- Детерминированные оракулы везде, где можно; LLM-судья — только откалиброванный по людям, по одному на измерение.
- Чистый worktree на прогон, скрытый оракул, записанная версия Codex CLI.
- Смотрите pass^k для задач без присмотра; ноль на многих прогонах — повод проверить саму задачу.