10 способов повысить эффективность RAG-системы

Краткого руководства недостаточно

Генерация ответа, дополненная результатами поиска,  —  это процесс расширения пользовательского ввода в большую языковую модель (LLM), такую как ChatGPT, дополнительной информацией, извлеченной вами (системой) из другого места. Затем LLM может использовать эту информацию для дополнения генерируемого ответа.

LLM  —  удивительное изобретение, но с одной ключевой проблемой. Эти модели придумывают всякую ерунду. RAG (Retrieval Augmented Generation  —  генерация ответа, дополненная результатами поиска) повышает эффективность модели, предоставляя ей фактический контекст, необходимый при ответах на запросы.

Используя краткое руководство по запуску таких фреймворков, как LangChain и LlamaIndex, каждый может создать простую RAG-систему (например, чат-бота для работы с документами) с помощью примерно пяти строк кода.

Но бот, сконструированный с помощью этих пяти строк кода, не будет работать ожидаемо хорошо. RAG легко прототипировать, но очень трудно внедрить в производство, т. е. довести до состояния, которым будут довольны пользователи. В базовой учебной версии RAG может работать на 80%. Но чтобы обеспечить недостающие 20%, часто требуются серьезные эксперименты. Лучшие практики еще не отработаны и могут варьироваться в зависимости от конкретной ситуации. Но ознакомление с ними стоит вашего времени, поскольку RAG  —  пожалуй, единственный наиболее эффективный способ применения LLM.

В этой статье рассматриваются стратегии повышения качества RAG-систем. Она предназначена для тех, кто создает RAG, стремясь преодолеть разрыв между базовыми настройками и достижением эффективности производственного уровня. В рамках данной статьи под оптимизацией понимается увеличение доли запросов, для которых система находит нужный контекст и генерирует соответствующий ответ. Предполагаю, что читатель уже имеет представление о RAG-процессе и знаком со стандартными фреймворками, используемыми для реализации подобных стратегий: LangChain и LlamaIndex. Тем не менее обсуждаемые здесь идеи не зависят от фреймворка.

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

10 способов повышения эффективности RAG

1. Очистка данных

RAG связывает возможности LLM с данными, используемыми в системе. Если данные запутаны по содержанию или компоновке, то в системе будут происходить нарушения. Если данные отличаются противоречивостью или избыточностью информации, поиск будет затруднен, а на этапе RAG может оказаться неоптимальным. Допустим, вы создаете чат-бота для справочной документации стартапа и обнаруживаете, что бот работает не очень хорошо. Первое, на что следует обратить внимание,  —  это данные, которые поступают в систему. Логично ли они разбиты на темы? Рассматриваются ли темы в одном месте или во многих отдельных местах? Если вы, как человек, не можете легко определить, какой документ вам нужно просмотреть, чтобы ответить на обычные запросы, то и ваша поисковая система не сможет этого сделать.

Этот процесс может быть простым  —  объединение вручную документов по одной и той же теме. Но можно пойти и дальше. Один из наиболее творческих подходов, известных мне, заключается в использовании LLM для создания резюме всех документов, предоставляемого в качестве контекста. На этапе поиска можно сначала выполнить поиск по этим резюме, а в детали погружаться только при необходимости. В некоторых фреймворках это даже является встроенной абстракцией.

2. Изучение различных типов индексов

Индекс является ключевым элементом LlamaIndex и LangChain. Это объект, на котором основана система поиска. Стандартный подход к RAG предполагает использование эмбеддинга и поиск по сходству. Собираются контекстные данные, обеспечивается эмбеддинг всего, а когда приходит запрос, находятся похожие фрагменты из контекста.

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

3. Разбивка данных на блоки

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

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

4. Экспериментирование с базовым промптом

Вот один из примеров базового промпта, используемого в LlamaIndex:

“Контекстная информация приведена ниже. Учитывая контекстную информацию и не имея предварительных знаний, дай ответ на запрос”.

Вы можете переписать его и поэкспериментировать с различными вариантами задач. Можете даже пренебречь RAG, позволив модели полагаться на собственные знания, если она не может найти подходящий ответ в контексте. Можете также настроить промпт так, чтобы он определял типы принимаемых запросов, например предписывал отвечать определенным образом на субъективные вопросы. Как минимум, полезно переписать промпт таким образом, чтобы у модели был контекст задач, которые она должна выполнить. Например:

“Ты работаешь в службе поддержки клиентов. Твоя задача  —  быть максимально полезным, предоставляя только фактическую информацию. Ты должен быть дружелюбным, но не слишком болтливым. Контекстная информация приведена ниже. Учитывая контекстную информацию и не имея предварительных знаний, дай ответ на запрос”.

5. Фильтрация метаданных

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

