
Зачем и для кого статья?
-
Для тех, кто хочет сделать своего ИИ-помощника, удобный поисковик.
-
Кому интересна тема RAG в целом.
-
Кто хочет понять, как это всё работает изнутри, на живом примере, а не на схеме из учебника.
Здесь будет
-
Личный опыт и точка зрения автора, грабли и находки.
-
Путь наименьшего сопротивления.
-
С чем Вы, скорее всего, столкнётесь и как это решается.
-
Вода и мысли.
Не будет
-
Пересказа тысячи статей о том, что такое RAG и как вектора «отражают смысл». Заголовок некликбейтный — предполагаю, что Вы уже хоть что-то про RAG слышали. Но пару ссылок, что почитать новичку, всё же приложу
-
Кода и разбора фреймворков — это есть в профильной документации
-
Киллерфич и инсайдов для senior RAG-архитекторов
Если Вы совсем новичок в теме, советую сначала прочитать:
https://inllm.ru/rag/vvedenie-v-rag — просто и разжёвано.
https://developers.sber.ru/help/business-development/what-is-rag — аналогично.
Введение
Больше года я занимаюсь реальным RAG проектом в своей небольшой компании. Проект — это ИИ-помощник (сейчас) и агентская система в будущем, со своими тулзами и навыками. Начиналось всё до неприличия просто, безграмотно и наобум. Приход более-менее вменяемых по качеству LLM с одной стороны и бесконечные, однотипные, ранее решённые вопросы от коллег в рабочих чатах — с другой, подтолкнули меня к мысли: здорово бы заиметь своего ИИ-помощника, который будет всё знать и отвечать на все вопросы, экономя вагон времени.
Я зашёл на YouTube и вбил в поиск «Своя база знаний в LLM», нырнул в 1-2 видео из топа, где блогер (англоязычный) показывает, как он запихнул целый документ, аж на 4000 токенов, в LM Studio, и ИИ ему отвечал на основе этого документа. Я сразу побежал ставить LM Studio, загрузил пару моделей 3b/8b и пару побольше, навалил туда 50+ инструкций своей компании, думаю: ну всё, сейчас все проблемы уйдут)0) ага…
Если не считать неприлично долгого времени ответа («съедание промпта» + генерация), то и само качество ответов не годилось для реального пользователя. Ответы вроде похожие на правду, но либо неполные, либо совсем неверные. От такой системы больше вреда, чем пользы. А мне хотелось, чтобы ИИ не просто нашёл очевидный ответ в документации, а-ля «Чек-бокс такой-то находится на такой-то вкладке», а умел в примитивный анализ: «Чек-бокс такой-то не отражается в кабинете пользователя, потому что в настройках отключён режим такой-то». То есть получить вопрос пользователя и самостоятельно диагностировать проблему.
Мои первые шаги

Я начал изучать литературу, читать такие же статьи на Хабре и смотреть всякие лекции на ютубе. Все предлагали так называемый классический — наивный RAG (Naive RAG). Реализация простая: порезать документы на куски, через эмбеддер записать их смысл в виде массива чисел n-размерности в векторную БД, а при вопросе пользователя прогонять его вопрос через тот же эмбеддер и сравнивать косинусную близость топ-N этих кусков документации и вопроса юзера. Кто-то предлагал схему чуть сложнее: подключить BM25 (поиск по ключевым словам, хорошо работает на всяких аббревиатурах, чей смысл нельзя адекватно записать в вектор, например код отчёта «01-ЦФИ.Я2») и реранкер.
Уже полезный, практический опыт
Что я получил? Да, в целом чуть улучшенную в плане экономии токенов версию поисковика, чем запихнуть всё разом в LLM. Но понимания доменной области и базового навыка диагностики/аналитики ИИ это не дало. Если Вам нужно просто найти фрагмент(ы) в огромном массиве документов и не более — этот вариант Вам подойдёт. Не усложняйте.
Этот вариант очень хорошо работает и подходит ещё для такого сценария. Например, мы хотим, чтобы ИИ отвечал на вопросы, на которые ранее уже отвечали в рабочих чатах, почте, helpdesk и прочих каналах. Мы выгружаем все тонны переписки и батчами (порциями) гоним их через ту же локальную LLM с промптом:
ROLE: Ты специалист по разметке данных.Тебе на вход подаётся сырой кусок переписки из рабочего чата. Твоя задача - отсечь весь мусор (приветствия, поздравления, пожелания приятного аппетита) и найти полезные пары "вопрос-ответ", которые касаются исключительно бизнес/системной логики компании.Ответ считается полезным, если на него есть явная благодарность или обратная связь от задавшего вопрос.Формат ответа:TXT, разделитель между парами - "*****".Пример готовых пар вопрос-ответ:вопрос: Где найти отчёт 01-ЦФИ.Я2 ?ответ: В разделе "Клиенты > Меню > Личная карточка клиента > Отчёты > Отчёты за период операций"*****вопрос: Где найти отчёт 03-ЦФИ.Я3 ?ответ: В разделе "Клиенты > Меню > Личная карточка клиента > Отчёты > Отчёты за период операций"*****вопрос: Где найти отчёт 04-ЦФA.Я5 ?ответ: В разделе "Клиенты > Меню > Личная карточка клиента > Отчёты > Отчёты за период операций"
Этот сценарий фактически позволяет Вам собрать не просто почти готовые кейсы, но и сформировать живой, не синтетический eval-набор для будущей оценки работы системы. И вот здесь наивный RAG показывает свою силу и предназначение.
Продолжаем
Я совершенно случайно нашёл, куда применить такую простую схему, но проблему с пониманием доменной области и аналитикой это не решило. Часть доменных знаний уже содержится в документации, другая часть — только в головах сотрудников компании. Разные тонкости и нюансы, моменты, на которые бот явно должен обращать внимание и в каких-то случаях уточнять. И, конечно, у нас, как и у многих, был огромный пробел в документации: в тепличных условиях всё красиво, но в жизни так не бывает.
Это вторая моя находка, очевидная, но не сразу. Тут я сразу вспомнил статьи про RAG и выражение «Мусор на входе → мусор на выходе» — полностью согласен. Если у Вас нет документации, нет собранных и проверенных сотрудниками пар вопрос-решение — Вам в данный момент нужно заниматься документацией, а не RAG! Магии тут нет, готовьте качественные данные. Отличная связка для такого проекта — AI-инженер + технический писатель.
Что получилось?

