Как создать copilot с нуля?
С Дмитрием Браженко (Microsoft) построили RAG с нуля и улучшили его с помощью механик и эвристик.
Как оценивать качество поиска? Как перейти от варианта «на коленке» к продакшну? — вместе найдем ответы на эти наболевшие вопросы.
Хочешь настроить модель на своём корпусе? Читай статью до конца, скачивай код и общайся с моделью на основе своих материалов!
YNDX Family & UnionVK
Материал подготовлен на основе онлайн-митапа YNDX Family и UnionVK, сообществ текущих и бывших сотрудников Яндекса и группы VK. А полную запись встречи можно посмотреть по ссылке на YouTube канале.
Как заставить твои данные заговорить с тобой без шизофрении и галлюцинаций?
Сегодня мы построим RAG-систему, которая сможет разговаривать с тобой на основе какого-либо локального знания, например, списка источников твоей курсовой.
В качестве LLM у нас сегодня – модель от Open AI. Но можно использовать и другие.
Этап 1. Эмбеддинги и для чего они нужны
Предположим, у нас есть набор данных, например, несколько абзацев текста. Мы хотим найти в них информацию, релевантную нашему запросу. Можем запустить поиск по ключевым словам. Но тогда мы ограничим себя в функционале: например, машина не сможет найти синонимы.
Есть альтернатива – векторный поиск с помощью эмбеддингов.
🤖Как это работает: Мы выражаем наш текст через вектор. Последние не несут особого смысла, но имеют несколько важных характеристик. Например, векторы двух схожих по семантике высказываний будут ближе, чем те, значениях которых никак не связаны.
Давайте разберём работу эмбеддингов на примере корпуса из нескольких предложений.
Вектор каждого предложения имеет 384 числовых значения (самое нижнее число).
На основе этих данных строим матрицу похожести по парам для каждого из предложений.
Что мы видим?
Во-первых, значения соседних квадратов в левом верхнем углу показывают, что предложения 1 и 2 из предыдущего скриншота связаны сильнее (0,83), чем первое и третье (0,11).
Во-вторых, предложения «Python is a cool programming language» и «I’m cooking python for breakfast right now» (интересно пообщаться с таким персонажем😀), хоть и содержат схожие элементы, но модель понимает: речь идёт о совершенно разных ситуациях.
Таким образом, мы доказали: механизм эффективен.
Эмбеддинговых моделей очень много. Некоторые натренированы на программистских запросах, другие – хорошо работают с Q&A. Рассмотрим последний тип подробнее.
Модель ниже анализирует, какое из трёх предложений наиболее релевантно запросу: «What is the US capital?»
Индекс similarity говорит, что последнее высказывание ближе всего по смыслу. Попробуем изменить запрос. Видим, что модель тоже неплохо справляется 🙂
Таким образом, если ты правильно подберешь эмбеддинговую модель, у тебя появится сильный инструмент для поиска семантических сходств.
Этап 2. Базы данные векторов (Vector DB)
Чтобы хранить наши векторы, нужна база данных, например, lancedb. Она наиболее удобна при локальном использовании. Для более тяжёлых систем существуют специфические решения – универсального ответа здесь нет.
Но пока остановимся на lancedb и сохраняем все эмбеддинги в базу данных.
Попробуем извлечь похожие предложения по запросу cooking.
Как видим, модель справилась неплохо. Теперь зададим промпт python programming. Видим, что в нижнем запросе программа не справилась с многозначностью нашего питона 🐍
Чтобы улучшить результат, нам нужен другой подход.
Этап 3. Cross Encoder как альтернатива эмбеддингам
Напомню: до этого мы с тобой ранжировали несколько вариантов по степени схожести. Но можно поступить по-другому. Используя Cross Encoder, зададим вопрос, похожи ли соотносимые предложения. Пересчитаем значения для наших предложений.
Видим, что первые 2 предложения ожидаемо больше связаны с программированием, чем последняя. Теперь модель нас понимает.
😬 Минус: чем больше предложений мы получаем на входе, тем тяжелее и дольше идёт процесс.
Два подхода легко комбинировать: с помощью эмбеддингов ты можешь извлечь предварительный набор предложений, а дальше, с помощью Cross Encoder, определить лучшие варианты в зависимости от результата (score).
Этап 4. Нешуточная борьба со здравым смыслом
Итак, мы создали движок для поиска документов. Давай представим, что ты пользователь, который задаёт запрос в чат. Машина понимает, что нужно взять информацию из базы знаний, а после этого вернуться с ответом, уточнением или выполненным действием (распечатать что-то, отправить письмо на почту и т.д.).
Иногда чат начинает галлюцинировать – доставать знания не из твоих данных, а из обучающего датасета или, что ещё хуже, изобретает это знание самостоятельно.
Как бороться с этим?
Интересный способ валидации – работа с фейками 🙂
Вот их примеры:
Давай поставим эксперимент. Загрузив наш набор «знаний» в базу данных, вводим запроc на основе первого предложения: «Пингвины могут полететь, если съедят достаточно рыбы». Добавляем требование ясности и чёткости формулировки и получаем ответ (в самом низу):
Наша модель ориентировалась именно на загруженные в неё знания – галлюцинаций нет. Значит, мы хорошо настроили модель и написали промпт.
Если бы модель ответила верно с точки зрения здравого смысла, значит, она бы опиралась на данные из других источников. Это не то, что мы просим.
В случае ошибки следует усовершенствовать промпт. Лучше добавить фильтры или разработать для этих целей специальную метрику, чтобы ускорить работу. Но универсального способа улучшения нет.
Теперь ты можешь построить LLM, которая будет выполнять эту последовательность действий и сама оценивать, насколько верно дан ответ. Затем считаем score и делаем вывод, работает ли модель.
Этап 5. Работа с большими данными: усложняем пайплайн
Следует понимать, что реальные данные устроены гораздо сложнее, чем те, с которыми мы работали выше. Чаще всего модель работает с многостраничными файлами с картинками и таблицами.
Есть несколько подходов, которые позволят тебе выйти на новый уровень.
Рассмотрим один из них.
- Мы разбиваем текст на блоки и для каждого из них создаём эмбеддинги.
- Загружаем их в базу данных и ищем схожие по смыслу кусочки.
😬 Минус: Цельная мысль может быть разрезана и помещена в разные блоки (кусочки предложений или абзацы, связанные семантически).
Как избежать этого:
- После деления на блоки лучше скармливать их не по одному, а вместе с предыдущим и последующим, чтобы модель не теряла взаимосвязи. Конечно, база знаний будет в 3 раза тяжелее, зато ответ – точнее.
- Разделить текст так, чтобы блок был равен главе. Перед каждой из них поместить summary и работать по эмбеддингам этих данных.
А теперь – к практике 💪
В качестве сложного файла возьмём pdf-ку про нутрициологию. Разрежем её на блоки, посчитаем эмбеддинги и сохраним в базу знаний. В результате мы получим таблицу с данными.
- Задаём вопрос: «Вреден ли алкоголь для здоровья?»
- Извлекаем знания, схожие с запросом (после слова result).
- Получаем куски текста, которые сформулированы криво, но хотя бы релевантны нашим требованиям.
Зафиксируем это знание. К нему мы вернёмся позже.
Этап 6. Создание агента
Агент должен интеллектуально взаимодействовать с локальными знаниями. Его работа сложнее, чем поиск релевантной информации. А ещё агент может использовать несколько источников: интернет, различные api и файлы определённых форматов.
Логика: В качестве источника для логики мы будем использовать библиотеку langchain.
Инструменты: Во-первых, у нас есть search tool, который ищет данные в интернете, а во-вторых, fake facts tool, который извлекает данные из нашей базы фейков.
Отключим пока инструмент поиска по открытым источникам и зададим любимый вопрос про пингвинов 🐧 🥰
Вот что делает агент:
Первым делом он обосновывает своё действие. Затем обозревает базу данных и находит наш фейк про рыбов 🐟
После этого начинает думать. В процессе великой думы агент отрицает фейковое знание. А это плохо. Нужно усовершенствовать результат. Самый простой способ – переформулировать запрос. Давай быканём на агента и уточним наше желание:
Результат налицо!
Но есть и более продвинутый вариант: проанализировать промпты, которые загружены в агент, и кастомизировать его под нашу задачу.
Этап 7. Работа с агентом на основе pdf-файла
Вернёмся к документу о нутрициологии. Задаём те же параметры:
Формулируем запрос о вреде алкоголя:
Давай посмотрим, что делает агент:
Агент проанализировал знание и пришёл к выводу: алкоголь не особо полезен, но его умеренное употребление может быть.
Итоги
Мы сделали первые шаги в построении RAG: поработали с эмбеддингами, базами данных и Cross Encoder, узнали, как работает агент.
В заключение хочу добавить: обученные под конкретную задачу RAG эффективнее глобальных LLM. Ведь последние чаще галлюцинируют, да и с постоянным обновлением знаний большие модели работают хуже.
Так что будьте точны в своих желаниях – и будет вам счастье!
Кстати, код из туториала можно воспроизвести у себя. Скачать его можно тут 😉
Хочешь продолжить свой путь в изучении LLM? Оставайся с командой AlumniHub! Прикоснуться к самым передовым знаниям из мира AI тебе помогут наши материалы.