RAG-пайплайн устроен так: документы загружаются и очищаются, режутся на фрагменты, индексируются; на вопрос система находит подходящие фрагменты, передаёт их модели вместе с вопросом и проверяет ответ; качество каждого этапа отдельно измеряется на эталонной выборке. Модель в этой цепочке стоит почти в конце, а большинство ошибок рождается раньше неё.
Поэтому разговор «какую модель взять» откладывается. Сначала нужно понять, что попадает в индекс, как это режется и как это ищется. Принцип RAG описан в статье «Что такое RAG простыми словами», выбор хранилища для индекса — в статье о векторных базах. Здесь только сам конвейер и его слабые места.
Пять этапов и что на каждом решается
Схема одна, решений на каждом этапе несколько. Таблица показывает, что надо решить заранее и как пайплайн ломается, если оставить решение по умолчанию.
| Этап | Что делает | Что решают заранее | Как ломается |
|---|---|---|---|
| Загрузка | Берёт документы из источников, извлекает текст, убирает мусор и сохраняет метаданные | Какие источники входят, кто владеет каждым, как часто обновлять, что делать со сканами и таблицами | Колонтитулы и оглавления забивают индекс, таблица превратилась в кашу из цифр, в базе две редакции одного приказа |
| Разбиение | Режет текст на фрагменты, которые будут находиться по отдельности | Режем по структуре документа или по длине, есть ли перекрытие, что хранится вместе с фрагментом | Фрагмент оторван от заголовка и непонятно, о чём он; ответ разнесён на два фрагмента, и находится только один |
| Поиск | По вопросу находит нужные фрагменты | Только по смыслу или ещё по точным словам, сколько фрагментов брать, нужно ли переранжирование | Артикулы, номера приказов и сокращения не находятся; находятся похожие, но не те фрагменты |
| Сборка и ответ | Составляет запрос к модели из инструкции, фрагментов и вопроса, получает ответ и проверяет его | Формат ответа, ссылки на источник, поведение при отсутствии ответа в базе | Модель отвечает по общим знаниям, когда фрагмента нет, или ссылается на то, чего в источнике не было |
| Оценка | Прогоняет эталон и показывает, какой этап подвёл | Кто размечает эталон, какие показатели считаются отдельно, что блокирует выкатку | Качество проверили на пяти вопросах, а после правки не проверили вообще |
Загрузка: качество индекса определяется здесь
Индекс не может быть лучше исходных документов. Три вещи делают загрузку серьёзной работой, а не копированием файлов.
- Извлечение текста. Текстовые файлы и документы с разметкой читаются просто. Сканы требуют распознавания и проверки результата, а таблицы и колонки в двух столбцах при извлечении легко теряют порядок. Выборочная проверка двух-трёх десятков документов глазами экономит недели отладки поиска. Как оценивать распознавание, разобрано в статье «Как измерить точность распознавания документов».
- Метаданные. Вместе с текстом хранится название документа, раздел, редакция, дата, владелец и группы, которым документ доступен. Без них нельзя ни показать источник, ни ограничить доступ, ни убрать устаревшее.
- Дубли и редакции. Если в индексе лежат старая и новая редакция регламента, система уверенно ответит по старой. Редакции помечаются, а действующая выбирается по правилу, которое записано, а не угадывается.
Мусор вроде колонтитулов, оглавлений и повторяющихся подписей в письмах убирается на загрузке, иначе он оказывается среди найденных фрагментов и вытесняет полезные. Результат загрузки стоит просматривать глазами: это скучно, но дешевле любой настройки поиска.
Разбиение на фрагменты: где резать
Фрагмент — единица, которую поиск находит и передаёт модели. Слишком короткий теряет контекст, слишком длинный размывает смысл и съедает место в запросе. Универсальной длины нет, и подбирают её по эталону, а не по рекомендациям из чужих статей.
| Способ | Как работает | Когда подходит | Что учесть |
|---|---|---|---|
| По длине | Режет текст на равные куски, при необходимости с перекрытием | Однородный сплошной текст без структуры | Граница может пройти посреди мысли, поэтому нужно перекрытие |
| По структуре | Режет по заголовкам, пунктам и абзацам документа | Регламенты, инструкции, договоры, где пункт самостоятелен | Нужно разобрать структуру; пункты разной длины |
| С заголовком | К каждому фрагменту добавляется название документа и раздела | Почти всегда, если фрагмент без заголовка непонятен | Заголовок съедает часть места, но спасает от «оторванных» кусков |
| Иерархически | Хранит мелкие фрагменты для поиска и крупный родительский для ответа | Длинные документы, где ответ требует соседних абзацев | Сложнее устроены индекс и обновление |
Особый случай — таблицы. Строка таблицы без заголовков столбцов бессмысленна, поэтому к ней добавляют названия столбцов, а иногда строку превращают в короткое предложение. Для нормативных документов с ссылками вида «см. пункт такой-то» стоит хранить связи между фрагментами, иначе ответ получится неполным.
Поиск: по смыслу, по словам и вместе
Поиск по смыслу сопоставляет вопрос и фрагменты как векторы, поэтому находит «сколько дней отпуск» в тексте про «ежегодный оплачиваемый отпуск». Но он слабее там, где нужен точный токен: номер приказа, артикул, фамилия, сокращение. Поиск по словам, наоборот, не видит перефразирования. Поэтому в рабочих системах часто используют оба и объединяют результаты.
- Только по смыслу проще и хватает для текстов без кодов и артикулов.
- Гибридный поиск нужен, если пользователи спрашивают номерами, названиями и сокращениями. Это решается проверкой на эталоне, а не на вкус.
- Переранжирование берёт больше кандидатов и пересортировывает их отдельной моделью, ставя лучшие первыми. Оно улучшает порядок, но добавляет задержку и стоимость.
- Переписывание вопроса помогает в диалоге, где вопрос «а для командировок?» без предыдущих реплик бессмыслен. Это ещё одна модель в цепочке, и её тоже проверяют.
Фильтр по правам стоит внутри запроса к индексу: хранилище отдаёт только фрагменты, доступные пользователю. Если фильтровать после выдачи, часть лучших кандидатов отсекается и качество непредсказуемо падает, а при ошибке в фильтре чужой текст попадает в ответ. Чем отличаются хранилища и как их выбирать, написано в статье «Как выбрать векторную базу для RAG».
Сборка запроса и ответ
На этом этапе всё детерминировано, кроме самого вызова модели, и почти все решения лежат в обычном коде.
- Инструкция. Модели прямо сказано отвечать только по найденным фрагментам и отказаться, если ответа в них нет.
- Фрагменты. Берём не все найденные, а прошедшие порог релевантности, с названием документа и номером редакции. Порядок и количество зависят от длины контекста.
- Формат. Ответ с указанием, из каких фрагментов он составлен; структура проверяется кодом.
- Проверка. Ссылки на источники должны существовать среди переданных фрагментов. Ответ без ссылки либо отклоняется, либо показывается с пометкой.
- Отказ. Фраза «в базе нет ответа» это ожидаемый результат, а не сбой. Её доля на вопросах вне базы измеряется отдельно.
Тексты внутри документов считаются недоверенным вводом: в письме или файле может стоять фраза «игнорируй инструкции». Поэтому у модели в RAG нет прав на действия, а любые действия по её ответу проходят через код с проверкой, как описано в статье «Что такое prompt injection».
Обновление индекса и права доступа
Пайплайн не заканчивается первой загрузкой. Документы меняются, права меняются, сотрудники увольняются, и индекс должен за этим успевать.
- Обновление по изменению или по расписанию. Для регламентов хватает ночной проверки, для оперативной информации нужна загрузка по событию. Решение зависит от цены устаревшего ответа.
- Удаление. Когда документ отозван, его фрагменты должны исчезнуть из индекса, а не остаться «на всякий случай». Это одно из главных преимуществ RAG перед обучением модели на документах.
- Права. Группы документа хранятся в метаданных, а группы пользователя берутся из вашего каталога при каждом запросе. Отозвали доступ, и ответ меняется сразу.
- Журнал. Для каждого ответа записано, какие фрагменты и какой редакции использовались. Текст вопроса и ответа по умолчанию не хранится.
Оценка: поиск и ответ измеряются раздельно
Общая оценка «ответ хороший» скрывает, где ломается цепочка. Если нужный фрагмент не найден, настраивать модель бессмысленно. Поэтому оценка идёт по этапам, на эталоне из реальных вопросов с известными ответами и указанием документа, откуда ответ берётся.
| Что измеряем | Как считаем | Что делать, если плохо |
|---|---|---|
| Поиск | Найден ли нужный фрагмент среди первых результатов (по доле вопросов эталона) | Менять разбиение, добавлять поиск по словам, переранжирование |
| Верность ответа | Соответствует ли ответ найденным фрагментам, нет ли выдуманных деталей | Править инструкцию, формат, порог отбора фрагментов, проверять модель |
| Отказ | Доля вопросов вне базы, на которые система отказалась отвечать | Усиливать инструкцию и порог релевантности |
| Ссылки | Все ли указанные источники существуют и подтверждают сказанное | Проверка в коде, а не просьба к модели |
| Права | Попытки получить чужой документ из-под каждой роли | Фильтр в запросе к индексу, тест при каждой правке |
Часть эталона откладывается и не используется при настройке, иначе вы подгоните систему под эти вопросы. Прогон повторяется при смене модели, разбиения, индекса и порога. Размечать эталон должны те, кто знает правильные ответы, а не разработчики. Пример построения эталонной выборки есть в статье «Архитектура LLM-приложения».
Симптом, этап и что проверить
Когда пользователь жалуется, что система ответила неверно, надо понять, какой этап подвёл. Таблица помогает начать с нужного места.
| Симптом | Вероятный этап | Что проверить |
|---|---|---|
| Ответ уверенный, но неверный | Поиск или загрузка | Был ли нужный фрагмент среди переданных; не устарела ли редакция |
| «В базе нет ответа», хотя документ есть | Разбиение или поиск | Попал ли документ в индекс; не разрезан ли ответ на два фрагмента |
| Не находятся артикулы и номера | Поиск | Добавить поиск по словам и сравнить на эталоне |
| Ответ противоречит самому себе | Загрузка | Нет ли в индексе двух редакций документа |
| Ссылка есть, но в источнике этого нет | Сборка и проверка | Проверка соответствия ссылки и текста в коде |
| Пользователь видит чужой документ | Права | Фильтр внутри запроса к индексу, метаданные групп, кэш |
| Ответы стали хуже после правки | Оценка | Прогон эталона до и после, версия шаблона и индекса в журнале |
Что делаем мы и с чего начать
Начинаем с обследования: 200 000 ₽ и 2–3 недели за один-два источника, 350 000 ₽ и 3–4 недели, если источников и систем несколько. Мы разбираем документы и права, собираем эталон из ваших реальных вопросов, проверяем несколько вариантов разбиения и поиска и показываем результат на выборке. Стоимость вычитается из первого этапа, если вы продолжаете с нами, а документ обследования можно использовать и без нас.
Дальше лёгкий пилот (150 000–400 000 ₽, 2–8 недель) и вывод в эксплуатацию, на любой ступени можно остановиться. Мы работаем в вашем контуре и на модели, которая стоит на ваших серверах, а к внешним моделям возвращаемся, когда документы допускают. Исходники лежат в вашем репозитории с первого коммита. Цены и сроки собраны на странице «Тарифы», описание услуги — на странице «RAG-системы на заказ».
Частые вопросы
Из каких этапов состоит RAG?
Из пяти: загрузка документов, разбиение на фрагменты, поиск, сборка запроса и ответ, оценка качества. Обновление индекса и права доступа сопровождают их все. Модель участвует только в одном этапе, остальные выполняет обычный код.
Какой длины должны быть фрагменты?
Единого значения нет. Слишком короткие теряют контекст, слишком длинные размывают смысл. Длину и способ разбиения подбирают на эталонной выборке: сравнивают несколько вариантов на ваших вопросах и берут тот, где чаще находится нужный фрагмент.
Нужен ли поиск по словам, если есть поиск по смыслу?
Нужен, если пользователи спрашивают номерами приказов, артикулами, фамилиями и сокращениями. Для таких запросов поиск по смыслу слабее. Если вопросы свободные и кодов нет, может хватить одного поиска по смыслу. Решает сравнение на эталоне.
Почему RAG отвечает неверно, если документ в базе есть?
Причины разные: документ плохо извлечён, разрезан так, что ответ разошёлся по двум фрагментам, не найден поиском, вытеснен другим фрагментом или проигнорирован моделью. Поэтому поиск и ответ оценивают раздельно: сначала смотрят, найден ли фрагмент.
Как RAG защищает документы с ограниченным доступом?
Права хранятся в метаданных каждого фрагмента, а фильтр применяется внутри запроса к индексу по группам текущего пользователя. Фильтровать выдачу после поиска нельзя: качество падает, а ошибка открывает чужой текст. Прогон из-под каждой роли входит в приёмку.
Как обновлять индекс, когда документы меняются?
По событию или по расписанию, в зависимости от цены устаревшего ответа. Отозванные документы удаляются из индекса, редакции помечаются. В журнале видно, какие фрагменты использовал каждый ответ.
Можно ли построить RAG на модели в закрытом контуре?
Да. Для данных ограниченного доступа внешние облачные модели чаще всего не подходят по требованиям заказчика, поэтому индекс и модель размещают на ваших серверах. Качество такой модели на русском языке и ваших документах проверяется на эталоне, а не обещается заранее.
Сколько стоит построить RAG-пайплайн?
Обследование стоит 200 000 ₽ за один-два источника и 350 000 ₽ за несколько, лёгкий пилот — 150 000–400 000 ₽ за 2–8 недель, тяжёлый — 400 000–1 200 000 ₽ за 2–3 месяца. Вывод в эксплуатацию начинается от 1 500 000 ₽ этапами по 300 000–700 000 ₽. Точная цена называется по итогам обследования.