Я сделал всю систему как по учебнику, — гибридный поиск(семантика + BM25).
https://inllm.ru/rag/gibridnyy-poisk
Добавил реранкер cohere через OpenRouter.
https://openrouter.ai/cohere/rerank-v3.5
БД — qdrant
https://github.com/qdrant/qdrant
В качестве генератора — deepseek по их API
Сейчас v4-flash, и опционально юзер может выбрать ПРО-режим, где под капотом v4-pro.
Я сразу позаботился о банальных recall, precision, hit rate. И добавил обратную связь от пользователя — либо лайк 👍 и точка, либо 👎 и поп-ап, куда юзер пишет, что не понравилось. Советую сразу делать метрики и обратную связь, но это не самое сложное. Самое сложное — приучить людей оставлять обратную связь: у меня пользователей немного, порядка 30 человек, но обратную связь оставляют только на 15% ответов.
Стало ли лучше? В подходе и архитектуре RAG — да. В качестве ответов ИИ — нет.
Расследование снова указало на документацию и способ подачи найденных чанков в генератор
Мне не нравилось, что ИИ сокращает ответы, хоть DeepSeek к этому и не склонен. В процедурных инструкциях, где важно ЦЕЛИКОМ воспроизвести инструкцию или её часть, генератор выдавал обрубки. Это категорически недопустимо для таких документов, особенно если у Вас финансовая, медицинская, юридическая и прочая ответственность. Не нравилось мне и то, что приходилось угадывать с количеством top-n, top-k, чтобы точно извлечь все нужные фрагменты даже в рамках одного документа. Как пример — справочник отчётов на 200+ кодов и описаний. Под некоторые критерии запроса юзера попадает 1-2 отчёта, под некоторые — 100. Что делать? Сортировать отчёты по содержанию, расположению? Да тоже не вариант. Запрос может охватывать их отовсюду.
Решение найдено — паттерн Child-Parent, но частично, только для процедурных документов или там, где важно видеть картину целиком. Когда мы находим нужный кусок, а отдаём генератору весь документ (флаг True у fullDoc). Дополнительно нужно, чтобы в каких-то документах стиль ответа был немного строже или ИИ обращал внимание на тонкости. Но писать эти тонкости и внутренние моменты в саму документацию нельзя. Решение — METAINFO к каждому документу, который пишете Вы и постепенно, с опытом, корректируете. METAINFO крепится как дополнительный блок при подаче в генератор, например:
{ "source_file": "отчёты.md", "chunk_id": 4, "fullDoc": true, "metainfo": "Это блок метаинфо, его не отображаем юзеру! При ответе всегда сообщай пользователю о наиболее похожих отчётах, если ты не нашёл полностью соответствующих его запросу"}
Приведя в порядок документацию, добавив частично паттерн child-parent и контекстную инженерию(metainfo), я получил реально полезную систему, которую живые пользователи высоко оценивают (94% Positive Rate, отношение 👍 к 👎).
https://inllm.ru/prompting/kontekstnaya-inzheneriya

Итого
— 50 документов
— Гибридный поиск
— Реранкер
— Метрики
— Обратная связь
— Контекстная инженерия
— Child-Parent подход
А нужно ли?
Коротко: в данном случае — НЕТ. Хоть это и масштабируемо, НО я сделал трактор для клумбы. А клумбы пока нет. И будет ЛИ? Зато получил опыт, как полезный, так и негативный. Хотя негативный тоже считаю полезным.
Меня осенило, что всё это можно было решить другой, более простой схемой — с наименьшим количеством точек отказа. Избавиться от векторной БД, эмбеддера и реранкера. Как? Использовать LLM в два вызова.
На первом подаём так называемую карту всех документов, map.json:
[ { "file_name": "отчёты.md", "summary": "Здесь описаны все отчёты компании «Рога и копыта». Используем этот документ, если пользователь спрашивает про отчёты, где посмотреть продажи, или присылает непонятные коды - И1.ЦФ.02 и похожие", "key_words": "отчёты, % комиссии, за период операций", "metainfo": "Это блок метаинфо, его не отображаем юзеру! При ответе всегда сообщай пользователю о наиболее похожих отчётах, если ты не нашёл полностью соответствующих его запросу" }, { "file_name": "отчёты2.md", "summary": "Здесь описаны все отчёты компании «Рога и копыта». Используем этот документ, если пользователь спрашивает про отчёты, где посмотреть продажи, или присылает непонятные коды - И1.ЦФ.02 и похожие", "key_words": "отчёты, % комиссии, за период операций", "metainfo": "Это блок метаинфо, его не отображаем юзеру! При ответе всегда сообщай пользователю о наиболее похожих отчётах, если ты не нашёл полностью соответствующих его запросу" }]
И промпт роутера:
ROLE: Ты роутер в RAG-системе.Ты на вход получаешь вопрос пользователя и map.json.Твоя задача - определить, какие документы могут ответить на вопрос пользователя, и вернуть в формате JSON список документов. Если явных документов нет - верни пустой массив [].
На втором этапе берём список документов от роутера и подаём вместе с вопросом пользователя в генератор. И всё.
Я сделал такой движок как резервный и подключил к своей RAG, сравнил на реальных пользователях — и качество ретрива (роутера в данном случае) и генератора осталось таким же. Причём карту сгенерировала ИИ, я лишь немного поправил её. А если картой заняться основательно, думаю, на таком объёме документов она побьёт векторный вариант RAG.
У этого подхода есть явные плюсы:
-
Он хорошо прослеживается и корректируется человеком.
-
Он прост в управлении при небольшом количестве документов.
-
Он отлично кешируется LLM и выходит в копейки.
-
Не требуется эмбеддер, реранкер, дополнительная БД.
-
Генерация и управление картой легко автоматизируются с помощью той же LLM.
Минусы очевидные тоже есть:
-
Сложно масштабируется. Хотя можно делать вложенность, карты в карту. Но объективно потолок — 300-500 документов на карту.
-
Лишний вызов.
-
Ошибка роутера.
Я рекомендую всегда идти от простого к сложному. Не нужно сразу строить мультиагентную систему с тулзами к векторному поиску, обходом графа и прочим, если у Вас нет нужного объёма документации, даже если эксперты будут уверять что Вам это нужно. Для своей задачи — свой инструмент.
Еще один кейс
Вы можете сделать ИИ-Асистента, но обыватель будет использовать его так, как привык использовать все ИИ. Задавать вопросы, уточнять. Я сначала сделал бота без мульти-диалогов, — Multi-Turn. То есть если пользователь захочет что-то уточнить, его уточнение пойдет в ретривер и ретривер без контекста предыдущих сообщений будет искать ответ. Выглядит так:
user: Какие есть отчеты по продажам? [система ищет вектора похожие на {Какие есть отчеты по продажам?}] ИИ: A1, A5, A7user: А еще? [система ищет вектора похожие на {А еще?}]
И вот тут грабли, казалось бы Вы так и планировали, но юзер ждёт привычный опыт, а не новый чат на каждый вопрос.
Как это решается?
Ответ: Прикреплением предыдущих сообщений и Рерайтером
Рерайтер — та же LLM, которая видит вопрос пользователя и предыдущий диалог и решает нужно ли переформулировать вопрос пользователя? Относится ли он к предыдущему диалогу?
Переформулирование запроса (query rewriting) — Переписывание запроса агентом перед поиском: раскрытие аббревиатур, починка формулировки, подбор терминов ближе к языку документов. Один большой нечёткий запрос почти всегда проигрывает нескольким точным.
Роль рерайтера может выполнять роутер. Так же если пользователь задал мульти-вопрос, можно реализовать его разбиение на отдельные вопросы и каждому искать похожие чанки.

