Рассмотрим техники построения и улучшения RAG систем: от нарезания текстов на куски, до продвинутых способов улучшения качества ответа.
Этим блогом можно пользоваться как шпаргалкой для проектирования своего RAG-а и/или для подготовки к собеседованиям.
Все полезные ссылки и материалы, на которые я опирался будут в конце.
Что такое RAG и зачем нужен
RAG - это фреймворк взаимодействия предобученной LLM с базой знаний. То есть при ответе LLM на запрос пользователя модель отвечает используя актуальный контекст из базы и свои pre-trained знания.

Обогащение запрос контекстом позволяет модели дать более точный ответ без необходимости дообучения на этих данных.
RAG очень часто можно использовать для формирования отчетов, создания корпоративных и специализированных чат-ботов. Причем так как не нужно дополнительного дообучения на доменных данных, то использование RAG-а часто более дешевый и быстрый вариант, а также безопасный и интерпретируемый по сравнению с fine-tuning-ом.
Базовый пайплайн подготовки системы RAG:
Загрузить документы
Нарезать на куски
Построить базу данных
Подготовить ретривер и, возможно, эмбеддер
Развернуть LLM для инференса
Базовый пайплайн применения RAG:
Аутентифицировать пользователя
Обработать входной запрос
Найти релеватные куски из базы данных
Отранжировать контексты
Собрать промпт из запроса и контекстов
Запромптить LLM
Получить от LLM ответ на вопрос
Верифицировать и отдать пользователю

Метрики RAG-а:
Приверженность — то, как сильно ответ похож на контекст, поданный в модель. Чем ниже метрика, тем выше вероятность галлюцинаций.
Полнота — то, насколько полный ответ для заданного вопроса и предоставленного контекста.
Учёт контекстов — то, какая доля контекстов использовалась при ответе.
Утилизация контекстов — то, какую долю контекста модель использовала при ответе на вопрос.
токсичность ответа
тональность ответа — например, по категориям эмоций
Больше про метрики можно почитать тут
Загрузка документов
Это первый этап построения RAG системы.

Для своей базы знаний можно использовать разные источники: видео на ютубе, конспекты ноушен, эксель таблицы и др.
Если в документе есть таблицы, или картинки, то из них можно извлечь полезную информацию в том числе - воспользоваться OCR и / или TableTransformer.
Библиотека, которая сама все делает за вас
Также важно помнить, что у каждого документа есть метаданные: название, дата, автор и др.
Нарезка документов
Mastering RAG: Advanced Chunking Techniques for LLM Applications
Урок по разделению с помощью LangChain
Нарезка документов на куски нужна для того, чтобы не переполнять контекст LLM ненужной и шумной информацией, а максимально информативной - это нужно как для более точного ответа, так и для ускорения работы LLM.
На что влияет нарезка на куски:
качество контекстов, которые отдаем LLM
Чем меньше контекст, тем меньше там информации, которая может сбить с толку LLM, а также тем легче правильнее определить семантический смысл эмбеддеру при создании вектора представления
затраты на индекс кусков
Чем больше кусков, тем выше затраты, так как нужно хранить больше векторов
Скорость извлечения релевантных кусков из индекса
Чем больше кусков, тем дольше задержка
Скорость ответа LLM
Чем длиннее контекст, подаваемый в LLM, тем дольше она будет отвечать
Факторы, влияющие на разделения документа на куски:
Структура текстапунктуация, переносы строки, маркдаун верстка и др.
Контекстное окно LLM и эмбеддера
Сложность и специфика запросов
Параметра функции разделения документа на куски:
размер куска по символам или по токенам
размер пересечения (наложения) кусков
желаемый разделитель

Виды нарезок:
По символам / токенам без специфичного разделителя
Разделяем документ согласно длины контекста и размере пересечения.Работает быстро, но глупо.
По символам / токенам с желаемыми разделителями
Пытаемся разделять на куски, например, по переносу строки, по точке или хотя бы по пробелу. Но все равно есть ограничение на длину контекста и длину наложения.Чуть дольше, но намного умнее, потому что не обрывает слова или даже предложения.
По Маркдаун разметке
Примерно как предыдущий метод, только разделитель заголовки маркдаунаПолучается более структурированное разделение, а также обновляет метадату кусков - добавляет поле названия.
Семантически
Разделяем текст на предложения.Добавляем в кусок текста новое предложение, если оно похоже семантически на уже имеющийся кусок.Ограничиваем количество предложений в куске.Разделение довольно умное, но расчет более ресурсоемкий из-за модели схожести.
Переписываем текст как утверждение
С помощью специальной модели или через LLM переписываем исходные предложения так, чтобы каждой из них по отдельности имело смысл, было понятно, о чем идет речь и не могло больше разделится на более мелкое утверждение.Потенциально очень умное разделение, которое сразу же помогает отвечать на вопрос. Но есть вероятность испортить хороший текст, а также требует дополнительных ресурсов на обработку документов.

