Половина разговоров про AI-ассистентов заканчиваются одним и тем же: «Он хорош, но не знает наших внутренних документов». Кто-то скажет — просто дай ему контекст в промпте. Пробовал. Работает до определённого момента — пока у тебя пара страниц. А если knowledge base на 300 мегабайт?
RAG расшифровывается как Retrieval-Augmented Generation. Идея простая: LLM отвечает не только на основе своих весов, а сначала находит релевантные куски из твоей базы знаний и подсовывает их модели. Как если бы ты на экзамене сначала открыл конспект, а потом отвечал.
Звучит нехило. Реализация — отдельная история.
Где я наступил на грабли
Первый свой RAG я собрал за вечер. Залил PDF-ы с документацией продукта, прикрутил векторный поиск через ChromaDB, подсунул всё это в GPT-4. И началось.
Запрос «как настроить интеграцию с 1С» — модель берёт кусок из раздела «быстрый старт», хотя в базе лежит отдельный документ с пошаговой инструкцией именно для этой связки. Запрос «сроки поддержки версии 3.2» — выдаёт данные из устаревшего FAQ, хотя рядом лежит свежая таблица совместимости.
Дело в том, что векторный поиск ищет по семантической близости — а это не одно и то же, что поиск по релевантности. Не в поиске была проблема. В том, что я вообще не понял, как он работает.
Из чего на самом деле состоит RAG
Когда я разложил свой первый подход на части, оказалось, что пропустил пол-системы.
Chunking — разбиение документов на куски. Тут есть тонкость: если резать тупо по 500 токенов, легко разорвать таблицу, список шагов или абзац с причинно-следственной связью. Я пробовал фиксированный размер, рекурсивное разбиение по абзацам, разбиение по предложениям с нахлёстом. Лучше всего сработал semantic chunking: бьёшь по логическим границам — заголовки, абзацы, — потом мержишь маленькие куски между собой.
Дальше — эмбеддинги. Я использовал text-embedding-3-small от OpenAI, есть и open-source варианты: BGE, E5. Выбор модели влияет больше, чем кажется. Модель, обученная на русском, работает с русскими текстами заметно лучше универсальной — это не теория, это сразу видно на живых запросах.
На практике хорошего chunking и эмбеддингов недостаточно. Нужен гибридный поиск: семантика плюс обычный BM25. Плюс фильтрация по метаданным — дата, тип документа, отдел. Это подняло качество ответов примерно на 20%.
И последний слой, который большинство пропускает — reranking. Retrieval нашёл топ-20 кандидатов, дальше отдельная модель — Cohere Rerank или аналог — переранжирует их и оставляет 3-5 самых точных. Latency растёт, но ответы того стоят.
С чем я возился дольше всего
С индексацией. Казалось бы — загрузил, разбил, векторизовал. Но когда документов десятки тысяч, сразу появляются вопросы: как отслеживать обновления? Как переиндексировать только изменённые куски? Как не потерять контекст между файлами?
Я пришёл к parent-child chunks. Большой документ бьётся на маленькие чанки, но каждый чанк хранит указатель на родительский документ. При поиске сначала нахожу релевантные чанки, потом достаю родительский документ целиком — модель получает полный контекст, а не вырванный кусок.
Ещё одна история — мусорные документы. Таблицы, картинки, неструктурированные скриншоты. Векторная база не понимает таблицу как таблицу. Я перешёл на подход, где таблицы извлекаются отдельно и сохраняются вместе с заголовками и структурой — не просто куском текста. Документы со сканами или вовсе убираю из базы, или пропускаю через OCR. Без этого шага индексация превращается в шум.
А как же LangChain и LlamaIndex?
Спрашивают постоянно. Мой опыт: LangChain хорош для прототипа, но когда начинаешь смотреть под капот — там много магии, которая ломается на ровном месте. LlamaIndex гибче и прозрачнее. Но если совсем честно, для большинства моих задач хватило бы сырого решения на 500 строк Python: загрузка через unstructured, хранение в pgvector, эмбеддинги через API. Без фреймворка проще дебажить — ты точно знаешь, что именно работает, а что нет.
Фреймворк — это ускоритель. Если ты уже понимаешь, как работает retrieval, он сэкономит время. Если не понимаешь — просто спрячет грабли. А наступишь на них всё равно.
Что в итоге
RAG — инженерная система, где качество зависит от каждого звена: как порезаны документы, какая модель эмбеддингов, какая стратегия поиска и как сформулирован вопрос. Один и тот же набор данных может давать совершенно разные ответы в зависимости от того, как ты его подготовил.
Мой текущий пайплайн выглядит так: загрузка → парсинг с отдельной обработкой таблиц → семантическое разбиение → гибридный поиск → reranking → подмешивание в контекст с указанием источника. На большом корпусе это даёт ответы, которые не просто правдоподобно звучат, а соответствуют тому, что реально написано в документах.
Если есть конкретная задача — кидай, попробуем разобраться вместе.
