В 2024 году исследователи провели простой эксперимент: взяли тесты с вариантами ответов и начали менять варианты местами. Сами вопросы и содержание ответов оставались прежними. Казалось бы, какая разница, находится правильный вариант под буквой A или под буквой C?
Оказалось, разница есть. В работе Large Language Models Sensitivity to The Order of Options in Multiple‑Choice Questions авторы показали заметный разброс качества моделей при перестановке вариантов. На разных сочетаниях моделей и датасетов разрыв оказался очень большим. Уже неприятно. Но дальше становится интереснее.
Ответы LLM часто оценивают с помощью другой LLM. Такой подход называют LLM‑as‑a‑Judge: модель получает вопрос, эталон или критерии, ответ тестируемой системы и выставляет оценку. Однако и такой судья не идеально объективен. В исследовании Judging LLM‑as‑a‑Judge with MT‑Bench and Chatbot Arena описаны, в частности, позиционное смещение и предпочтение более подробных ответов.
Получается любопытная конструкция: поведение одной модели зависит от формулировки и порядка элементов, а проверяем мы её другой моделью — со своими особенностями. Для исследователей это интересный эффект. Для команды, которая выпускает AI‑функцию в продакшен, — обычная инженерная проблема.
Почему ручной проверки недостаточно
Представим типичный релиз. Команда изменила system prompt, заменила модель, добавила reranker или дала агенту новый инструмент. Разработчик открыл чат, задал десять вопросов и просмотрел ответы. Выглядит прилично. Кажется, стало лучше. Слово «кажется» здесь и есть главная проблема. С обычным кодом всё просто:
calculate_discount(1000, 10)
Ожидаемый результат:
900
Если функция вернула 850, тест упал.
Теперь возьмём внутреннего AI‑ассистента. В документации написано:
Заявку на отпуск нужно создать минимум за 14 дней.
Модель отвечает:
Подайте заявку не позднее чем за две недели до начала отпуска.
Строки разные. Обычный Exact Match сочтёт ответ неправильным. Человек скажет, что смысл сохранён.
Значит, в LLM‑приложении приходится проверять не только совпадение текста, но и несколько независимых свойств ответа:
Correctness — верны ли факты;
Completeness — достаточно ли полно дан ответ;
Relevance — отвечает ли модель именно на заданный вопрос;
Faithfulness — опирается ли ответ на предоставленный источник, без выдуманных деталей;
Instruction Following — выполнены ли требования к формату и поведению.
Один ответ может быть правильным по фактам, но содержать выдуманное дополнение. Или быть содержательно корректным, но нарушать требуемый JSON‑формат. Поэтому одной оценки «хорошо / плохо» обычно недостаточно.

В RAG нужно отделять поиск от генерации
Особенно хорошо необходимость декомпозиции видна на RAG‑системах. Пользователь задаёт вопрос по внутреннему регламенту, а ассистент отвечает неправильно. Первая реакция часто звучит так: «модель опять галлюцинирует». Но причин может быть несколько:
нужный документ не попал в результаты поиска;
retriever нашёл устаревшую версию;
нужный фрагмент потерялся при chunking;
корректный контекст попал в prompt, но модель его проигнорировала;
модель добавила утверждение, которого в контексте не было.
Только последние два случая относятся к генерации ответа. Поэтому RAG лучше тестировать по частям.
Для retrieval применяют собственные метрики: например, Recall@K, Hit Rate и MRR. Они показывают, нашёл ли поиск нужный документ и насколько высоко тот оказался в выдаче. После этого отдельно проверяют генерацию: correctness, faithfulness, relevance и корректность ссылок на источники. Подобное разделение retrieval и generation лежит и в основе RAGAS.
Допустим, пользователь спрашивает о сроке возврата товара. В базе есть документ, где указано 14 дней.
Если retriever не нашёл этот документ, перед нами Retrieval Failure. Если документ попал в prompt, а модель всё равно ответила «30 дней», это уже Generation / Faithfulness Failure.
Для пользователя разницы нет: он получил неправильный ответ. Для разработчика разница огромная, потому что исправлять придётся разные части системы.