Мульти-векторная индексация.
Иногда полезно для каждого документа иметь несколько векторов представления. Например, поделить документ на куски и для каждого посчитать векторы, делать ретрив по векторам кусков, а возвращать сам документ, таким образом уменьшается влияние шума и разнообразие топиков в документе. Также можно заменить или добавить к вектору документа вектор представление суммаризации этого документа. Также частая практика это придумывать гипотетические вопросы, ответом на которые может быть документ, и складывать в индекс вектор представления этих вопросов.
Кстати, метадата документа должна наследоваться каждому его куску, а также иногда дополняться свойствами отдельного куска, например, заголовок маркдаун при соответствующем методе разделения.
Метрики оценки качества, на которые можно ориентироваться при выборе стратегии разбиения документов на куски:
Приверженность — то, как сильно ответ похож на контекст, поданный в модель. Чем ниже метрика, тем выше вероятность галлюцинаций.
Полнота — то, насколько полный ответ для заданного вопроса и предоставленного контекста.
Учёт контекстов — то, какая доля контекстов использовалась при ответе.
Утилизация контекстов — то, какую долю контекста модель использовала при ответе на вопрос.
Как подбирать параметры для нарезки на куски:

База данных
База данных это система, которая хранит, индексирует и позволяет обрабатывать запросы для неструктурированных данных, таких как текст, изображения и подобное через числовое представление в виде вектора.
С помощью таких векторов можно делать поиск похожих объектов в базе данных. В нашем случае обычно поиск похожих кусков текста на пользовательский запрос.
Ключевые факторы выбора базы данных:
Открытая ли база или закрытая
Язык программирования, на котором можно создать клиента для использования базы данных
Лицензия
Фичи для организаций:
лимиты
пользовательская аутентификация
многое другое
Продуктовые фичи:
точный поиск
приближенный поиск — когда можно пожертвовать качеством для ускорения и масштабирования
префильтрация — когда до векторного поиска нужно уменьшить количество кандидатов
постфильтрация — дополнительный фильтр для улучшения точности результатов
гибридный поиск
поддержка разреженных векторов
поддержка поиска напрямую по тексту, например, через bm25
Возможность инференса моделей эмбеддингов.Например, sentence transformers, Mixedbread, BGE, OpenAI.
Возможность инференса модели реранкера
Скорость добавление новых объектов
Скорость поиска
индексация
кэширование
другие оптимизации
Затраты на обслуживание
disk-based базы данных VS in-memory
Serverless базы данных
квантизация эмбеддингов
Поддержка, мониторинг, бэкапы и тд
Выбор эмбеддера
Эмбеддинг — это векторное представление текста (или картинки, звука и тд) в пространстве, в котором похожие тексты отображаются в похожие векторы.

Как можно использовать эмбеддинги:
кодировать вопросы и контексты эмбеддером, чтобы для вопроса находить самые релевантные куски информации
находить few-shot примеры для in context learning (ICL)
определять намерения пользователя, чтобы, например провести по какой-нибудь ветке заготовленного сценария общения, или вызвать какой-нибудь инструмент
На что смотреть при выборе эмбеддера:
размерность векторного пространства
размер модели
перфоманс модели на доменных или общих бенчмарках
открытая или закрытая модель
стоимость
поддержка языков
гранулярность: на уровне слов, предложений, длинных документов
Виды эмбеддингов:
dense векторы-классика
разряженные. Извлекают из текста только самую релевантную информацию, а в других размерностях значения просто 0.Часто используется в задачах со специфической терминологией. Работает примерно как bag-of-words, но обходит многие его недостатки.https://arxiv.org/abs/2109.10086
матрешка эмбеддинги. Позволяют выбирать размерность вектора на инференсе. Можно почитать тут лонгрид про них.

long-context эмбеддинги
Если мы можем эффективно и без потери качества кодировать более длинные куски текста, то будем уменьшать задержку при поиске и косты на хранение векторов, так как их будет меньше.
code эмбеддинги
специально натренированные модели для работы с кодом
Примеры как, на каких задачах измерить качество эмбеддингов:

