RAG (Retrieval-Augmented Generation) — это когда модель отвечает не «из головы», а сначала находит релевантные куски ваших документов и подставляет их в контекст. Идея простая, но создание RAG-системы, которая действительно отвечает точно, а не гладко врёт с уверенным видом, — это не одна векторная база, а связка из шести компонентов. Ниже — вся архитектура по шагам, простым языком, с примерами для двух типичных задач: медицинской базы вопрос-ответ для врачей и каталога товаров.
1. Подготовка данных: LLM любят Markdown
Первый и самый недооценённый шаг в разработке RAG-систем — то, в каком формате лежат исходные данные. LLM обучены на гигантском количестве размеченного текста, и Markdown — один из самых частых форматов в этой выборке: модели «привыкли» к #-заголовкам, спискам и таблицам и заметно лучше понимают границы смысловых блоков в них, чем в сыром HTML с десятками <div> или в тексте, вытащенном из PDF без разметки.
Практическое следствие: прежде чем строить поиск, приведите документы к чистому Markdown.
Плохо (вытащено из PDF как есть):
Парацетамол Показания: лихорадка боль слабой и средней интенсивности Дозирование
взрослым 500-1000 мг каждые 4-6 часов не более 4 г в сутки Противопоказания
тяжелая печеночная недостаточность
Хорошо:
## Парацетамол
### Показания
Лихорадка, боль слабой и средней интенсивности.
### Дозирование (взрослые)
500–1000 мг каждые 4–6 часов, не более 4 г в сутки.
### Противопоказания
Тяжёлая печёночная недостаточность.
Второй вариант — это не только «красивее». Заголовки ### дают модели готовые границы для сегментации, а таблицы Markdown (| Препарат | Доза | Кратность |) эмбеддинги и LLM разбирают на структурированные пары ключ-значение почти без ошибок, в отличие от той же таблицы, слитой в один абзац.
Для карточки товара тот же принцип: название, характеристики и наличие — отдельными заголовками или Markdown-таблицей, а не одним маркетинговым абзацем текста.
2. Правильная сегментация (чанкинг)
Сегментация — это разбиение документов на чанки, которые попадут в векторную базу. Частая ошибка на старте разработки RAG-систем — резать текст по фиксированному числу символов (например, каждые 500 символов), не глядя на структуру. В результате в один чанк попадает конец одного раздела и начало следующего, а модель получает на входе смысловой мусор.
Правило проще, чем кажется: чанк = одна законченная мысль, а границей служит структура документа — заголовок, пункт списка, строка таблицы.
- Медицинская база вопрос-ответ: один чанк = один вопрос с полным ответом, а не «половина ответа влезла, половина обрезалась». Если ответ длинный (например, схема лечения по шагам), не разрезайте пошаговый список между чанками — он должен либо помещаться целиком, либо иметь чанк-заголовок с кратким резюме и ссылкой на полный текст.
- Каталог товаров: один чанк = одна карточка товара (или одна её секция — например, отдельно характеристики и отдельно комплектация для громоздких карточек оборудования). Никогда не смешивайте две разные модели в одном чанке — при поиске «максимальная нагрузка на ось» система не должна путать характеристики двух разных погрузчиков.
Ориентир по размеру: 200–500 токенов на чанк для плотного технического текста (медицина, характеристики оборудования) и до 800 для описательного текста. Слишком мелкие чанки теряют контекст, слишком крупные — размывают релевантность поиска.
3. Гибридный поиск
Чистый векторный поиск ищет по смыслу — и именно поэтому он плохо справляется с точными строками: артикулами, кодами МКБ-10, номерами партий, аббревиатурами. Эмбеддинг «видит» МКБ-10 J18.9 и воспаление лёгких неуточнённое как близкие по смыслу фразы, но если пользователь ищет ровно код J18.9, векторный поиск не гарантирует, что нужный документ окажется в топе.
Решение — гибридный поиск: результаты классического полнотекстового поиска (BM25 или обычный keyword-индекс) и векторного поиска объединяются, чаще всего через Reciprocal Rank Fusion (RRF):
def reciprocal_rank_fusion(vector_results, keyword_results, k=60):
scores = {}
for rank, doc_id in enumerate(vector_results):
scores[doc_id] = scores.get(doc_id, 0) + 1 / (k + rank + 1)
for rank, doc_id in enumerate(keyword_results):
scores[doc_id] = scores.get(doc_id, 0) + 1 / (k + rank + 1)
return sorted(scores, key=scores.get, reverse=True)
- Медицина: запрос «дозировка амоксициллина детям» должен находить документ и по смыслу («антибиотик для ребёнка»), и по точному термину «амоксициллин» — гибридная схема ловит оба случая, чистый vector search иногда промахивается мимо конкретного действующего вещества.
- Каталог товаров: запрос по артикулу
SKU-48120или моделиDL-500X— это ровно тот случай, где keyword-поиск обязателен: у случайного набора букв и цифр нет «смысла», который поймает эмбеддинг.
4. Ранжирование результатов
Гибридный поиск отдаёт достаточно широкий список кандидатов (обычно top-20–50) — но не все из них одинаково релевантны, а в контекст модели можно передать реально 3–8 чанков (иначе контекст «размывается», см. следующий пункт). Здесь нужен reranker — отдельная модель (часто cross-encoder, например bge-reranker или Cohere Rerank), которая умеет точно сравнивать пару «вопрос — чанк», а не полагается на приблизительное косинусное сходство векторов, посчитанное заранее.
Разница на примере: на запрос «можно ли беременным ибупрофен» векторный поиск может поднять в топ общий документ «Нестероидные противовоспалительные препараты: обзор» просто потому, что там много релевантных слов, тогда как более узкий документ «Ибупрофен: противопоказания» с точным ответом окажется на 8-м месте. Reranker, оценивающий пары напрямую, обычно ставит его на первое.
Практическая схема: гибридный поиск возвращает 30 кандидатов → reranker пересчитывает релевантность → в модель уходят top-5.
5. Настройка промптов
Даже с идеальным поиском модель может проигнорировать найденный контекст и ответить «из головы» — если явно не запретить. Системный промпт для RAG должен жёстко фиксировать три вещи: откуда брать ответ, что делать при нехватке данных и как оформлять ссылку на источник.
Отвечай ТОЛЬКО на основе текста ниже в блоке «Контекст».
Если ответа в контексте нет — так и скажи: «В базе знаний нет
информации по этому вопросу», не придумывай ответ.
В конце ответа укажи источник в формате [Источник: <название документа>].
Контекст:
{context}
Вопрос: {question}
Для медицинской тематики это не опция, а обязательное требование — врач должен видеть, из какого протокола взят ответ, и модель обязана честно признавать пробелы, а не выдавать правдоподобную, но неверную дозировку.
6. Контроль и очистка контекста
Последний шаг — гигиена того, что реально попадёт в промпт модели. Три частые проблемы и как их лечить:
- Дубликаты. Один и тот же факт может лежать в трёх разных документах (старая и новая версия протокола, например) — если в контекст попадут все три, модель либо путается в противоречиях, либо просто тратит токены впустую. Дедуплицируйте по смысловому сходству перед отправкой в промпт.
- Устаревшие данные. Для каталога товаров это особенно критично: если в векторной базе остался чанк с ценой или наличием месячной давности, RAG уверенно озвучит неверную цену. Синхронизируйте индекс с источником данных (PIM/CMS/1C) при каждом изменении, а не раз в неделю по расписанию.
- «Шумные» чанки не по теме. Даже после ранжирования иногда проскакивает чанк, который формально прошёл порог релевантности, но не отвечает на вопрос. Добавьте порог отсечения (score threshold) — если top-1 после reranking ниже порога, честно верните «ответ не найден», а не притягивайте за уши слабый результат.
Что дальше
Шесть шагов выше — это общий каркас, который работает для любой RAG-системы. Дальше начинаются нюансы, специфичные для домена:
- Если строите RAG над узкоспециализированными данными (например, медициной) и вам важна проверяемость ответов — читайте «Валидация и Self-RAG: как проверить качество RAG-системы»: там про специфичные эмбеддинги, Self-RAG и то, как сделать так, чтобы система понимала структуру документа целиком, а не только вырванный чанк.
- Если строите поиск по каталогу товаров или оборудования — в статье «RAG для каталога товаров: продукты и оборудование» разобраны особенности сегментации карточек, гибридного поиска по артикулам и ранжирования с учётом цены и наличия.
- А если задача шире — сделать так, чтобы вас вообще видели LLM и answer-engines в поиске, — это уже соседняя тема, разобранная в GEO-чек-листе: тот же принцип «LLM любят Markdown» работает и там.
Коротко
Как сделать RAG систему, которая не выдумывает ответы?
Три вещи вместе: гибридный поиск с ранжированием, чтобы в контекст попадали действительно релевантные чанки; промпт с явным разрешением ответить «не знаю»; и валидация ответов на этапе тестирования.
Чем архитектура RAG-системы отличается от «просто добавить векторный поиск»?
Векторный поиск — один из шести компонентов: важны ещё подготовка данных, сегментация, гибридный поиск, ранжирование, промпты и контроль контекста. Слабое звено в любом из них портит итоговые ответы.
С чего начать разработку RAG-системы: с модели или с данных?
С данных. Модель и эмбеддинги можно поменять за час, а плохо подготовленный корпус документов придётся переразмечать вручную.
Нужен ли гибридный поиск, если эмбеддинги и так хорошие?
Да — даже сильные эмбеддинги хуже находят точные артикулы, коды и аббревиатуры, чем обычный полнотекстовый поиск.
Если нужно построить RAG-систему под конкретные данные — пишите нам через форму на сайте, поможем с архитектурой и разработкой.