Как устроен RAG внутри
Внутри любой RAG-системы работают два независимых цикла, и путают их постоянно. Индексация идёт заранее, по расписанию, и о ней никто не думает, пока она не сломается. Ответ на запрос проходится целиком на каждый заданный вопрос, за секунду-две.
Разделять их важно практически: почти все проблемы качества лежат в первом цикле, а замечают их во втором.
flowchart TB
subgraph IDX ["Индексация: заранее и по расписанию"]
direction TB
SRC["Источники «Маркета»: ADR, вики, runbook, задачи, инциденты"]
CHUNK["Разбивка на фрагменты + метаданные"]
EMB["Превращение фрагментов в векторы"]
STORE["Хранилище с поиском"]
SRC --> CHUNK --> EMB --> STORE
end
subgraph ASK ["Ответ: на каждый вопрос"]
direction TB
Q["Вопрос пользователя"]
FIND["Поиск подходящих фрагментов"]
PROMPT["Сборка промпта: вопрос + найденные фрагменты"]
ANS["Ответ со ссылками на источники"]
Q --> FIND --> PROMPT --> ANS
end
STORE -.->|"поиск идёт по готовому индексу"| FIND
Левая дорожка проходится заранее и обновляется по расписанию; правая проходится на каждый заданный вопрос.
Шаг 1. Сбор источников
Заголовок раздела «Шаг 1. Сбор источников»Система забирает документы из мест, где они живут: репозиториев, вики, трекера задач, базы инцидентов. Забирает не разово, а постоянно: как только документ меняется, обновляется и его фрагмент в индексе.
Здесь принимается решение, которое потом дорого менять: какие метаданные тащить вместе с текстом. Минимум, который понадобится почти наверняка:
- Домен и команда-владелец. Без этого невозможна маршрутизация запросов и невозможно спросить с кого-то за качество ответов.
- Дата последнего изменения. Чтобы отличать актуальный регламент от прошлогоднего черновика.
- Ссылка на оригинал. Она попадёт в ответ и позволит человеку проверить.
- Права доступа. Кто имеет право видеть этот документ. Добавить это поле задним числом, когда индекс уже собран и работает, заметно дороже, чем заложить сразу.
- Тип документа. ADR, runbook, задача, страница вики. Пригодится, чтобы при прочих равных предпочитать архитектурное решение обсуждению в тикете.
Шаг 2. Разбивка на фрагменты (чанкинг)
Заголовок раздела «Шаг 2. Разбивка на фрагменты (чанкинг)»Документы не кладут в индекс целиком. Причины две: сорокастраничный регламент выплат селлерам не влезет в контекст вместе с остальными результатами, а его усреднённый «смысл» слишком размыт, и находиться он будет на любой вопрос про деньги и ни на один точно.
Поэтому документ режут на фрагменты (chunks). Резать по числу символов плохо: разрез приходится на середину предложения или отрывает таблицу от её заголовка. Резать нужно по смысловым границам: разделам, подразделам, отдельным пунктам регламента, отдельным функциям в коде.
Практические ориентиры:
- Размер фрагмента: примерно от абзаца до раздела. Слишком мелкие теряют контекст («в этом случае комиссия не удерживается»: в каком случае?), слишком крупные размывают поиск.
- Небольшое перекрытие между соседними фрагментами спасает мысль, оказавшуюся на границе разреза.
- Каждый фрагмент должен нести свой заголовочный путь: «Регламент выплат → Возвраты → Возврат после отгрузки». Иначе, вырванный из документа, он непонятен ни модели, ни человеку.
Резать приходится не всегда: есть отдельный подход, поиск без векторов, где документ вместо нарезки разбирают в дерево разделов. Остальные шаги этой главы в нём тоже устроены иначе.
Шаг 3. Векторы и поиск по смыслу
Заголовок раздела «Шаг 3. Векторы и поиск по смыслу»Чтобы искать по смыслу, а не по буквам, каждый фрагмент превращают в эмбеддинг (embedding), длинный список чисел, координаты текста в пространстве смыслов. Работает это так, что тексты о близких вещах получают близкие координаты. Дальше поиск сводится к геометрии: находим фрагменты, чьи координаты ближе всего к координатам вопроса.
Что это даёт: пользователь спрашивает «когда селлеру приходят деньги», а находится раздел «сроки перечисления вознаграждения продавцу». Ни одного общего слова, но смысл тот же; обычный поиск по словам здесь промахнулся бы.
И сразу о том, что это ломает, потому что здесь половина будущих проблем: векторный поиск плохо работает с точными идентификаторами. Номер задачи PROJ-123, имя таблицы seller_payouts, код ошибки, артикул товара выглядят для векторов просто похожими друг на друга строками без смысла. Спросите «что решили в PROJ-123» и получите фрагменты про соседние задачи. Поэтому в реальных системах векторный поиск почти никогда не используют в одиночку, о чём подробнее на странице Варианты архитектуры.
Шаг 4. Сколько фрагментов брать
Заголовок раздела «Шаг 4. Сколько фрагментов брать»Поиск возвращает не один фрагмент, а несколько лучших, обычно от трёх до десяти. Число выбирается не наугад:
- Мало фрагментов. Высокий риск, что нужного среди них нет. Модель ответит по тому, что дали, и ответ будет неполным, хотя выглядеть будет уверенно.
- Много фрагментов. Контекст наполняется приблизительно подходящим текстом. Работает та же «потеря в середине», о которой рассказано на странице Токены и контекстное окно: нужный фрагмент есть, но тонет среди шести похожих.
Здоровый ориентир: брать немного, но повышать вероятность, что среди них есть правильный. Этим и занимается реранкинг из следующей главы.
Шаг 5. Сборка промпта и ответ
Заголовок раздела «Шаг 5. Сборка промпта и ответ»Последний шаг собирает всё в один запрос к модели. В нём три части: вопрос пользователя, найденные фрагменты со ссылками и инструкция, как с ними обращаться. Инструкция обязательно содержит два требования:
- Отвечать только по приведённым фрагментам. Без этого модель охотно дополнит ответ общими знаниями о том, как «обычно» устроены маркетплейсы, и отличить это от факта про «Маркет» будет невозможно.
- Честно говорить, когда данных не хватает. «В доступных документах этого нет» тоже правильный ответ, и система должна его уметь. Если такого ответа она не даёт никогда, значит, вместо него вы получаете выдумку.
Плюс требование ссылаться на источники в ответе. Это не украшение: по ссылке человек проверит ответ за секунды вместо того, чтобы верить на слово.
Где это ломается чаще всего
Заголовок раздела «Где это ломается чаще всего»Пять типовых поломок, отсортированных по частоте:
- Документ изменили, а индекс не обновили, и система уверенно отвечает по прошлогоднему регламенту.
- Нарезка разорвала смысл: нужный фрагмент нашёлся, но без условия, к которому относился.
- Вопрос задан не тем словарём, что документ, и точного идентификатора в нём тоже нет.
- Нужный фрагмент вообще не попал в выдачу, потому что его вытеснили похожие.
- Фрагменты пришли правильные, но модель ответила «из головы» и разошлась с ними.
Первые четыре относятся к поиску, последняя к генерации. Различать их важно, а как это измерять, разобрано на странице Качество и оценка.
Что дальше
Заголовок раздела «Что дальше»- Варианты архитектуры: лестница от grep до агентного RAG и что каждый уровень чинит.
- Что индексировать: какие источники знаний дают пользу, а какие только шумят.