Год назад я написал статью “Почему важно тестировать промпты и как это делать”. В ней я рассказывал про библиотеку promptfoo, которая предлагает различные способы проверки ответов, генерируемых моделью. Сейчас рынок уже находится на другом этапе: почти никому не нужно объяснять, зачем тестировать LLM при её интеграции в продукт, однако вопрос «как именно это делать» всё ещё остаётся открытым. Причём он стал ещё острее — ведь объём тестовых запросов вырос с 30 штук до 4 тысяч.
Представьте, что у нас есть бенчмарк из 4 тысяч вопросов и эталонных ответов. Как определить, действительно ли очередное изменение в системе (обновления в промпте, дополнительный агент в цепочке или, например, переход с базового RAG на гибридный) даёт реальный прирост качества?
В этой статье я планирую рассмотреть три подхода: ручную разметку, BERTScore и использование самой LLM в роли судьи (LLM as judge). Сразу отмечу: универсальных рецептов здесь не будет — это скорее список выявленных мною проблем, которые, надеюсь, сэкономит вам время и помогут сориентироваться на первых шагах.
Ручная разметка
Помните, что между условиями тестирования и «полевыми» условиями есть разница. Часто оказывается, что эксперты, оценивая ответы в Excel, завышают баллы. Скажем, они ставят 20% ответов оценку «отлично, не нужно редактировать», однако в проде те же 20% не подтверждаются. Предполагаю, что эксперты завышают оценки, будучи оторваны от реального контекста: они представляют себе ситуацию и видят более широкий диапазон ответов как «отличные».
Ручную разметку нужно использовать, чтобы составить бенчмарк, но мы не можем прибегать к этому способу каждый раз. Нам важно проверять систему при любом изменении, а не раз в год. На практике часто бывает так: мы видим ограниченный набор кейсов, где, например, появляется проблема с тем, что в ответе, предназначенном пользователю, вдруг присутствуют советы для сотрудника (LLM не различает получателя). Мы исправляем это, проверяем примерно на 20 отобранных кейсах — всё кажется корректным, и мы выкатываем изменения в прод. Но при этом неизвестно, не сломалось ли что-то ещё в другом месте. Было бы невероятно круто, мониторить ситуацию целиком.
Поэтому естественно, что мы задумываемся об удешевлении и автоматизации этого процесса.
BERTScore
BERTScore считается стандартом в индустрии, однако в наших тестах показал посредственные результаты. Я намеренно выбрал по 100 пар «вопрос–ответ» с самыми низкими (0 — ответ непригоден) и самыми высокими (2 — ответ можно использовать без доработки) оценками. Когда я посчитал средние значения Recall по каждой группе, результаты удивили: и там, и там получилось 0.679! (То есть степень совпадения текстов, по мнению метрики, оказалась одинаковой и для ответов, которые эксперт признал хорошими, и для тех, которые эксперт счёл непригодными.)
Сначала мне показалось, что это должно быть ошибка на нашей стороне и мы дважды прогнали один и тот же набор или что-то подобное. Но, пересмотрев данные, я заметил различия между парами, а позднее обнаружил, что среднее по всему датасету тоже отличается. Выяснилось, что один запуск дал в первой группе среднее Recall = 0.6785 (округлённое до 0.679), а другой — 0.6787 (тоже 0.679 при округлении). Разница на четвёртом знаке после запятой.
BERTScore, по сути, лишь измеряет насколько два текста похожи друг на друга лексически и семантически с учётом контекстных эмбеддингов. Эта метрика вовсе не пытается понять, совпадают ли тексты по фактам. Если в ответах одинаковый tone of voice и в них говорится про сборку дивана, BERTScore не заметит, что шаги этой сборки различаются.
В итоге мы отказались от BertScore и решили попробовать LLM as judge.
LLM as judge
Я выбрал 9-балльную шкалу для LLM as judge. Почему не 10-балльную? Потому что я исходил не из красивого числа, а из идеи сделать равный шаг между баллами, чтобы модель не концентрировалась на одной и той же точке. Делая максимально одинаковый шаг, я на 9 балле дошёл до полного совпадения ответов.
Я использовал такой промпт. Единственное — для каждой оценки был свой пример, но, увы, не могу их показать из-за NDA (они были написаны в домене заказчика):
“Пожалуйста, сравните следующие два текста, обращая внимание только на содержащиеся в них факты. Оцените степень фактического совпадения по шкале от 0 до 8, где 8 означает максимально возможное совпадение. Не учитывайте никаких других критериев.
Нужно поставить оценку от 0 до 8. Система оценивания сконцентрирована на фактах, а тональность или форма ответа игнорируется. Если тексты фактически совпадают, но изложены по-разному и имеют разную длину, они всё равно должны получить высокий балл сходства. Это важно.
Шкала оценок:
0 — Нет сходства. Темы совершенно разные, общих фактов нет.
1 — Минимальное сходство. Темы слегка пересекаются, но факты не совпадают.
2 — Очень низкое сходство. Тексты близки по общей теме, но фактически различаются.
3 — Низкое сходство. Темы связаны, но факты расходятся по деталям. Общих слов и фраз по теме мало.
4 — Умеренное сходство. Некоторые темы и факты пересекаются. Видно несколько общих терминов.
5 — Среднее сходство. Похожие темы, совпадают ключевые факты. Много общих слов и фраз, связанных с этими фактами.
6 — Выше среднего. Темы и факты почти одинаковые, различаются лишь подробности. Сильное совпадение в формулировках.
7 — Высокое сходство. Темы и факты практически совпадают. Большая часть слов и фраз одинакова и описывает одно и то же.
8 — Очень высокое сходство. Почти идентичное наполнение, расхождения лишь в мелочах — синонимы, перестановка слов и т. п.
Сравните так два текста: "{person_text}" "{expected_text}"
Ответ должен быть валидным JSON с полями "reflections" и "mark", без вложенных структур. Не используйте комментарии; просто выведите JSON. Пишите всегда на русском языке.”
Дальше мы сделали три прогона по выборкам примерно из 200 запросов: два раза по «лайкам» и один раз по «дизлайкам».
По итогам тестирования у меня получились такие средние оценки:
Лайки_v1 = 4.392
Лайки_v2 = 4.44
Дизлайки = 4.101