Метрики рага, которые подходят и для метрик эмбеддера:
Приверженность - то, как сильно ответ похож на контекст, поданный в модель. Чем ниже метрика, тем выше вероятность галлюцинаций.
Полнота - то, насколько полный ответ для заданного вопроса и предоставленного контекста.
Учёт контекстов - то, какая доля контекстов использовалась при ответе.
Утилизация контекстов - то, какую долю контекста модель использовала при ответе на вопрос.
https://www.galileo.ai/blog/mastering-rag-improve-performance-with-4-powerful-metrics - больше про метрики можно почитать тут
Извлечение, поиск
Поиск - это процесс извлечения максимально релевантных кусков текста из базы данных, в которых потенциально находится информация необходимая для ответа на вопрос пользователя.

С одной стороны мы хотим найти как можно больше полезных кусков и предоставить максимально полную картину для LLM, поэтому мы хотим находить не только самые релевантные контексты, но и максимально разнообразные.
Но с другой стороны, чем больше текста вы извлекаем, тем более они зашумленные, менее релевантные, тем самым LLM будет проще галлюцинировать, поэтому важность этапа поиска нельзя недооценивать.
Техники для улучшения извлечения:
Hypothetical document embeddings (HyDE) https://arxiv.org/abs/2212.10496
На вопрос пользователя генерируем с помощью LLM такой текст, в котором гипотетически мог бы содержаться ответ на вопрос.Такой документ будет недостоверным в большинстве случаев, но зато текстовый энкодер сможет построить очень близкий эмбеддинг для реального контекста.
Maximal Marginal Relevance (MMR)
Техника для увеличения разнообразия в найденном множестве контекстов. То есть мы скорее отдадим предпочтение менее релевантному контексту, но еще незнакомому.
Autocut
Смотрим на скоры похожести контекста и определяем так называемые “прыжки” в них, находя самую оптимальную границу между релевантными и менее релевантными контекстами. https://weaviate.io/developers/weaviate/api/graphql/additional-operators#autocut
Recursive retrieval
Нарезаем контекст на более мелкие куски, ищем релевантный, но отдаем более крупный контекст.https://youtu.be/TRjq7t2Ms5I?si=D0z5sHKW4SMqMgSG&t=742 https://docs.llamaindex.ai/en/stable/examples/query_engine/pdf_tables/recursive_retriever.html Похожая техника Sentence window retrieval, где мы возвращаем не кусок, а окно, которое включает наш кусок текстаhttps://docs.llamaindex.ai/en/latest/examples/node_postprocessor/MetadataReplacementDemo.html
SelfQuery
Техника для таких вопросов, где полезно будет сделать фильтрацию по какому-нибудь атрибуту, например, по дате.
Сжатие контекстов
После извлечения самый релевантных контекстов, мы их суммаризируем при условии вопроса юзера с помощью LLM. Таким образом финальная LLM получает на вход более плотную информацию, с минимумом шума, но, скорее всего, довольно полную. Хотя тут мы делаем дополнительные вызовы суммаризатора.
Классический методы поиска
svm
tf-idf
и другие
Выбор LLM
При выборе LLM стоит опираться на то, на каких данных и задачах была обучена модель, с какими языками она хорошо работает. Желательно проверить несколько моделей самостоятельно перед выкаткой, также можно опираться на результаты бенчмарков.
Open-source или проприетарная модель
С одной стороны использование закрытых апи упрощает разработку системы, но с другой стороны возникает вопрос конфиденциальности данных, а также зависимости от внешней апи.
Размер модели
При большем размере растет качество, но растет задержка и падает пропускная способность.
Параметры генерации
Такие параметры как температура, top p, top k, могу сильно влиять на ответы моделей, их креативность и разнообразие.
Способ инференса
Этот вопрос может отпасть, если мы будем использовать закрытые апи, но в случае с моделями, которые мы сами хотим разворачивать и поддерживать, этот вопрос является очень существенным.Так как разные фреймворки инференса поддерживают разные модели, способы оптимизации и ускорения инференса.Основные решения: tensorrt-llm, vllm, tgi, deepspeed-mii.
Few-shot prompting
Показываем несколько примеров, как могут выглядеть ответы.Можно улучшить выбор этих нескольких примеров через поиск ближайших соседей.
Chain-Of-Thoughts
Заставлять LLM генерировать цепочку мыслей и только после размышления давать финальный ответ: “Take a deep breath and let’s think step by step”.
Map reduce
делаем суммаризацию каждого контекста, и только потом по всем суммаризациям генерируем ответ на вопрос.
Map refine
Начинаем с 1го контекста, отвечаем на вопрос по нему, затем обновляем ответ с учетом 2го контекста и так далее.
Thread of Thought
Разбиваем длинные куски текста на контексты, модель извлекает из них релевантную информацию, затем просим модель суммаризировать и проанализировать информацию, а не просто прочитать и понять.
Chain of Note
Примерно то же самое, что и ToT, но здесь для каждого извлеченного куска текста генерируем суммаризацию и оцениваем его релевантность касательно вопроса. И уже на основании таких заметок отвечаем.
Chain of Verification(CoVe)
На запрос генеририруем бейзлайн, далее подбираем проверочные вопросы, отвечаем на них и редактируем ответ.
Эмоциональное давление
Удивительно, но работает все: представь, что ты эксперт, я дам тебе 200 долларов за правильный ответ и др.)
Можно переписывать вопрос с учетом истории, чтобы поиск релевантных контекстов работал корректно.
Можно вместе с вопросом и релевантным контекстом также предоставить доступ LLM к истории чата.
Если чат разрастается, его можно суммаризировать: либо просто извлечь самое главное, либо суммаризировать при условии текущего вопроса, чтобы точно не потерять ничего важного из истории.
веду тг канал @rockaux
лонгриды в телетайпе по DL, NLP, LLM (посты: функции активации, оптимизаторы, регуляризация в DL и другие)
YouTube канал (видео: ML roadmap, DL roadmap)
выступление на митапе Контура по LLM alignment