Evaluation начинается с тестового набора
Evaluation начинается не с выбора модной метрики и не с подключения очередного фреймворка. Сначала нужен dataset.
Один тестовый кейс может выглядеть так:
{ "input": "Могу ли я отменить бронирование?", "expected_behavior": "Запросить тариф или условия бронирования", "category": "cancellation", "risk": "medium" }
Здесь нет эталонного ответа — и он не обязателен. Есть ожидаемое поведение: система не должна угадывать условия отмены, а должна запросить недостающие данные. Такие кейсы часто полезнее идеально сформулированного reference answer. В датасет стоит постепенно собирать:
реальные пользовательские запросы;
типовые сценарии;
граничные случаи;
неоднозначные вопросы;
запросы, для которых системе не хватает данных;
ошибки, уже обнаруженные в продакшене.
Последняя категория особенно важна: найденный пользователем баг должен становиться новым regression case. Иначе команда рискует чинить одну и ту же ошибку несколько раз.
Код, человек и LLM‑судья
Обычно приходится комбинировать несколько типов проверок. Всё, что можно надёжно проверить кодом, лучше проверять кодом:
assert valid_json(response) assert response.tool == "calendar.update_event" assert response.currency == "RUB"
Вызывать отдельную LLM ради проверки валидности JSON нет смысла. Детерминированная проверка будет дешевле, быстрее и воспроизводимее.
Для смысловых критериев можно использовать человека или LLM‑as‑a‑Judge. Например, judge получает вопрос и ответ модели:
Вопрос: {input} Ответ: {answer} Оцени correctness от 1 до 5. Верни JSON: { "score": 1-5, "reason": "..." }
Такой подход удобен и хорошо масштабируется, но judge тоже нужно тестировать. Практичный минимум — заранее разметить небольшую выборку силами экспертов, прогнать её через judge и сравнить оценки.
Если judge систематически расходится с людьми на определённом классе вопросов, проблема уже не в тестируемой модели. Полезно также менять порядок сравниваемых ответов, ограничивать влияние длины ответа и проверять стабильность оценок повторными прогонами.

Среднее качество может скрывать критическую регрессию
Допустим, baseline выглядел так:
Prompt v17 Overall: 82% Finance: 91% Support: 80%
После изменения prompt получили:
Prompt v18 Overall: 86% Finance: 73% Support: 92%
Цифры условные, но ситуация вполне реальная. Средняя оценка выросла с 82 до 86 процентов. Если смотреть только на неё, новая версия лучше. При этом финансовые сценарии просели с 91 до 73 процентов.
Если Finance — критичный раздел продукта, такую версию выпускать нельзя. Поэтому нужны:
зафиксированный baseline;
разбивка метрик по категориям и уровню риска;
одинаковый regression dataset для сравниваемых версий;
пороги, которые блокируют релиз.
Например:
correctness: ">= 0.85" faithfulness: ">= 0.90" critical_errors: 0
Если условия не выполнены, сборка не проходит дальше. Только в этот момент evaluation начинает влиять на качество продукта, а не просто рисовать графики.
У агента правильный текст ещё не означает успех
Рассмотрим запрос:
Перенеси мою встречу с Иваном на завтра после 15:00.
Агент отвечает:
Готово, встреча перенесена.
По тексту всё выглядит отлично. Но внутри могло произойти следующее:
Найти встречу ↓ Проверить календарь ↓ Ошибка инструмента ↓ «Готово»
Поэтому у AI‑агента нужно проверять не только финальный ответ:
правильный ли инструмент был выбран;
корректные ли аргументы переданы;
обработана ли ошибка инструмента;
не создал ли агент вторую встречу вместо изменения первой;
изменилось ли реальное состояние системы.
Одна из ключевых метрик здесь — Task Success: выполнил ли агент задачу в среде, а не просто убедительно сообщил об успехе. Именно оценке поведения моделей в интерактивных средах посвящён, например, AgentBench.
Evaluation — это цикл, а не финальный экзамен
Рабочий процесс начинается с требований и описания критериев, проходит через датасет, evaluators, метрики и baseline, а затем становится частью regression testing и релизного процесса. После релиза цикл не заканчивается. Новые реальные ошибки возвращаются в dataset и превращаются в regression cases.

Для старта не нужна огромная платформа. Достаточно:
Собрать несколько сотен запросов, похожих на реальные.
Для каждого описать ожидаемое поведение, категорию и критичность.
Детерминированные свойства проверять кодом.
Для смысловых критериев добавить человека или один‑два откалиброванных LLM‑judge.
Зафиксировать baseline.
Запускать один и тот же набор после заметных изменений модели, prompt, retrieval или логики агента.
Не выпускать версию при провале критичных порогов.
Этого уже достаточно, чтобы перейти от фразы:
По ощущениям теперь отвечает лучше.
к инженерному выводу:
Общая корректность выросла, но в сценариях возврата появилась регрессия, поэтому версию пока не выпускаем.
Что дальше
За рамками одной статьи остались построение golden dataset, выбор метрик для разных RAG‑архитектур, калибровка LLM‑as‑a‑Judge, adversarial cases, проверка tool calls и полноценное тестирование AI‑агентов.
Эти темы пошагово разбираются в курсе «LLM Evaluation: оценка качества RAG и AI‑агентов»: от тестовых наборов и метрик до judges, RAG и агентных сценариев.
Главная мысль при этом остаётся простой: LLM‑приложение нельзя выпускать на основании впечатления от десяти удачных диалогов. Качество должно быть измеримым, сравнимым с baseline и связано с решением о релизе.
Источники
Pouya Pezeshkpour, Estevam Hruschka. Large Language Models Sensitivity to The Order of Options in Multiple‑Choice Questions. Findings of NAACL, 2024.
Lianmin Zheng et al. Judging LLM‑as‑a‑Judge with MT‑Bench and Chatbot Arena. NeurIPS, 2023.
Shahul Es et al. RAGAS: Automated Evaluation of Retrieval Augmented Generation. EACL System Demonstrations, 2024.
Xiao Liu et al. AgentBench: Evaluating LLMs as Agents. ICLR, 2024.

