Как тестировать промпты и чейны (Ручная разметка-BERTScore-LLM as judge)

в 18:54, , рубрики: bertscore, LangChain, llm, llm-агент

Год назад я написал статью “Почему важно тестировать промпты и как это делать”. В ней я рассказывал про библиотеку 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

Это распределение оценок для выборки по лайкам из 233 запросов.

Это распределение оценок для выборки по лайкам из 233 запросов.

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

Итак, погрешность составила 0.048, а разрыв между лайками и дизлайками — 0.292. Получается, что погрешность между двумя «лайковыми» прогонами меньше, чем разница между лайками и дизлайками. Уверен, что при более тщательной настройке и добавлении большего количества примеров и размышлений эксперта в промпт стабильность ответов можно повысить. Нам ничего не мешало попросить эксперта разметить выборку из 100 примеров и в отдельном столбце написать размышления, которые далее были бы примером для LLM.

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

К сожалению, направление LLM as judge не получилось продолжать дальше в рамках исследования, хотя оно казалось мне весьма перспективным.

Но даже такие данные можно было бы использовать. Фактически мы можем выбрать 0.05 как некий «буфер»: если изменения в системе стабильно не увеличивают оценку как минимум на 0.05, то мы считаем их незначимыми и не выкатываем в прод.

Подытоживая

Ручная разметка нужна, чтобы создать бенчмарк, но она дорогая и долгая для промежуточных тестирований.

BERTScore, при условии того, что tone of voice у нас одинаковый и в целом речь идёт про похожие вещи, не работает.

LLM as judge выглядит многообещающим компромиссом.

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

Автор: N3VERZzz

Источник

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js