В связи с этим можно вывести общую концепцию, которую следует иметь в виду при создании RAG: похожее ≠ релевантное. Можно добавлять дату каждого письма в его метаданные и затем при поиске отдавать приоритет последнему контексту. В LlamaIndex есть встроенный класс постпроцессоров узлов (Node Post-Processors), который выполняет именно эту задачу.

6. Маршрутизация запросов

Часто бывает полезно иметь больше одного индекса. Тогда каждый поступающий запрос будет направляться к соответствующему индексу. Например, один индекс может обрабатывать обобщающие вопросы, другой  —  конкретизирующие, а третий  —  вопросы, связанные с определенной датой. Попытка оптимизировать один индекс для всех этих типов поведения в конечном итоге приведет к снижению эффективности работы. Вместо этого стоит направлять запросы к соответствующим индексам. Другой вариант  —  направить часть запросов в индекс, основанный на ключевых словах, как обсуждалось в разделе 2.

После создания индексов необходимо просто определить в тексте, для чего каждый из них должен использоваться. Тогда при запросе модель сама выберет подходящий вариант. Для этого в LlamaIndex и LangChain имеются соответствующие инструменты.

7. Реранжирование

Реранжирование (повторное ранжирование)  —  одно из решений проблемы несоответствия между сходством и релевантностью. При реранжировании поисковая система сначала, как обычно, получает верхние узлы для контекста, а затем повторно ранжирует их на основе релевантности. Для этого, как правило, используется Cohere Reranker. Эту стратегию часто рекомендуют эксперты. Независимо от случая использования при работе с RAG следует поэкспериментировать с реранжированием и посмотреть, улучшит ли это вашу систему. И LangChain, и LlamaIndex снабжены абстракциями, которые упрощают настройку.

8. Преобразование запросов

Вы уже изменили запрос пользователя, поместив его в базовый промпт. Возможно, имеет смысл изменить его еще больше. Приведу несколько примеров.

Перефразирование: если система не находит релевантного контекста для запроса, можно попросить LLM перефразировать запрос и повторить попытку. Два вопроса, которые кажутся человеку одинаковыми, не всегда выглядят одинаково в пространстве эмбеддинга.

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

Подзапросы: LLM, как правило, работает лучше, если разбивать сложные запросы на части. Вы можете встроить в систему RAG эту задачу, например разбить запрос на несколько вопросов.

В LLamaIndex есть документация, описывающая эти виды преобразования запросов.

9. Тонкая настройка эмбеддинг-модели

Сходство, основанное на эмбеддинге, является стандартным механизмом поиска для RAG. Данные разбиваются на блоки и встраиваются в индекс. Когда поступает запрос, он также встраивается для сравнения с эмбеддингом в индекс. Но в чем заключается смысл эмбеддинга? Как правило, в предварительно обученной модели, например text-embedding-ada-002 от OpenAI.

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

Для решения данной проблемы можно провести тонкую настройку эмбеддинг-модели. Это повысит показатели извлечения на 5–10%, хотя и потребует немного больше усилий. Данный процесс проще, чем кажется на первый взгляд, поскольку LlamaIndex поможет вам создать обучающий набор.

10. Использование инструментов разработки LLM

Скорее всего, вы уже используете LlamaIndex или LangChain для создания своей системы. Оба фреймворка имеют полезные средства отладки, позволяющие определить обратные вызовы, посмотреть, какой контекст используется, из какого документа происходит извлечение и т. д.

Если вам покажется, что встроенных в эти фреймворки инструментов недостаточно, обратите внимание на растущую экосистему инструментов, дающих возможность погрузиться во внутреннюю работу RAG-системы. Инструмент для работы в ноутбуке от Arize AI позволяет выяснить, как и почему извлекается контекст. Rivet  —  инструмент, недавно переведенный на открытый исходный код компанией Ironclad, специализирующейся на юридических технологиях,  —  предоставляет визуальный интерфейс для создания сложных агентов.

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

Заключение

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

В данной статье я не стал подробно останавливаться на оценке, на том, как можно измерить эффективность системы. В настоящее время оценка  —  это скорее искусство, чем наука. Важно создать некую постоянно проверяемую систему. Только так можно понять, изменили ли вы ситуацию. Для получения дополнительной информации изучите LlamaIndex Evals, LangChain Evals и многообещающий новый фреймворк под названием RAGAS.

Читайте также:

Читайте нас в Telegram, VK и Дзен


Перевод статьи Matt Ambrogi: 10 Ways to Improve the Performance of Retrieval Augmented Generation Systems

Предыдущая статьяКак создать первый проект по инженерии данных: инкрементный подход. Часть 1
Следующая статьяKube-Proxy и CNI: скрытые компоненты сети Kubernetes