Введение: Наш самый полезный баг
Привет, я Рамиль, 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. Это и создало «тихую ошибку»: вместо того чтобы сигнализировать о проблеме, система рапортовала об успехе.
Когда в actual_output прилетала простая строка-категория, например "Покупка", модели вели себя по-разному.
Два архетипа: «Прагматичный Помощник» и «Педантичный Профессор»
Чтобы понять разницу, представим две модели в виде двух архетипов.
1. YandexGPT — Прагматичный Помощник
Эта модель стремится понять намерение пользователя. Она видит, что мы хотим что-то проверить, и даже если слово «Покупка» не является «утверждением», она пытается что-то с этим сделать. Она действует по «духу закона».
2. GPT-4o — Педантичный Профессор
Эта модель следует инструкциям с максимальной точностью. Для нее слово "Покупка" — это имя существительное, а не «фактологическое утверждение». Поэтому она строго по инструкции возвращает пустой список [], что и активировало нашу ошибку в коде.
Результаты говорят сами за себя
Анализ отчетов на реальных данных (34 примера) наглядно демонстрирует, как наша ошибка в коде по-разному проявилась на двух моделях.
|
Метрика |
YandexGPT |
GPT-4o |
Комментарий |
|
Средняя |
61.76% |
91.18% |
GPT-4o показывает высокий балл, потому что наша ошибка в коде это позволяет. |
|
Типичное |
Утверждение: » |
В ответе не найдено утверждений для проверки. |
YandexGPT пытается работать, GPT-4o честно говорит, что не может. |
|
Итог |
Низкая оценка от YandexGPT заставила нас искать ошибку. |
Высокая оценка от GPT-4o маскировала нашу проблему, создавая иллюзию качества. |
|
Наблюдение: «Педантичность» GPT-4o в итоге сослужила нам хорошую службу — она явно показала, что наш промпт был некорректным. А «прагматизм» YandexGPT, хоть и пытался «помочь», но давал менее однозначный сигнал.
От Faithfulness к AnswerRelevancy: поиск правильной метрики
Стало очевидно, что Faithfulness — неподходящий инструмент для нашей задачи. Проблема была в самой постановке вопроса: мы пытались проверить «фактологическую верность» там, где нужно было оценивать релевантность. Наш классификатор не генерирует развернутый ответ, а выдает короткую категорию. Поэтому ключевой вопрос звучит иначе: «Является ли присвоенная категория адекватной реакцией на запрос клиента?»
Именно для ответа на этот вопрос мы использовали вторую метрику — AnswerRelevancyMetric. Она не ищет абстрактные «утверждения», а напрямую просит модель-судью оценить смысловое соответствие между запросом и ответом.
Наблюдение: Этот промпт гораздо надежнее, потому что он не содержит абстрактных, академических терминов («утверждение»), а ставит прямую и ясную бизнес-задачу: «Является ли категория Х адекватной реакцией на фразу Y?».
Пример из реальной жизни
Чтобы было понятнее, как это работает на практике, вот два примера из наших тестов.
Пример 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? Делитесь своим опытом в комментариях!
ссылка на оригинал статьи https://habr.com/ru/articles/937418/
Добавить комментарий