Сижу как-то ночью, кормлю LLM-ку кусками документации и думаю: а ведь полгода назад я бы даже не поверил, что это будет моя рабочая рутина. Тогда я считал, что большие языковые модели — это такая штука, которая сама всё знает. Нажал кнопку — получил ответ. А потом столкнулся с задачей, где модель уверенно врала про внутренние API нашей компании, которых вообще не существовало. Пришлось разбираться, что такое RAG и почему без него в продакшене никуда.
Почему «просто спросить модель» не работает
RAG — это Retrieval-Augmented Generation, если по-научному. Если по-человечески — это когда ты не надеешься на память модели, а сначала ищешь релевантные куски текста в своей базе, и только потом просишь модель сгенерировать ответ на их основе.
Проблема банальная. LLM обучена на данных до определённой даты, а наша внутренняя документация обновляется каждый день. Логично, что модель про неё ничего не знает. Можно, конечно, дообучать — но это дорого, долго, и каждый раз, когда меняется один абзац в одном документе, придётся гонять новый цикл обучения. Безумие.
RAG решает задачу элегантнее. Ты не меняешь модель — ты меняешь то, что ей «скармливаешь» в промпте. По сути, модель получает свежий контекст из твоей базы и работает уже с ним.
Из чего состоит RAG — без магии
Когда я первый раз полез собирать такую систему, мне казалось, что там что-то невероятно сложное. Оказалось — три кита: индексация, поиск, генерация. Всё.
Первый кит — индексация. Берёшь документы (PDF, Markdown, базу знаний в Notion, тикеты из Jira — что угодно), режешь на чанки, превращаешь каждый в вектор через эмбеддинг-модель и складываешь в векторную базу. Это и есть твоя «база знаний». Звучит просто, но дьявол в деталях.
Второй кит — поиск. Когда приходит запрос, ты точно так же превращаешь его в вектор и ищешь ближайшие по косинусному расстоянию чанки. Топ-5, топ-10 — сколько влезет в контекстное окно, столько и берёшь.
Третий кит — генерация. Скормливаешь модели промпт вида «вот контекст, вот вопрос пользователя, ответь на основе контекста, если ответа нет — скажи, что не знаешь». Всё, магия случилась.
Где я обжёгся: размер чанков и метаданные
Начал с наивного подхода. Взял текст, порезал по 500 символов, закинул в эмбеддинги. Протестировал — получил какую-то ерунду. Модель выдавала ответы, которые вроде бы про то, но какие-то обрубленные, без начала мысли.
Час потратил, чтобы понять: при разрезке по 500 символов чанк может оборваться посередине предложения, посередине логической мысли. Эмбеддинг такого куска получается «размазанным» — он не про конкретную тему, а про всё сразу.
В итоге перешёл на семантическую нарезку — резать не по символам, а по структуре документа. Заголовки, параграфы, списки. Иногда абзац получался на 200 символов, иногда на 1500 — и это нормально. Главное, что каждый чанк — это законченная мысль.
То же самое вышло с метаданными. Сначала я их игнорировал — зачем, мол, если есть векторный поиск. А потом выяснилось, что часто нужно искать не «что похоже по смыслу», а «вот это из конкретного раздела, конкретного типа документа, за последний месяц». И тут без метаданных никак. Пришлось возвращаться и переиндексировать всё, добавляя в каждый чанк source, section, date, author. Урок: метаданные — не опция, а необходимость.
Какие инструменты брать
Тут я прошёл путь от «напишу всё сам» до «возьму готовое и допилю под себя». Сначала хотел собрать пайплайн на чистом Python с sentence-transformers и FAISS. Получилось, работает, но это прототип на выброс — в продакшен такое не засунешь без боли.
Потом попробовал LangChain. Сразу предупреждаю: первое впечатление — «что за монстр». У него API меняется чаще, чем я успеваю читать changelog. Но когда разобрался — оказалось, что основные концепции (DocumentLoader, TextSplitter, VectorStore, Retriever) стабильны и логичны. Большая часть моих пайплайнов сейчас работает именно на нём.
Векторные базы перебрал три штуки. FAISS — быстро, локально, бесплатно, но это скорее библиотека, чем база. Для прототипа сойдёт, для серьёзной нагрузки — нет. Потом был Pinecone — облачный, удобный, но ценник кусается, когда база переваливает за миллион векторов. В итоге остановился на Qdrant: open-source, self-hosted, нормальный API, фильтрация по метаданным из коробки. Завелось за вечер.
Для эмбеддингов использую модель на базе BGE — она неплохо работает с русским текстом, что для меня было критично. С английскими моделями типа text-embedding-ada качество на русском заметно проседало.
Что я понял про качество поиска
Самая частая боль — не генерация, а именно поиск. Модель может выдать отличный текст, но если в контексте лежат нерелевантные чанки — она с радостью начнёт фантазировать на их основе. И тут уже не до смеха.
На практике помогли три вещи.
Гибридный поиск. BM25 по ключевым словам плюс векторный поиск, а потом объединение результатов. Один ловит синонимы и смыслы, другой — точные термины и имена. Вместе они работают сильно лучше, чем по отдельности.
Reranking. После того как нашёл топ-50 кандидатов, прогоняешь их через модель-кросс-энкодер, которая переранжирует результаты по релевантности запросу. Заметно улучшает точность топ-5, который реально идёт в промпт.
Перефразирование запроса. Часто пользователь пишет «не работает авторизация», а в базе это описано как «ошибка 401 при входе в систему». Если прогнать запрос через LLM и развернуть его в несколько вариаций — поиск становится заметно стабильнее.
Честные выводы
RAG — не серебряная пуля. Видел проекты, где внедряли RAG, вбухивали кучу ресурсов, а потом удивлялись, почему ответы всё равно так себе. Обычно проблема не в технологии, а в данных. Мусор на входе — мусор на выходе. Если в базе знаний бардак, устаревшие статьи, противоречивая информация — никакой RAG это не исправит.
С другой стороны, когда данные приведены в порядок, чанки нарезаны осмысленно, поиск настроен гибридно — результат впечатляет. Модель перестаёт фантазировать, начинает опираться на конкретные факты, и ты получаешь что-то похожее на настоящего ассистента, а не на генератор правдоподобной чуши.
Сейчас я для себя закрыл этот вопрос так: RAG — это не финальный этап, а фундамент. Дальше можно накручивать агентов, цепочки рассуждений, валидацию ответов, фильтрацию по доступам. Но без хорошего поискового слоя всё это — дом на песке.
Если вы только начинаете — не пытайтесь сразу строить идеальную систему. Возьмите LangChain, Qdrant и пару десятков документов. Соберите минимальный пайплайн, потыкайте, посмотрите, где ломается. Это даст больше понимания, чем любая статья. Включая эту.
