Evals: как понять, что ваш LLM-продукт вообще работает
Самая распространённая ошибка в LLM-продуктах: разработчик пишет промпт, прогоняет 5 примеров руками, видит что хорошо, деплоит. Через месяц пользователи жалуются. Что изменилось? Непонятно. Промпт тот же, код тот же, модель та же. Но качество упало.
Ответ почти всегда один: не было метрик, поэтому деградация шла незаметно. У вас не было baseline, не было регрессионных тестов, не было способа сравнить "вчера" и "сегодня".
Разбираю, как это исправить.
Почему LLM-продукты особенно хрупкие
В обычном коде регрессия видна сразу: тест упал, билд красный. В LLM-продукте вывод всегда какой-то — модель не падает, она просто отвечает хуже. Хуже на 5%, потом на 10%, потом пользователи тихо уходят.
Причины деградации:
- Провайдер обновил модель под тем же именем (было постоянно в 2023–2024, сейчас реже, но бывает)
- Вы правили один промпт и незаметно сломали соседний сценарий
- Поменялся характер входов — пользователи начали присылать другое
- Новая версия SDK / библиотеки эмбеддингов сдвинула поведение
Без evals вы узнаёте о любом из этих случаев от недовольного клиента, а не от метрики.
Минимальный eval-пайплайн за день
Нужны три вещи.
1. Dataset — 30–100 примеров с разметкой "хорошо/плохо" или идеальными ответами. Источники: реальные логи продакшена, сценарии от бизнеса, корнер-кейсы которых вы боитесь. Вручную прошли, отметили что ожидается. Хранить — в git, в JSONL. Не в Notion и не в Google Sheet.
2. Runner — скрипт, который прогоняет текущую версию продукта на всём датасете и сохраняет ответы. Не забывайте про temperature: 0 в evals — иначе вы измеряете шум, а не систему.
3. Grader — способ сравнить ответ с ожиданием. Три варианта:
- Точное совпадение (для структурированных выходов: JSON, классификация)
- Similarity (cosine между эмбеддингами для свободного текста — быстро, но приблизительно)
- LLM-as-judge (более мощная модель оценивает ответ по рубрике — дорого, но надёжно)
На практике миксуют: для классификаторов точное совпадение, для творческих ответов — judge.
Что мерить кроме качества
Стоимость на запрос. Если ответ стал в 2 раза лучше, но в 5 раз дороже — это регрессия экономики, не улучшение.
Задержка. Пользователь уходит после 8 секунд ожидания. Измеряйте p50, p95, p99 — среднее обманчиво.
Rate структурных ошибок. Если просите JSON — сколько процентов ответов парсятся? Должно быть 99.9%, иначе ломайте.
Длина вывода. Дрейфует незаметно — модель начинает отвечать длиннее, вы платите за лишние токены, UX ухудшается.
CI-интеграция
Eval должен запускаться автоматически — на каждый PR, который трогает промпты или код пайплайна. Threshold — не новый прогон vs абсолютный эталон, а новый vs предыдущий main. Отклонение более 3% — предупреждение, более 10% — блок.
Всё это делается GitHub Actions за полчаса. Скрипт, datasets в репо, runner вызывает ваш пайплайн, сравнивает с baseline из предыдущего коммита.
Антипаттерны
"Мы проверили руками, всё ок." Вы проверили 5 случаев, а в проде их 5000. Руками нельзя.
Слишком маленький dataset. 10 примеров не ловят ничего. Нужно минимум 30, лучше 100. Разнообразие важнее количества.
LLM-as-judge той же моделью, что и в проде. Модель оценивает сама себя с завышенной самоуверенностью. Judge должен быть другим провайдером или более мощной моделью.
Оцениваем "хорошо ли звучит ответ". Размытая рубрика = бесполезная оценка. Нужны конкретные критерии: упомянул ли модель N ключевых фактов, не галлюцинировала ли, соблюдала ли формат.
Что делать сегодня
Выделите час. Найдите 30 реальных запросов из логов. Разметьте глазами ожидаемые ответы. Напишите 50 строк питона или ноды, которые прогоняют пайплайн и сравнивают результат. Зафиксируйте текущий score — это ваш baseline.
Поздравляю, теперь вы видите деградацию на графике, а не от пользователей. Это уже 80% работы — дальше только добавлять кейсы по мере появления боли.
Если нужна помощь построить evaluation-пайплайн под ваш продукт — напишите. Это как раз попадает в пакет "Аудит AI-инфраструктуры".