14 KiB
Глава 11. Оценка качества агента
«Стал ли агент лучше после твоих правок?» — самый неудобный вопрос, который задают себе люди, собирающие агентов. Неудобный, потому что интуитивно кажется: «ну, я же потыкал, посмотрел, вроде работает». А объективно сказать, что вчерашний агент хуже сегодняшнего (или наоборот, ваш новый замечательный промпт всё сломал на половине задач), — тяжело. Агенты — недетерминированные системы с разнородными задачами, и наивная метрика «точность» к ним плохо применима.
Эта глава — про то, какой практический минимум нужен, чтобы отвечать на этот вопрос, и почему его нельзя откладывать до «когда-нибудь потом».
Две плоскости оценки
Полезно сразу различать.
Оценка итога. Задача решена или нет. Это то, что волнует пользователя: починили ли тест, нашли ли нужный факт, правильный ли конспект.
Оценка траектории. Как агент к итогу пришёл. Сколько шагов занял, какие инструменты звал, не было ли лишних вызовов, не ходил ли по кругу.
Обе важны, и измеряются они по-разному.
Результативный, но дорогой агент (правильный ответ за 50 шагов и $2) и результативный дешёвый агент (правильный ответ за 8 шагов и $0,15) с точки зрения первой метрики равны, с точки зрения второй — очень разные. Для продакшна это разница между «работает» и «окупается».
Golden set: тестовый набор задач
Первое, что должно быть у любого агента старше двух недель жизни — фиксированный набор задач с ожидаемыми результатами. Не для одноразового теста, а как постоянный референс.
Для кодового агента такой набор — это репозиторий-фикстура с подготовленными задачами:
golden/
├── task_001_fix_failing_test/
│ ├── project/ # начальное состояние проекта
│ ├── task.md # формулировка задачи
│ └── check.sh # скрипт проверки, возвращает 0 при успехе
├── task_002_add_endpoint/
│ ├── project/
│ ├── task.md
│ └── check.sh
...
Прогон: агент запускается в копии project/, работает над задачей из task.md, потом check.sh говорит, получилось или нет. Результат — процент пройденных задач.
Для исследовательского агента набор более хитрый. «Правильный ответ» на «что я читал про MoE» — не один-в-один текст, а набор свойств:
- упомянута ли нужная заметка?
- процитированы ли ключевые идеи?
- не было ли галлюцинаций про факты, которых в заметках нет?
Формат:
task: "Что я читал про MoE?"
expected:
must_cite: ["notes/llm/switch-transformer.md"]
must_mention: ["routing", "mixture of experts", "балансировка"]
must_not_hallucinate: ["Mixture-of-Recursions"] # это мы не читали
Метрики для двух архетипов
Кодовый агент
Pass rate на golden set. Сколько процентов задач прошло check.sh. Главная метрика.
Среднее число шагов до решения. Медиана по успешным задачам. Растущая — повод насторожиться: агент или разучился быть прямолинейным, или промпт стал многословным.
Доля ошибок инструментов. Сколько вызовов пришло с невалидными аргументами или попало в блоклист. Признак расхождения между тем, что модель «думает», и тем, что инструменты готовы принять.
Стоимость задачи. Среднее число токенов (входных + выходных) на успешную задачу. Полезно отдельно: стоимость падающих задач может превышать стоимость успешных, потому что агент долго бьётся.
Исследовательский агент
Citation accuracy. Доля утверждений в ответе, которые действительно подтверждаются цитированными источниками. Вычисляется проверкой: для каждого утверждения ищем подтверждающий фрагмент в указанных заметках.
Recall. Нашёл ли агент релевантный материал, который у пользователя действительно есть в заметках. Частая поломка — пропустил нужную заметку, потому что порог релевантности был высоковат или формулировка запроса промахнулась.
Hallucination rate. Доля ответов, содержащих факты, которых нет в цитированных источниках. Главная метрика для анти-галлюцинации.
Satisfaction. Нефункциональная, но важная: устроил ли ответ пользователя в слепом тесте. Измеряется вручную.
LLM-as-judge
Ручная оценка — дорогая и не масштабируется. Для большинства метрик выше можно использовать саму LLM как судью.
Принцип: для каждого ответа агента запускаем отдельный LLM-вызов с промптом вида:
Вот задача пользователя: <task>
Вот ответ агента: <answer>
Вот материалы, на которые агент опирался: <sources>
Оцени ответ по шкале 1-5 по следующим критериям:
- Релевантность задаче
- Фактическая корректность
- Наличие галлюцинаций
Верни JSON: {"relevance": int, "correctness": int, "hallucinations": int, "reasoning": str}
Важные нюансы.
Судья должен быть отдельной моделью или отдельной сессией. Если оценивает та же модель в той же сессии — она склонна одобрять свою же работу.
Судья работает хуже исполнителя на сложных задачах. Если задача на уровне самой модели — судья тоже ошибётся. Работает для слабого агента + сильного судьи; не работает наоборот.
Оценки шумные. Любой LLM-judge даёт разные оценки на идентичные входы. Прогоняйте каждую задачу несколько раз, берите среднее.
LLM-judge нужна калибровка. Сравните его оценки с ручной разметкой на небольшом наборе (50–100 задач). Если расходятся — перепишите промпт судьи.
Практически: LLM-judge — это не замена ручной оценки, а её масштабируемый спутник. Ручную держим на меньшем, но качественном срезе, LLM-judge прогоняем постоянно.
Как не обмануться
Типичные способы сделать себе иллюзию прогресса.
Тестировать на тех же задачах, на которых настраивали. Если вы подкручивали промпт, глядя на 20 конкретных задач, — оценивать надо на других 20. Иначе вы измеряете не качество, а свою способность запомнить, что агенту нравится.
Смотреть только успешные случаи. Самая информативная часть — где агент провалился. Трасса неудачного прогона говорит больше, чем десять успешных.
Мерить только средние. Медиана и хвосты важнее среднего. Агент, который решает 80% задач за 5 шагов и 20% задач за 200 шагов, в среднем «решает за 44 шага» — и это число ничего не говорит.
Тестировать только крупные правки. Маленькие промптовые изменения часто незаметно ломают что-то. Постоянный прогон золотого набора после каждого значимого изменения ловит регрессии, пока они дешёвые.
Регрессионное тестирование
Похоже на юнит-тесты, но для агентов:
- Есть базовая версия (main branch):
agent_baseline. - Есть версия с изменениями:
agent_candidate. - Прогоняем оба на golden set.
- Сравниваем: где candidate лучше baseline, где хуже, где равно.
- Смотрим, устраивает ли нас новая форма баланса.
Это особенно важно для изменений системного промпта. Промпт — это код. Правка промпта — это коммит. Без тестов на эту правку вы работаете с системой, которую нельзя сравнить саму с собой.
Минимальный автоматический формат:
def run_suite(agent, golden_dir):
results = []
for task_dir in sorted(glob.glob(f"{golden_dir}/*/")):
task = open(f"{task_dir}/task.md").read()
outcome = run_in_sandbox(agent, task_dir, task)
check_result = subprocess.run([f"{task_dir}/check.sh"], capture_output=True)
results.append({
"task": os.path.basename(task_dir.rstrip("/")),
"pass": check_result.returncode == 0,
"steps": outcome.n_steps,
"tokens": outcome.tokens,
})
return results
baseline = run_suite(agent_baseline, "golden/")
candidate = run_suite(agent_candidate, "golden/")
compare(baseline, candidate)
Раз в неделю, раз в день, перед каждым коммитом — зависит от того, как активно вы меняете агента. Для каждого — сохраняйте результаты в отдельный файл, чтобы можно было проследить тренд.
Онлайн-метрики
Всё выше — оффлайн. Отдельная тема — что мерить на живых пользователях, когда агент уже работает.
Success signals. Что-то, что явно говорит «задача решена». Для кодового — прошёл ли CI после merge-а агентского коммита. Для исследовательского — перешёл ли пользователь к следующему вопросу (условный признак, что предыдущим доволен) или снова спрашивает то же другими словами (не доволен).
User feedback. Кнопки «хорошо / плохо» рядом с ответом. Простая, но крайне ценная метрика при достаточном объёме.
Drop-off. На каких типах задач пользователи быстрее всего бросают сессию. Это либо слишком сложные для агента задачи, либо задачи, где агент проваливается неочевидно для себя.
Онлайн-метрики отражают реальность, но запаздывают: вы узнаёте о регрессии через дни. Оффлайн — ловят регрессии до релиза. Работает комбинация.
Что дальше
Оценка отвечает на вопрос «правильно ли работает агент». Следующий практический вопрос — «сколько это стоит и можно ли быстрее». Глава 12 — про стоимость, латентность и кэширование промптов.