Введение: Наш самый полезный баг

Привет, я Рамиль, QA-инженер в компании Raft. В своей работе я фокусируюсь на автоматизации тестирования, в том числе для LLM-решений, где часто использую связку Pytest и специализированных фреймворков. Эта статья — история из нашей недавней практики.

Когда перед нами встала задача построить автоматизированную систему оценки (evaluation) для LLM-классификатора, который должен был сортировать запросы клиентов, выбор инструментов казался очевидным. Мы взяли DeepEval — это open-source фреймворк, который заслуженно называют "Pytest для LLM". Он позволяет писать кастомные метрики, запускать тесты и оценивать качество ответов языковых моделей, используя для этого другие модели в качестве "судьи" (Judge). В качестве такого судьи мы начали с GPT-4o, поскольку DeepEval изначально "заточен" под экосистему OpenAI, и это был самый прямой и логичный путь.

Однако в проекте были требования информационной безопасности (ИБ): мы должны были убедиться, что наша система сможет работать с моделями, развернутыми в определенном контуре. Так в нашем тестировании появился второй обязательный участник — YandexGPT.

Именно это, на первый взгляд, обычное требование ИБ и привело нас к неожиданному открытию. Сравнивая результаты двух моделей-судей на нашей, как мы думали, простой метрике, мы обнаружили, что они ведут себя кардинально по-разному.

Эта статья — история не о недостатках DeepEval, а о тех уроках, которые мы извлекли, создавая собственные метрики. Это рассказ о том, как "характер" разных LLM-судей может выявить скрытые ошибки в вашем коде и как требования ИБ могут стать катализатором для более глубокого понимания ваших инструментов.

Почему мы начали с метрики Faithfulness?

Прежде чем погрузиться в детали ошибки, стоит пояснить, почему наш выбор пал именно на эту метрику. Наш LLM-классификатор должен был анализировать запрос клиента и присваивать ему одну из заранее определенных категорий (например, "Покупка", "Возврат", "Технический вопрос").

В этом контексте Faithfulness (дословно, "верность фактам") казалась нам логичной отправной точкой. Мы хотели убедиться, что классификатор основывает свое решение только на информации из запроса клиента, а не додумывает что-то свое. Идея была простой: если в ответе модели (названии категории) содержатся "утверждения", они должны быть подкреплены "контекстом" (исходным запросом).

Как мы увидим дальше, именно эта, казалось бы, правильная логика и завела нас в ловушку, когда мы столкнулись с особенностями разных моделей-судей.

Проблема на практике: как мы ошиблись при локализации метрики

Чтобы понять корень проблемы, давайте посмотрим на код метрики FaithfulnessMetric. Это не наша разработка с нуля, а, по сути, локализованная версия стандартной метрики Faithfulness из DeepEval. Так как DeepEval по умолчанию использует промпты на английском, для корректной работы с YandexGPT и нашими русскоязычными данными мы просто перевели его внутренние инструкции на русский. И именно здесь нас ждал сюрприз.

Наблюдение: При адаптации мы слепо скопировали логику, в которой был заложен анти-паттерн: если модель-судья не находила "утверждений" в тексте, метрика по умолчанию выставляла высший балл 1.0. Это и создало "тихую ошибку": вместо того чтобы сигнализировать о проблеме, система рапортовала об успехе.

Сокращенная версия нашей метрики FaithfulnessMetric
Сокращенная версия нашей метрики FaithfulnessMetric

Когда в actual_output прилетала простая строка-категория, например "Покупка", модели вели себя по-разному.

Два архетипа: "Прагматичный Помощник" и "Педантичный Профессор"

Чтобы понять разницу, представим две модели в виде двух архетипов.

1. YandexGPT — Прагматичный Помощник

Эта модель стремится понять намерение пользователя. Она видит, что мы хотим что-то проверить, и даже если слово "Покупка" не является "утверждением", она пытается что-то с этим сделать. Она действует по "духу закона".

2. GPT-4o — Педантичный Профессор

Эта модель следует инструкциям с максимальной точностью. Для нее слово "Покупка" — это имя существительное, а не "фактологическое утверждение". Поэтому она строго по инструкции возвращает пустой список [], что и активировало нашу ошибку в коде.

Результаты говорят сами за себя

Анализ отчетов на реальных данных (34 примера) наглядно демонстрирует, как наша ошибка в коде по-разному проявилась на двух моделях.

Метрика

YandexGPT
(Прагматик)

GPT-4o
(Педант)

Комментарий

Средняя
Faithfulness

61.76%

91.18%

GPT-4o показывает высокий балл, потому что наша ошибка в коде это позволяет.

Типичное
объяснение

Утверждение: ''
> Вердикт: Нет

В ответе не найдено утверждений для проверки.

YandexGPT пытается работать, GPT-4o честно говорит, что не может.

Итог

Низкая оценка от YandexGPT заставила нас искать ошибку.

Высокая оценка от GPT-4o маскировала нашу проблему, создавая иллюзию качества.

Наблюдение: "Педантичность" GPT-4o в итоге сослужила нам хорошую службу — она явно показала, что наш промпт был некорректным. А "прагматизм" YandexGPT, хоть и пытался "помочь", но давал менее однозначный сигнал.

От Faithfulness к AnswerRelevancy: поиск правильной метрики