Работают напрямую с текстом.
https://learn.deeplearning.ai/courses/langchain-chat-with-your-data/lesson/5/retrieval - урок по извлечению от LangChain
Question Answering / generation
На этапе генерации мы отдаем релевантные контексты вместе с вопросом пользователя в LLM, а от нее уже получаем ответ. Это центральный элемент RAG системы, поэтому тут также нужно аккуратно рассмотреть следующие пункты:
Отдельно затронем техники промпт-инжиниринга для улучшения ответов LLM. Они могут помочь сделать ответ информативнее, более персонализированным для юзера, а также уменьшить вероятность галлюцинаций.





Также для того, чтобы еще минимизировать риски галлюцинаций, можно ответ LLM проверять дополнительно на безопасность/адекватность той же LLM, или другими специализированными моделями, или просто регулярными выражениями.
Chat, user experience
Также для некоторых бизнес кейсов важно уметь помнить то, о чем шла речь в предыдущих сообщениях.
Это можно сделать, сохраняя предыдущие вопросы и ответы на них.
Так как RAG это в общение с пользователем, то для дальнейших улучшений, можно в сервис внедрить логику сбора обратной связи: через лайки/дизлайки или открытых форм.
В том числе для большей прозрачности работы сервиса можно давать пользователю доступ к извлеченным контекстам и цепочкам мыслей модели.
А для ускорения работы модели, можно делать кэширование и не нагружать модели по несколько раз.
А также использовать техники оптимизации инференса как эмбеддинг модели (например, вот лонгрид: https://teletype.in/@abletobetable/embeds_ops ), так и LLM (начиная от continuous batching до speculative decoding).
Заключение
Заключения не будет, я устал. Просто красивые схемы из статьи.



Полезные ссылки
Мой тг - @abletobetable
По RAG:
A Survey on Retrieval-Augmented Generation for Large Language Models:
https://arxiv.org/abs/2312.10997
HF blogposts:
RAG vs Fine-Tuning for LLMs: A Comprehensive Guide with Examples
Better RAG 2: Single-shot is not good enough
Better RAG 3: The text is your friend
Mastering RAG series:
How To Architect An Enterprise RAG System
RAG Vs Fine-Tuning Vs Both: A Guide For Optimizing LLM Performance
Advanced Chunking Techniques for LLM Applications
Improve RAG Performance With 4 Powerful RAG Metrics
LLM Prompting Techniques For Reducing Hallucinations
How to Select A Reranking Model
How to Select an Embedding Model
Choosing the Perfect Vector Database
Generate Synthetic Data for RAG in Just $10
Mastering RAG: 8 Scenarios To Evaluate Before Going To Production
DeepLearning.AI courses: