
Зачем и для кого статья?
-
Для тех, кто хочет сделать своего ИИ-помощника, удобный поисковик.
-
Кому интересна тема 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, A7
user: А еще?
[система ищет вектора похожие на {А еще?}]
И вот тут грабли, казалось бы Вы так и планировали, но юзер ждёт привычный опыт, а не новый чат на каждый вопрос.
Как это решается?
Ответ: Прикреплением предыдущих сообщений и Рерайтером
Рерайтер – та же 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 мастодонтам покажутся глупыми или местами абсурдными, но таков был мой путь. Я открыт к обсуждению, полезным советам, а так же к критике, возможно она спасёт от еще не обнаруженных мной граблей. Я стараюсь так же читать кейсы других людей на ХАБР`е и изучать новое. Порой из личных кейсов можно подчерпнуть больше полезной информации чем из научных статей, по этому пишите:)
Автор: Front-Den