Стало очевидно, что Faithfulness — неподходящий инструмент для нашей задачи. Проблема была в самой постановке вопроса: мы пытались проверить "фактологическую верность" там, где нужно было оценивать релевантность. Наш классификатор не генерирует развернутый ответ, а выдает короткую категорию. Поэтому ключевой вопрос звучит иначе: "Является ли присвоенная категория адекватной реакцией на запрос клиента?"

Именно для ответа на этот вопрос мы использовали вторую метрику — AnswerRelevancyMetric. Она не ищет абстрактные "утверждения", а напрямую просит модель-судью оценить смысловое соответствие между запросом и ответом.

Наблюдение: Этот промпт гораздо надежнее, потому что он не содержит абстрактных, академических терминов ("утверждение"), а ставит прямую и ясную бизнес-задачу: "Является ли категория Х адекватной реакцией на фразу Y?".

Сокращенная версия нашей успешной метрики AnswerRelevancyMetric
Сокращенная версия нашей успешной метрики AnswerRelevancyMetric

Пример из реальной жизни

Чтобы было понятнее, как это работает на практике, вот два примера из наших тестов.

Пример 1: Релевантный ответ

- Входные данные (test_case.input): "Хочу подключить еще один город к своему тарифу"
- Ответ системы (test_case.actual_output): "Подключение услуг"
- Вердикт модели-судьи (YandexGPT):

{
"score": 1.0,
"reason": "Ответ релевантен, так как подключение города является одной из услуг, предоставляемых в рамках тарифа."
}

Пример 2: Нерелевантный ответ

- Входные данные (test_case.input): "какой остаток по счету"
- Ответ системы (test_case.actual_output): "Покупка"
- Вердикт модели-судьи (YandexGPT):

{
"score": 0.0,
"reason": "Ответ нерелевантен. Запрос касается информации о балансе, а система выдала категорию, связанную с совершением покупки."
}

Эти примеры наглядно показывают, что метрика AnswerRelevancy точно решает нашу задачу — оценивает соответствие ответа бизнес-логике, а не абстрактные лингвистические "утверждения".

Бонус: Наша матрица для диагностики ошибок классификации

Чтобы системно анализировать ошибки, мы используем следующую матрицу, которая помогает нам превращать результаты тестов в конкретные задачи для команды.

Точность

Релевантн-ть

Консистент-ть

Диагноз: Что это значит?

Рекомендация: На что обратить внимание?

1

>= 0.7

>= 0.7

✅ Идеальный
случай.

Ничего делать не нужно.

1

< 0.7

>= 0.7

⚠️ "Семантический разрыв".

Фокус на промпт! Уточнить описание класса.

0

>= 0.7

>= 0.7

❌ "Близкий промах".

Самая важная ошибка! Улучшить разграничение классов.

0

< 0.7

< 0.7

☠️ Полный провал.

Кандидат №1 на разбор. Что "сломало" модель?

...

...

...

...

...

Выводы: 5 правил для тех, кто оценивает LLM с помощью кастомных метрик

Наш опыт позволяет сформулировать несколько практических правил для всех, кто использует такие фреймворки, как DeepEval, для создания собственных систем оценки.

1. Признайте, что у моделей есть "характер". Промпт — это не универсальный код. Всегда тестируйте свои кастомные метрики как минимум на двух разных моделях (например, от OpenAI и Yandex), чтобы выявить скрытые зависимости от "стиля" интерпретации.

2. Не создавайте "тихих ошибок" в своих метриках. Ваша система оценки не должна молча ставить 1.0, если что-то пошло не так. Если модель-судья не смогла выполнить инструкцию, это должно быть явной ошибкой (Error или Score = 0.0), а не тихим успехом.

3. Пишите промпты для "педанта", а не для "помощника". Всегда стремитесь к максимальной однозначности в своих инструкциях.

* Плохо (зависит от интерпретации): "Извлеки утверждения из ответа".

* Хорошо (однозначно): "Посчитай, сколько предложений в 'Ответе'. Для каждого предложения проверь, содержится ли оно в 'Контексте'".

4. "Характер" модели — это не баг, а фича. Нет "плохой" или "хорошей" модели в этом сравнении. Педантичность GPT-4o идеальна для отладки промптов. Гибкость YandexGPT может быть полезна в других задачах. Выбирайте инструмент под задачу.

5. Доверяй, но проверяй (и логгируй). Всегда логгируйте сырые ответы от моделей-судей. Если бы мы не увидели, что GPT-4o возвращает пустой список [], мы бы никогда не нашли ошибку в нашем коде и продолжали бы доверять завышенной оценке.

А какие инструменты и подходы используете вы для определения качества классификации на основе LLM? Делитесь своим опытом в комментариях!

Комментарии (2)


  1. ARazum
    15.08.2025 08:08

    Классная статья. Таких не хватает сейчас.

    Есть 2 вопроса:

    1) Пробовали ли вы встроить процесс регулярной валидации в CI/CD, чтобы минимизировать риск ошибок на проде, или вернее, что бы ускорить их обнаружение, после обновлений или если появятся новые промпт-инъекции?

    2) Не пробовали ли работать на закрытых моделях, что бы проверка располагалась строго в контуре на открытых моделях?


    1. AllahverdievRamil Автор
      15.08.2025 08:08

      Спасибо за комментарий!

      1. Сейчас регулярную валидацию пока реализовали через батч‑запуски, без интеграции в CI/CD – фокус пока был на самой методологии оценки и корректности метрик

      2. Проверять строго на закрытых моделях пока не пробовали – сейчас основной акцент был на возможности сравнительного анализа и гибкости настройки