А сколько это стоит?
У меня 50 инструкций, общий объём с метаинфой ~150.000 токенов. Но для удобства расчёта возьмём 100 документов, ~300.000 токенов.
Токенайзер https://inllm.ru/osnovy/tokenizatsiya
Беру deepseek https://api-docs.deepseek.com/quick_start/token_usage
Курс доллара 75 ₽.
|
Тип токенов |
Без кэша, ₽/1M |
С кэшем, ₽/1M |
|
Входящие (input) |
10,5 ₽ |
0,15 ₽ |
|
Исходящие (output) |
31,5 ₽ |
0,22 ₽ |
Я протестировал более чем на 1500 реальных ответов пользователям. В среднем для ответа нужно 1-4 документа + промпт + карта на первом вызове. Если всё сложить и усреднить, стоимость ответа получается 30 копеек. На векторном поиске + реранкер + генерация — 46 копеек. Чанками в кэш попасть сложно.
Своё железо, аренда или облако?
Тут уже зависит, насколько чувствительные у Вас данные и какая позиция руководства. Количество запросов, в том числе одновременных: если параллелить, нужно более сильное железо. Нужна ли сильная модель, как v4, или, может, Вам хватит условной 8-30b? Мне они не очень понравились, но тут нужно тестировать лично Вам. К тому же своё железо нужно обслуживать, аренду — администрировать. С облаком заплатил и пользуйся.
В моём случае аренда той же A4000 для qwen3.6-35b-a3b стоила бы порядка 30.000 в месяц. Это ~100.000 запросов к куда более сильной v4-flash. У меня нет такого объёма даже с автотестами.
В общем, считайте под свои нужды.
А может, вообще всё в контекст?
Мог бы расписать то, что уже сто раз расписано, — про потерю контекста в центре и прочие нюансы.
Можно почитать тут https://inllm.ru/rag/rag-vs-dlinnyy-kontekst
Но я бы не исключал такого варианта: если у Вас до 200-300к контекста, с кэшированием это будет стоить копейки. Насколько удовлетворит качество? В каждом случае индивидуально, я лично не тестировал, хотя в планах есть. Обязательно проверяйте — возможно, Вам этого хватит и по скорости, и по качеству. Если цель минимальными усилиями решить небольшую задачу и сделать простого чат-бота — делайте так. Я понимаю, что за такой совет от AI-инженеров может прилететь, но это моя точка зрения 🙂
Что же выбрать?
Лично я бы начал в такой последовательно, учитывая свой опыт
|
Ваш случай |
Что брать |
Почему |
|
Нет нормальной документации |
Сначала — займитесь документацией |
Мусор на входе → мусор на выходе |
|
Документация влезает в контекст (до ~200-300к токенов) |
Всё в контекст + кэш |
Ни БД, ни эмбеддера, ни роутера. С кэшем — копейки. Просто попробуйте |
|
До ~300-500 документов |
LLM-роутер по map.json (2 вызова) |
Прослеживается, корректируется руками, отлично кэшируется. Без вектора и реранкера |
|
Десятки тысяч документов, нужен поиск по фрагментам |
Гибридный поиск (вектор + BM25) + реранкер |
Классика. Масштабируется туда, где карта в контекст уже не влезает |
|
Связанные сущности, вопросы «кто с кем» и «как это влияет на то» |
Graph RAG |
Граф держит связи, которых нет в отдельных чанках |
Главное правило: идти снизу вверх по этой таблице, а не сверху вниз. Не повторяйте мой путь.
Ещё пара наблюдений из практики
-
Эмбеддер. На русском языке лучшие результаты у меня показал qwen3-4b. Пробовал BGE и другие — qwen зашёл. Русские не пробовал.
-
Бюджетный генератор. Из недорогих моделей рекомендую ту же серию qwen: можно брать на OpenRouter либо официально у Alibaba. Их много, идите по возрастающей по цене и тестируйте на своей клумбе.
-
Обратная связь и eval. Делайте обратную связь от пользователей с самого начала и собирайте живой eval-датасет. И сразу автоматизируйте тестирование — чтобы после каждого изменения видеть по цифрам, стало лучше или хуже, а не «по ощущениям».

Заключение
Возможно мой кейс и путь опытным RAG мастодонтам покажутся глупыми или местами абсурдными, но таков был мой путь. Я открыт к обсуждению, полезным советам, а так же к критике, возможно она спасёт от еще не обнаруженных мной граблей. Я стараюсь так же читать кейсы других людей на ХАБР`е и изучать новое. Порой из личных кейсов можно подчерпнуть больше полезной информации чем из научных статей, по этому пишите:)
ссылка на оригинал статьи https://habr.com/ru/articles/1049344/