Восемь лучших практик RAG, протестированных на 746 страницах руководств.
Какие популярные оптимизации RAG выдержали тестирование на трех руководствах по продукту: очистка символов дефиса, размер блоков данных, маршрутизация, пороги эскалации и переранжирование.
Источники: I Measured Every RAG “Best Practice” on 746 Pages of Product Manuals. Only Four Survived.
Бот поддержки, созданный на основе трех реальных PDF-файлов — 408-страничного руководства владельца электромобиля, 329-страничного телевизионного планера и 9-страничного листовка с беспроводной мышью, — стал площадкой для тестирования восьми популярных методов оптимизации RAG. Правило было строгим: ничто не принималось к использованию, если измерения на этом наборе данных, оцененные по заранее подготовленному ключу ответов, не показывали их эффективности. Литература и привычки не учитывались.
Результатом стало повышение точности с 72% до 89%, в то время как количество токенов запроса сократилось с 14 до 57%. Это было достигнуто за счет четырех изменений, а не восьми. Два отклоненных подхода даже негативно сказались на результатах, а одна реализованная оптимизация оказалась никогда не применяемой. Все исследования обошлись примерно в 0,015 доллара за вызовы API.
Ниже приведена информация о результатах каждого подхода. Самым ценным элементом в списке вовсе не является метод RAG.
Ошибка, более серьезная, чем любой гиперпараметр
Прежде чем настраивать процесс поиска, проверьте, что именно попало в индекс. Все подтверждают, что PDF был распарсен. Более важный вопрос — в каком состоянии находится текст.
Бесконечный цикл может на него ответить:
SOFT = "" # U+00AD, the soft hyphen
for manual in manuals:
print(manual.name, extract(manual).count(SOFT))
В руководстве по автомобилю было обнаружено 2,272 слова.
Мягкая дефисная черта — это указание верстальщика для программы форматирования: разделить слово здесь, если строка исчерпана. Читатель PDF соблюдает это правило и скрывает её, поэтому человек её не видит, но инструмент для извлечения текста передаёт её в неизменном виде. На странице отображается целое слово, тогда как в индексе появляются такие строки, как «ve», «мягкая дефисная черта», «новая строка», «hicle».
В этом руководстве 999 разных слов были разделены пополам, в том числе, многократно, самое важное существительное в любом руководстве по автомобилю. Слово «Vehicle» встречалось 1,486 раз как целое слово, тогда как должно было встретиться 1,580 раз. 94 случая его употребления оставались недоступными при поиске.
Это одновременно портит каждый путь поиска. Поиск по ключевым словам сопоставляет буквальные строки, и слово «hicle» не считается синонимом слова «vehicle», без возможности частичного баллов. Векторный поиск работает хуже, чем ожидалось, поскольку модели эмбеддингов разбивают текст на субслова, и фрагмент вроде «hicle» распадается на бессмысленные части. Даже удачное совпадение вставляет искажённый контекст в запрос.
Исправление заключается в одной строке, рядом с которой находится ловушка. Отступленный текст также разрывается на настоящих дефисах. Слово «cus-tomer», разделённое на части, следует объединить обратно в «customer», но выражение «toll-free» должно оставаться с дефисом, и для различения их требуется словарь. Таких случаев было 95, и все они остались неправильными, при этом число было записано. Известный пробел в 95 слов — это совершенно иной тип искажения, чем тихое предположение, которое незаметно портит корпус данных. Никакой размер фрагмента не может восстановить слово, которое индекс никогда не считал словом.
Лучший размер фрагмента — это ловушка
Более крупные фрагменты содержат больше контекста, поэтому соответствуют большему количеству вопросов, но в то же время они постоянно включают в каждый запрос дополнительный нерелевантный текст. Существует перекрытие, чтобы ответ, находящийся на границе, не разделялся на две части.
Было протестировано девять конфигураций с использованием ключа ответов. Точность легко измерить, в то время как за нее приходится платить.
Перекрытие никогда не оправдывало своего существования, хотя и не по той причине, которая кажется при первом прочтении. При длине 700 символов отсутствие перекрытия принесло прирост в 11 баллов, что может показаться критичным, но при длине 1800 символов значения 0, 100 и 200 для перекрытия соответственно составили ровно 89%. Одно сравнение показывает, что перекрытие дорогостояще, а другое — что оно не влияет ни на что.
Причиной обоих результатов является статистический показатель, который практически не упоминался в отчетах. Во всех конфигурациях, даже самых простых, каждый ответ полностью помещался в один блок, и ни один из них не обрезался на границе. Наличие перекрытия служило защитой от проблемы, отсутствующей в данном корпусе данных, поэтому его измеренное значение колебалось от нуля до отрицательных значений. Проверка того, насколько часто ответы действительно пересекают границу, должна предшествовать уплате такой доплаты.
Затем возникает ловушка. Лучший результат — 89% — был достигнут при 1400 символах и 200 единицах перекрытия. Для его получения необходимо отправлять по 1,283 токена за запрос вместо 750, что на 71% больше для каждого запроса на протяжении всего срока использования продукта. При 18 вопросах в тестовом наборе 89% означает 16 правильных ответов, а 83% — 15. Лучшая конфигурация превосходит самую дешевую ровно на один вопрос.
Следовательно, победителя нельзя определять автоматически. Необходимо выбрать самую дешевую конфигурацию, оценка которой не отличается от оценки победителя — в данном случае это любое значение в пределах 8 очков. По этому правилу шесть из девяти конфигураций оказались на одном уровне, и победила самая дешевая из них. Точность выросла с 72% до 83%, к тому же снизились затраты на обработку данных — это был первый случай «бесплатного обеда» проекта, достигнутый за счет отказа от оптимизации измеряемой метрики.
Фильтр, стоивший 22 очка
Корпус данных включает три продукта. Вопрос о мыше не требует поиска в 408-страничном руководстве по автомобилю, поскольку два других документа лишь увеличивают вероятность ошибки. План заключался в том, чтобы определить продукт и ограничить поиск именно этим руководством.
Эту задачу выполнил классификатор. Он правильно определял продукт в 78% случаев за менее чем 4 мс без каких-либо затрат, что является значительным преимуществом по сравнению с поиском в трех раза большего объема текста. Тем не менее точность снизилась на 22 пункта.
Для того чтобы механизм сработал, потребовалось удивительно много времени. Неправильный фильтр не просто снижает ранг правильного ответа — он его удаляет. Без фильтра запутанный поиск среди всех трех руководств всё равно находит правильный отрывок, возможно, на 4-й позиции вместо 1-й, и алгоритм переранжирования может его спасти. С фильтром неправильно сформулированный вопрос ищет в неверном документе, и правильный фрагмент не попадает в низкий ранг, поскольку его нет в наборе кандидатов.
Следовательно, классификатор, который прав в 78% случаев, не приводит к незначительному снижению качества. Он устанавливает жесткий лимит в 78% для всего последующего процесса, независимо от качества остальных этапов обработки.
Решение заключается не в более точном классификаторе, а в возможности воздержаться от принятия решения: применять фильтр только тогда, когда есть уверенность, а искать все варианты, когда оценки двух лидирующих продуктов близки. В вопросе вроде «Как соединить их по Bluetooth?» слово «его» имеет огромное значение, и классификатор находится в такой же неуверенности, как и любой другой.
Или вообще пропустить процесс инференции
Самый дешевый роутер представлен в виде выпадающего списка. Для подобного инструмента задать вопрос о том, о каком продукте идет речь, требует всего одного нажатия, которое пользователь обычно делает при открытии чата с страницы помощи продукта. Это мгновенно устраняет неоднозначность, в которой классификатор может лишь догадываться, причем фильтр всегда дает правильный результат.
Цифры точны. Классификатор неправильно направил 4 из 18 вопросов. Поиск без фильтра выдал 15 ответов, а с фильтром — 11; каждая ошибка направления превратила успешный поиск в неудачный. Если устранить эти ошибки, точность останется прежней, а также будет достигнута точность, обещанная фильтром.
Оптимизация, которая так и не была реализована
Эту часть хотелось бы удалить ради внешнего вида.
План заключался в том, чтобы использовать дешевую модель объемом 20 миллиардов параметров, когда результаты поиска казались надежными, и переходить на модель объемом 120 миллиардов параметров в противном случае. Неверные ответы обычно сгруппировываются там, где результаты поиска нестабильны, а алгоритм переранжирования уже выдает оценку уверенности, поэтому использование этого сигнала не требует дополнительных затрат.
Порог был установлен в 0,0, что казалось обоснованным с точки зрения принципов: кросс-кодер генерирует логит, sigmoid(0) равен 0,5, а падение значения ниже нуля интерпретируется как увеличение вероятности того, что верхний фрагмент является менее релевантным, чем более релевантным. Однако вывод реального распределения показывал другую картину: минимальное значение среди зафиксированных результатов составило 3,24, медиана — 8,29, а максимальное — 10,00.
Порог оказался ниже самого низкого когда-либо зафиксированного значения, поэтому он так и не сработал ни разу. Политика увеличения значений была практически идентична постоянному использованию дешевой модели, плюс дополнительные накладные расходы в виде строки в таблице результатов, которая ничего не отражала, но выглядела как результат исследования. Нулевой порог не является нейтральным стандартом — это утверждение о распределении, которое никто не анализировал, и у каждого абсолютного порога логита есть такой недостаток, поскольку распределение меняется при изменении алгоритма ранжирования или корпуса данных.
Решение заключалось в вычислении этого числа. Необходимо было запустить только два конечных пункта — для реальных случаев, всегда дешевых, и для реальных случаев, всегда дорогих, — записывая при этом оценку, правильность и стоимость каждого вопроса. Каждый промежуточный порог представлял собой повторную обработку уже находящихся в памяти данных без дополнительных вызовов API, при этом использовался полный обзор вместо выборочного.
Результаты теста принесли сюрприз: каждый порог лишь влияет на стоимость, в то время как точность остается неизменной. Диагностика показывает причину этого. Из двенадцати вопросов удалось восстановить только один, что означает, что дешевая модель потерпела неудачу, а дорогая — справилась с задачей. Этот вопрос получил оценку 10,00, что является максимальным значением, тогда как средний балл вопросов, которые дешевая модель уже решила правильно, составил 8,29. Единственной неудачей, заслуживающей дальнейшего рассмотрения, была та, в восстановлении которой модель была наиболее уверена, поэтому никакое правило, основанное на усилении действий при неопределенности, не может выявить ее на любом пороге. Сигнал маршрутизации не способен предсказать то, что от него и ожидалось.
Появился ещё один побочный эффект. Результат прямой обработки составил 92%, тогда как повторная обработка уже обработанных данных дала прогноз 83%. Разница не связана с пороговыми значениями; она возникает из-за того, что система оценки LLM оценивает идентичные ответы по-разному при втором просмотре, избегая таким образом объявления о приросте в 9 пунктов, который на самом деле является лишь шумом.
Изменение, принесшее двойную пользу
Переупорядочивание было наиболее эффективным решением, и причина этого заслуживает как понимания, так и применения.
Векторный поиск встраивает вопрос и каждый фрагмент отдельно с помощью модели, которая никогда не рассматривает их как пару. Кросс-кодер же принимает пару как единый входной материал и оценивает её напрямую, что делает процесс гораздо более точным, но слишком медленным для обработки всего корпуса данных. Поэтому в работе используется метод, позволяющий дешево получить 25 кандидатов, заново их отсортировать и оставить лучшие 4.
Уровень воспроизведения вырос с 83% до 89%. Более значимым показателем является место, в котором оказался результат. До переранжирования правый фрагмент обычно занимал второе или третье место, поэтому для надежности отправляли пять фрагментов. После этого он обычно оказывался на первом месте, и четырех фрагментов было достаточно. Достаточство базовой точности позволяет сократить количество фрагментов до двух, что снижает количество токенов на 57%.
Поскольку порядок улучшается, меньше фрагментов достигают одинаковой точности, а именно токены запроса и оплачиваются. Это изменение обеспечивает повышение точности и одновременно дает скидку. Его стоимость составляет 73 мс работы локального процессора по сравнению с 1 252 мс при использовании LLM — разница, которую никто не замечает.
Что остается прежним
- Проверьте входные данные перед тем, как обращаться к механизму поиска. Один простой цикл может либо выявить проблемы, такие как проблема с дефисами, либо подтвердить отсутствие ограничений, что делает последующие действия более безопасными.
- Стандартные настройки представляют собой показатели кого-то другого или вообще никаких показателей. Значение 100 никогда не помогало, а порог 0,0 никогда не активировался, и в реальных условиях ни то, ни другое не будет видно. Единственным симптомом может стать более высокий счёт без каких-либо объяснений.
- Следует учитывать, как ведёт себя оптимизация в случаях ошибок, а не только частоту этих ошибок. Фильтр, который ошибается в 22% случаев, но лишь перестраивает порядок результатов, ещё терпим. Фильтр же, который ошибается в 22% случаев и делает ответ недоступным, — уже нет.
- Отклонённая идея — это тоже результат. Четыре идеи, которые не были внедрены, — это ещё четыре элемента без обслуживания, мониторинга, версионирования или уведомлений по ночам.
Существует одно важное ограничение. В ключе ответов указано 18 проверенных пар вопроса и ответа, поэтому каждый вопрос стоит 5,6 балла. Сбой в расчетах, приводящий к потере 22 баллов, связан с четырьмя вопросами, что достаточно для принятия мер. Результат с перекрытием 11 баллов связан с двумя вопросами, поэтому он имеет лишь косвенное значение. Ключ ответов, составленный на основе собственных данных команды, должен рассматриваться в приоритете перед доверием к чужим цифрам. Полная обработка занимает примерно пять центов на процессоре ноутбука, причем существует жесткий предел бюджета, который приводит к исключению из правил, а не к указанию этой суммы в счете-фактуре.