RAG-пайплайн устроен так: документы загружаются и очищаются, режутся на фрагменты, индексируются; на вопрос система находит подходящие фрагменты, передаёт их модели вместе с вопросом и проверяет ответ; качество каждого этапа отдельно измеряется на эталонной выборке. Модель в этой цепочке стоит почти в конце, а большинство ошибок рождается раньше неё.

Поэтому разговор «какую модель взять» откладывается. Сначала нужно понять, что попадает в индекс, как это режется и как это ищется. Принцип RAG описан в статье «Что такое RAG простыми словами», выбор хранилища для индекса — в статье о векторных базах. Здесь только сам конвейер и его слабые места.

Коротко. Ошибки RAG чаще всего находятся в данных и поиске, а не в модели. Измеряйте поиск и ответ раздельно: сначала найден ли нужный фрагмент, потом верен ли ответ по нему. Права проверяйте внутри запроса к индексу, а не после выдачи.

Пять этапов и что на каждом решается

Схема одна, решений на каждом этапе несколько. Таблица показывает, что надо решить заранее и как пайплайн ломается, если оставить решение по умолчанию.

ЭтапЧто делаетЧто решают заранееКак ломается
ЗагрузкаБерёт документы из источников, извлекает текст, убирает мусор и сохраняет метаданныеКакие источники входят, кто владеет каждым, как часто обновлять, что делать со сканами и таблицамиКолонтитулы и оглавления забивают индекс, таблица превратилась в кашу из цифр, в базе две редакции одного приказа
РазбиениеРежет текст на фрагменты, которые будут находиться по отдельностиРежем по структуре документа или по длине, есть ли перекрытие, что хранится вместе с фрагментомФрагмент оторван от заголовка и непонятно, о чём он; ответ разнесён на два фрагмента, и находится только один
ПоискПо вопросу находит нужные фрагментыТолько по смыслу или ещё по точным словам, сколько фрагментов брать, нужно ли переранжированиеАртикулы, номера приказов и сокращения не находятся; находятся похожие, но не те фрагменты
Сборка и ответСоставляет запрос к модели из инструкции, фрагментов и вопроса, получает ответ и проверяет егоФормат ответа, ссылки на источник, поведение при отсутствии ответа в базеМодель отвечает по общим знаниям, когда фрагмента нет, или ссылается на то, чего в источнике не было
ОценкаПрогоняет эталон и показывает, какой этап подвёлКто размечает эталон, какие показатели считаются отдельно, что блокирует выкаткуКачество проверили на пяти вопросах, а после правки не проверили вообще

Загрузка: качество индекса определяется здесь

Индекс не может быть лучше исходных документов. Три вещи делают загрузку серьёзной работой, а не копированием файлов.

  • Извлечение текста. Текстовые файлы и документы с разметкой читаются просто. Сканы требуют распознавания и проверки результата, а таблицы и колонки в двух столбцах при извлечении легко теряют порядок. Выборочная проверка двух-трёх десятков документов глазами экономит недели отладки поиска. Как оценивать распознавание, разобрано в статье «Как измерить точность распознавания документов».
  • Метаданные. Вместе с текстом хранится название документа, раздел, редакция, дата, владелец и группы, которым документ доступен. Без них нельзя ни показать источник, ни ограничить доступ, ни убрать устаревшее.
  • Дубли и редакции. Если в индексе лежат старая и новая редакция регламента, система уверенно ответит по старой. Редакции помечаются, а действующая выбирается по правилу, которое записано, а не угадывается.

Мусор вроде колонтитулов, оглавлений и повторяющихся подписей в письмах убирается на загрузке, иначе он оказывается среди найденных фрагментов и вытесняет полезные. Результат загрузки стоит просматривать глазами: это скучно, но дешевле любой настройки поиска.

Разбиение на фрагменты: где резать

Фрагмент — единица, которую поиск находит и передаёт модели. Слишком короткий теряет контекст, слишком длинный размывает смысл и съедает место в запросе. Универсальной длины нет, и подбирают её по эталону, а не по рекомендациям из чужих статей.

СпособКак работаетКогда подходитЧто учесть
По длинеРежет текст на равные куски, при необходимости с перекрытиемОднородный сплошной текст без структурыГраница может пройти посреди мысли, поэтому нужно перекрытие
По структуреРежет по заголовкам, пунктам и абзацам документаРегламенты, инструкции, договоры, где пункт самостоятеленНужно разобрать структуру; пункты разной длины
С заголовкомК каждому фрагменту добавляется название документа и разделаПочти всегда, если фрагмент без заголовка непонятенЗаголовок съедает часть места, но спасает от «оторванных» кусков
ИерархическиХранит мелкие фрагменты для поиска и крупный родительский для ответаДлинные документы, где ответ требует соседних абзацевСложнее устроены индекс и обновление

Особый случай — таблицы. Строка таблицы без заголовков столбцов бессмысленна, поэтому к ней добавляют названия столбцов, а иногда строку превращают в короткое предложение. Для нормативных документов с ссылками вида «см. пункт такой-то» стоит хранить связи между фрагментами, иначе ответ получится неполным.

Поиск: по смыслу, по словам и вместе

Поиск по смыслу сопоставляет вопрос и фрагменты как векторы, поэтому находит «сколько дней отпуск» в тексте про «ежегодный оплачиваемый отпуск». Но он слабее там, где нужен точный токен: номер приказа, артикул, фамилия, сокращение. Поиск по словам, наоборот, не видит перефразирования. Поэтому в рабочих системах часто используют оба и объединяют результаты.

  • Только по смыслу проще и хватает для текстов без кодов и артикулов.
  • Гибридный поиск нужен, если пользователи спрашивают номерами, названиями и сокращениями. Это решается проверкой на эталоне, а не на вкус.
  • Переранжирование берёт больше кандидатов и пересортировывает их отдельной моделью, ставя лучшие первыми. Оно улучшает порядок, но добавляет задержку и стоимость.
  • Переписывание вопроса помогает в диалоге, где вопрос «а для командировок?» без предыдущих реплик бессмыслен. Это ещё одна модель в цепочке, и её тоже проверяют.

Фильтр по правам стоит внутри запроса к индексу: хранилище отдаёт только фрагменты, доступные пользователю. Если фильтровать после выдачи, часть лучших кандидатов отсекается и качество непредсказуемо падает, а при ошибке в фильтре чужой текст попадает в ответ. Чем отличаются хранилища и как их выбирать, написано в статье «Как выбрать векторную базу для RAG».

Сборка запроса и ответ

На этом этапе всё детерминировано, кроме самого вызова модели, и почти все решения лежат в обычном коде.

  1. Инструкция. Модели прямо сказано отвечать только по найденным фрагментам и отказаться, если ответа в них нет.
  2. Фрагменты. Берём не все найденные, а прошедшие порог релевантности, с названием документа и номером редакции. Порядок и количество зависят от длины контекста.
  3. Формат. Ответ с указанием, из каких фрагментов он составлен; структура проверяется кодом.
  4. Проверка. Ссылки на источники должны существовать среди переданных фрагментов. Ответ без ссылки либо отклоняется, либо показывается с пометкой.
  5. Отказ. Фраза «в базе нет ответа» это ожидаемый результат, а не сбой. Её доля на вопросах вне базы измеряется отдельно.

Тексты внутри документов считаются недоверенным вводом: в письме или файле может стоять фраза «игнорируй инструкции». Поэтому у модели в 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 ₽. Точная цена называется по итогам обследования.