На графике видно, что большее количество оценок падает в середину — это может быть как проблемой настройки промпта, так и отображением реальности. Следующий шаг — разметить эту же выборку экспертом по нашей 9 бальной шакале, чтобы проверить, подтверждается ли наше нормальное распределение.
Итак, погрешность составила 0.048, а разрыв между лайками и дизлайками — 0.292. Получается, что погрешность между двумя «лайковыми» прогонами меньше, чем разница между лайками и дизлайками. Уверен, что при более тщательной настройке и добавлении большего количества примеров и размышлений эксперта в промпт стабильность ответов можно повысить. Нам ничего не мешало попросить эксперта разметить выборку из 100 примеров и в отдельном столбце написать размышления, которые далее были бы примером для LLM.
Оптимизация костов это следующий вопрос, например можно было бы закешировать промпт или гонять тесты на какой-то опенсорсной модели.
К сожалению, направление LLM as judge не получилось продолжать дальше в рамках исследования, хотя оно казалось мне весьма перспективным.
Но даже такие данные можно было бы использовать. Фактически мы можем выбрать 0.05 как некий «буфер»: если изменения в системе стабильно не увеличивают оценку как минимум на 0.05, то мы считаем их незначимыми и не выкатываем в прод.
Подытоживая
Ручная разметка нужна, чтобы создать бенчмарк, но она дорогая и долгая для промежуточных тестирований.
BERTScore, при условии того, что tone of voice у нас одинаковый и в целом речь идёт про похожие вещи, не работает.
LLM as judge выглядит многообещающим компромиссом.
В конце мне бы хотелось узнать, как вы оцениваете эффективность изменений в ваших системах? Возможно, у кого-то уже есть собственные наработки по автоматизации сравнения ответов или более глубокое понимание тех систем оценки, о которых я говорю. Буду рад вопросам и критике.
Автор: N3VERZzz