Если вы уже разворачивали простую RAG-систему, схема вам знакома: разбить документы на фрагменты, построить эмбеддинги, сохранить их в векторной базе данных, найти по запросу первые k результатов и добавить их в промпт. Для демонстрации этого нередко достаточно. При эксплуатации качество часто разочаровывает.
Между демонстрационной и промышленной RAG-системой большая разница. Реальные документы содержат шум. Реальные запросы неоднозначны. Качество заметно различается в зависимости от типа запроса. Стоимость и задержка ограничивают архитектуру. Обновления и версионирование тоже имеют значение.
Это контрольный список для проектирования RAG-системы промышленного уровня: шесть логических стадий и необходимые измерения на каждой из них. Это не воспроизводимый сравнительный тест AI Expert. Начните с оригинальной статьи о RAG, изучите выбор метрик на разнородном наборе задач, например BEIR, а до выбора компонентов создайте закрытый оценочный набор на основе собственного корпуса.
Конвейер
У RAG-системы промышленного уровня шесть логических стадий:
[Документы]
↓
1. Приём данных (разбор, очистка, извлечение метаданных)
↓
2. Разбиение на фрагменты (единицы поиска)
↓
3. Построение эмбеддингов (векторы и хранение)
↓
[Запрос]
↓
4. Поиск (семантический, лексический и фильтры)
↓
5. Переранжирование (первые k → первые N)
↓
6. Формирование ответа (LLM и контекст)
↓
[Ответ]
У каждой стадии свои риски для качества. Улучшение одного этапа может улучшить систему в целом, но эффект необходимо измерять сквозной оценкой.
Пройдём по каждой.
Стадия 1: приём данных (ingestion)
Мусор на входе — мусор на выходе. Качество приёма данных определяет потолок всего, что идёт ниже.
Типы источников, с которыми вы столкнётесь:
- PDF (в основном худший вариант).
- Word-документы.
- HTML-страницы.
- Markdown.
- Таблицы.
- Слайды (PowerPoint, Google Slides).
- Письма.
- Код.
- Структурированные данные (CSV, JSON).
У каждого свои сложности парсинга.
Парсинг PDF. PDF — формат презентации, а не данных. Они печально известны сложностью парсинга. Стратегии:
- Текстовые PDF:
pdfplumber,pymupdf,unstructured. Работают для чистого текста. - Сканированные PDF: OCR с Tesseract, Google Cloud Vision или AWS Textract.
- С учётом макета:
LayoutLM, Mathpix или парсинг через LLM (GPT-4 vision) для сложных макетов (таблицы, несколько колонок).
Современный подход для сложных PDF: использовать LLM с поддержкой зрения для OCR и структурирования контента. Стоит дороже, но качество заметно выше, чем у традиционного OCR.
Обработка таблиц. Таблицы в неструктурированном тексте — боль. Варианты:
- Конвертировать в markdown-таблицы (сохраняет структуру).
- Линеаризовать в прозу («Row 1: customer A had 50 orders…»).
- Считать отдельными извлекаемыми единицами со структурированной схемой.
Правильный выбор зависит от того, какие запросы вы будете получать.
Извлечение метаданных. У каждого документа есть метаданные, которые важны:
- Заголовок, автор, дата, версия.
- Тема, категория, теги.
- URL источника или его местоположение.
- Права / видимость.
Захватывайте это на этапе приёма данных. Это станет параметрами фильтрации при извлечении.
Очистка. Срезайте шаблонный мусор:
- Колонтитулы, повторяющиеся на каждой странице.
- Навигация, реклама, баннеры о cookie.
- Страницы оглавления.
- Пустые или дублированные параграфы.
По чистому корпусу поиск работает лучше. Шаблонный мусор приводит к ложным срабатываниям.
Проверки качества.
- Извлёк ли парсер реально текст? (Некоторые PDF возвращают пустые строки.)
- Есть ли проблемы с кодировкой?
- Сохранены ли таблицы?
- Подписаны ли картинки или пропущены?
Встройте базовые проверки в конвейер приёма данных. Ловите битые результаты парсинга до того, как они загрязнят индекс.
Стадия 2: разбиение на фрагменты (chunking)
У вас есть чистые документы. Теперь делите их на единицы извлечения (чанки).
Фундаментальный компромисс:
- Мелкие чанки: точное извлечение (сфокусированное на вопросе), но без контекста.
- Крупные чанки: больше контекста, но менее точно (нужная часть закопана).
Обе крайности могут снижать качество для части запросов. Выбирайте границы фрагментов и объём родительского контекста по результатам размеченной оценки поиска и ответов — отдельно для каждого типа документов и запросов.
Распространённые стратегии:
Фиксированный размер с перекрытием. Резать на чанки по N токенов (300–800 токенов) с перекрытием 50–100 токенов. Просто, работает как базовый вариант.
С учётом границ предложений. Резать по границам предложений (используя nltk, spaCy или аналог). Избегает разрывов в середине предложения.
По параграфам. Каждый параграф — чанк. Работает для документов с хорошо структурированными параграфами.
Иерархический (мелкий + крупный). Два индекса:
- Мелкие чанки (300 токенов) для точного извлечения.
- Крупные чанки (1500 токенов) или целые секции для контекста. При извлечении достаём мелкие; в LLM подаём родительский крупный.
Учёт структуры документа. Используйте структуру документа (заголовки, секции) для формирования чанков. Каждая секция — чанк, иерархия сохраняется.
Семантическая нарезка. Используйте эмбеддинги, чтобы найти естественные точки разрыва (где меняется тема). Дороже, но даёт лучшие чанки для некоторого контента.
Нарезка через суммаризацию LLM. Длинные документы суммируются в иерархические чанки на нескольких уровнях (резюме параграфа, резюме секции, резюме документа). LLM генерирует это один раз на этапе приёма данных.
Правильная стратегия зависит от контента. Статьи и вики: параграфы или иерархия. Код: уровень функций. Разговоры: по репликам. Технические доки: учёт структуры.
Метаданные чанков. Каждый чанк должен нести:
- ID и URL исходного документа.
- Путь секции (chapter > section > subsection).
- Номер страницы (для цитирования).
- Заголовки выше этого чанка (для контекста).
- Метаданные уровня документа (дата, автор, тип).
Эти метаданные позволяют фильтровать и цитировать в финальном ответе.
Стадия 3: эмбеддинг
Превращаем чанки в векторы.
Выбор модели эмбеддинга.
Среди возможных вариантов — модели эмбеддингов OpenAI, Voyage и Cohere, а также модели BGE или Nomic с открытыми весами. Названия, размерность, лицензии, цены и поддерживаемые типы входных данных меняются. Составляйте список кандидатов по актуальным рекомендациям OpenAI, документации Voyage, документации Cohere Embed и карточкам соответствующих моделей с открытыми весами.
Выбор модели: тестируйте на своём домене. Не считайте, что лидер таблицы рейтингов — лучший для ваших данных.
Размерность эмбеддинга. Размерность влияет на объём индекса и стоимость хранения, но более длинный вектор не гарантирует более качественного поиска. Некоторые API позволяют сокращать векторы, другие используют фиксированную размерность. Сравнивайте поддерживаемые варианты на одних и тех же размеченных запросах и учитывайте размер индекса и задержку.
Версионирование. Модели эмбеддинга обновляются; вы можете захотеть переключиться. Планируйте это:
- Отслеживайте, какая версия модели произвела каждый вектор.
- Заново эмбеддите всё при миграции (или используйте переход через двойной индекс).
- Не смешивайте векторы разных моделей в одном поиске.
Стоимость. Рассчитывайте первичную индексацию и последующие обновления по измеренному числу токенов, актуальной цене провайдера, доле повторных попыток, затратам на OCR и разбор документов, хранению векторов, репликам и частоте повторной индексации. Сохраняйте рядом с расчётом ссылку на источник цены и дату: конкретная цифра в статье быстро устареет.
Хранение. Векторы большие. 1M чанков × 1536 измерений × 4 байта = 6GB. Планируйте хранилище соответственно.
Стадия 4: поиск (retrieval)
Приходит запрос. Нужно найти релевантные фрагменты.
Векторный поиск (плотное извлечение). Эмбеддим запрос, ищем ближайших соседей. Ловит семантическое сходство. Стандарт.
Поиск по ключевым словам (BM25 / лексический). Традиционный текстовый поиск. Ловит точные совпадения, редкие термины, конкретные имена. Часто дополняет векторный.
Гибридный поиск. Запускаем оба, сливаем результаты. Reciprocal Rank Fusion (RRF) — стандартный подход к слиянию.
def reciprocal_rank_fusion(rankings, k=60):
scores = {}
for ranking in rankings:
for rank, doc_id in enumerate(ranking):
scores[doc_id] = scores.get(doc_id, 0) + 1 / (k + rank + 1)
return sorted(scores.items(), key=lambda x: -x[1])
На практике гибрид обыгрывает чисто векторный или чисто по ключевым словам на большинстве бенчмарков. Используйте по умолчанию.
Фильтрация по метаданным. Фильтруйте извлечение по метаданным до скоринга. «Только документы с 2025+». «Только документы из доступного пользователю набора». «Только документы типа policy».
Критично для мультитенантных систем: каждый запрос ограничен документами, доступными тенанту.
Понимание запроса. Предобработка запроса:
- Коррекция опечаток. «engineerin» → «engineering».
- Раскрытие аббревиатур. «MFA» → «Multi-Factor Authentication MFA».
- Синонимы / расширение запроса. Генерируем альтернативные формулировки; ищем по каждой.
- Декомпозиция. Многосоставные вопросы разбиваются на подзапросы, каждый извлекается отдельно.
Эти шаги предобработки значительно улучшают извлечение на реальных запросах.
HyDE (Hypothetical Document Embeddings). Сгенерировать с помощью LLM фиктивный «идеальный ответ». Получить его эмбеддинг. Использовать результат как запрос для извлечения. Часто работает лучше, чем эмбеддинг вопроса напрямую, потому что фиктивный ответ ближе к реальным документам-ответам.
def hyde_retrieve(query, k=20):
hypothetical = llm("Write a passage answering: " + query)
embedding = embed(hypothetical)
return vector_search(embedding, k=k)
Компромисс: дополнительный вызов LLM на запрос (задержка, стоимость). Стоит того для сложных запросов; перебор для простых.
Мультивекторное извлечение. Вместо одного вектора на чанк — несколько (например, один для чанка, один для резюме, один для гипотетических вопросов). Добавляет хранение и сложность, но улучшает извлечение для некоторого контента.
Стадия 5: переранжирование (reranking)
После первичного поиска (первые k результатов, например k=20–50) переранжировщик может точнее оценить найденные кандидаты.
Зачем переранжировать?
Начальное извлечение быстрое, но неточное. Векторная близость — грубая аппроксимация релевантности. Переранжировщик (обычно модель-кросс-энкодер) читает запрос и каждого кандидата вместе, выдавая более точную оценку.
Варианты переранжировщиков:
- Cohere Rerank. Индустриальный стандарт. Хорошее качество, хостируемое.
- Voyage Rerank. Сильный конкурент.
- BGE Rerank-large. Открытый код, бесплатный при размещении у себя.
- Кросс-энкодеры MiniLM. Меньше, быстрее, ниже качество.
- Переранжирование на базе LLM. Используем маленькую LLM для оценки пар «запрос-кандидат». Самое высокое качество, самая высокая стоимость.
Эффект.
Переранжирование может улучшить порядок кандидатов, но эффект зависит от первого этапа поиска, числа кандидатов, корпуса, распределения запросов и выбранной метрики. Оставляйте его в системе только тогда, когда размеченная оценка показывает, что прирост качества оправдывает задержку и стоимость.
Типичная конфигурация:
- Достать 30–50 кандидатов гибридным поиском.
- Оставить после переранжирования первые 5–10 результатов.
- Передать лучшие результаты в LLM.
Адаптивное переранжирование.
Для запросов, где топ-1 результат имеет высокое векторное сходство (явное совпадение), переранжирование пропускайте. Для запросов с близкими оценками (несколько кандидатов вровень) — переранжируйте агрессивно.
Стадия 6: генерация
LLM производит ответ, используя извлечённый контекст.
Структура промпта:
Вы — ассистент, отвечающий на вопросы на основе предоставленного контекста.
Контекст:
[Document 1 - title, URL]
[chunk 1 content]
[Document 2 - title, URL]
[chunk 2 content]
...
Вопрос пользователя: {query}
Инструкции:
- Отвечайте только на основе предоставленного контекста.
- Если в контексте нет ответа, так и скажите. Не выдумывайте информацию.
- Цитируйте источники в формате [Источник N].
- Будьте лаконичны, но полны.
Важные элементы промпта:
- Маркировка источников. Каждый чанк контекста маркируется источником для цитирования.
- Инструкции по опоре на контекст. Явно говорим модели использовать только контекст.
- Требование цитирования. Принуждаем к цитированию. Ловит галлюцинации.
- Запасная инструкция. «Если в контексте нет ответа — так и скажите.» Предотвращает домысливание.
Обработка цитирования.
Распространённый подход — включать в ответ ссылки на источники, которые интерфейс отображает как кликабельные.
«Политика удалённой работы компании допускает до 4 дней в неделю из дома [1].
[1]: https://wiki.company.com/policies/remote-work»
Так пользователь видит, на какой материал опирается ответ. Но ссылка сама по себе не доказывает, что источник актуален, доступен этому пользователю или истолкован верно. Приложение должно открывать реальный адрес источника, к которому у текущего пользователя есть доступ. Домен .invalid в примере намеренно не разрешается и используется только как тестовый адрес.
Управление размером контекста.
Длинные контексты деградируют. Большинство моделей лучше работают со сфокусированными контекстами (5–10 высокорелевантных чанков), а не со свалкой всего. Качество падает по мере разбухания контекста.
Если приходится включать много контекста, суммируйте менее релевантные элементы, а не включайте их целиком.
Оценка на каждой стадии
Нельзя оптимизировать то, что не измеряешь. Каждой стадии нужны свои оценки.
Оценка приёма данных. Корректно ли распарсились документы? Возьмите выборку документов; проверьте, что ключевой контент сохранён.
Оценка нарезки. Правильного ли размера чанки? Сохраняют ли контекст? Рвутся ли по разумным границам?
Оценка эмбеддинга. Эмбеддятся ли похожие концепции близко друг к другу? На тестовом наборе заведомо похожих и заведомо разных пар — соответствует ли косинусная близость ожиданиям?
Оценка извлечения. Для данного запроса — есть ли релевантные чанки в top-K? Типичная метрика: recall@K (какой % запросов имеет хотя бы один релевантный чанк в top K).
Оценка переранжирования. Среди извлечённых кандидатов — ставит ли переранжировщик самый релевантный первым? Метрика: NDCG (normalized discounted cumulative gain) или MRR (mean reciprocal rank).
Оценка генерации. При данном контексте и запросе — выдаёт ли LLM корректный ответ? Метрики: верность контексту (faithfulness — использует ли ответ контекст?), корректность (correctness — верен ли ответ?), полезность (helpfulness — отвечает ли он на намерение пользователя?).
Сквозная оценка (end-to-end). При реальном запросе — выдаёт ли система правильный ответ? Самое важное; зависит от всех стадий.
Вам нужны тестовые наборы для каждой стадии. Типичный стартовый набор:
- Собрать 50–100 запросов с известно-хорошими ответами.
- Для каждого запроса определить, какой документ(ы) содержит ответ.
- Использовать это для оценки извлечения (достали ли мы нужные документы?) и генерации (верен ли ответ?).
Инструменты: Ragas, TruLens, свои наборы. Конкретный инструмент менее важен, чем сам факт запуска оценок.
Операционные вопросы
Несколько реалий эксплуатации:
Конвейеры индексации. Появляются новые документы: для них нужно построить эмбеддинги, обновить индексы, обработать удаления и изменения. Этот конвейер работает постоянно, а не только при первоначальной настройке. Проектируйте его как систему, а не как одноразовый скрипт.
Задержка.
Отдельно измеряйте эмбеддинг запроса, поиск кандидатов, переранжирование, время генерации до первого токена и до завершения, а также сетевые задержки. Фиксируйте распределения для холодного и прогретого запуска при ожидаемой параллельной нагрузке: название компонента само по себе ничего не говорит о задержке.
Оптимизация:
- Кэшировать эмбеддинги частых запросов.
- Кэшировать переранжирование для повторяющихся пар «запрос-кандидат».
- Передавать генерацию потоком.
- Распараллеливать, где возможно.
Если у продукта есть целевое время ответа, распределите измеренный бюджет между обработкой запроса, поиском, переранжированием, генерацией и сетью. Поведение «быстрее двух секунд» нельзя вывести из списка компонентов — его нужно проверить на холодном и прогретом пути при ожидаемой нагрузке.
Стоимость.
Для каждого оценочного запроса учитывайте эмбеддинг, ёмкость поиска и индекса, переранжирование, входные и выходные токены, попадание в кэш и повторные попытки. Затем переносите на прогнозируемый трафик всё наблюдаемое распределение, а не одно среднее. Миллион запросов может стоить совершенно по-разному в зависимости от размера документов, числа кандидатов, модели, длины ответа, кэширования и условий размещения инфраструктуры.
Оптимизация стоимости:
- Кэшировать.
- Использовать более дешёвые модели там, где качество позволяет.
- Сжимать контекст (резюме вместо полных чанков).
Обновления. Документы меняются. Возможные подходы:
- Версионирование: у каждого документа есть версия; старые версии хранятся или удаляются по политике.
- Инкрементально: новые версии заново нарезаются и заново эмбеддятся; старые чанки удаляются.
- С учётом различий: переобрабатываются только изменённые секции.
Для сценариев с большим объёмом обновлений это существенно.
Права. RAG над приватными данными: у документов есть средства контроля доступа; извлечение должно их уважать.
- Фильтрация по метаданным (у каждого чанка есть метаданные о том, кому он доступен).
- Индексы, изолированные по тенантам, для жёсткой изоляции.
- Журналирование доступа: кто к чему обращался.
Не доверяйте LLM обеспечивать соблюдение прав. Обеспечивайте его на этапе извлечения.
Типичные режимы провала
Ниже перечислены распространённые причины отказов RAG-систем:
Провал 1: плохая нарезка. Чанки рвутся посреди мысли, таблица разбита между чанками, заголовки отделены от своего содержания. Исправление: лучшая стратегия нарезки.
Провал 2: извлечение промахивается мимо ответа. Релевантный чанк существует, но не находится. Часто — несоответствие запроса и документа. Исправление: HyDE, расширение запроса, лучшие эмбеддинги.
Провал 3: правильные чанки, неверный порядок. Релевантный чанк извлечён, но ранжирован низко. LLM использует более высокоранжированные нерелевантные чанки. Исправление: переранжирование.
Провал 4: правильные чанки, галлюцинация. Чанки правильные, но LLM придумывает дополнительную информацию. Исправление: более строгий промпт с опорой на источники, требования цитирования, более низкая температура.
Провал 5: правильные чанки, неверный ответ. Чанки содержат ответ, но LLM извлекает неверную часть. Исправление: лучший промпт генерации, возможно — модель побольше.
Провал 6: устаревшие данные. Индекс не обновлялся. Исправление: непрерывный конвейер приёма данных.
Провал 7: утечки прав. Пользователи видят чанки, которые им не положены. Исправление: применять фильтрацию на этапе извлечения; аудит.
Провал 8: неконтролируемый рост затрат. Задержка и стоимость выросли с ростом корпуса и трафика. Исправление: кэширование, маршрутизация моделей, возможно — архитектурные изменения.
Модель проекта: RAG для корпоративной базы знаний
Это шаблон конфигурации, а не результат внедрения AI Expert и не описание «типичной компании». Все объёмы, продукты, интервалы и глубину поиска ниже следует считать иллюстративными гипотезами и заменить измеренными исходными данными.
Корпус: ~50 000 документов (страницы вики, политики, руководства по эксплуатации, заметки встреч, треды в Slack).
Конвейер:
-
Приём данных:
- Notion: экспорт через API.
- Slack: экспорт архива, отфильтрованный до релевантных каналов.
- Google Drive: API-экспорт согласованных папок.
- Очистка: убрать сообщения только из эмодзи, шаблонные подписи.
-
Нарезка:
- Иерархическая: мелкие чанки уровня параграфа, родительские чанки уровня секции.
- ~250K общих чанков на мелком уровне.
- Метаданные: источник, путь секции, last_updated, accessible_to.
-
Эмбеддинг:
- Эмбеддинги Voyage-3, 1024 измерения.
- Хранение в Pinecone (управляемый сервис).
- Еженедельный повторный эмбеддинг для изменённых документов.
-
Извлечение:
- Гибридное: векторный поиск + BM25 (через гибрид Pinecone).
- HyDE для запроса.
- Фильтр по метаданным для доступности.
- Извлекается top 30.
-
Переранжирование:
- Cohere Rerank.
- Top 30 → top 8.
-
Генерация:
- Claude Sonnet 5.
- Top 8 родительских чанков (не мелких) подаются как контекст.
- Цитирование требуется в ответе.
План оценки — установите пороги из допустимого для бизнеса уровня ошибок:
- 100 вручную составленных пар «запрос/ответ».
- Полнота поиска при выбранном числе кандидатов с разбивкой по типам запросов; требуемое значение задайте до настройки.
- Корректность финального ответа и полнота поиска: измеряйте обе метрики и разбирайте расхождения, не предполагая заранее, какая будет ниже.
- Соответствие ответа источникам отслеживайте отдельно от корректности.
Иллюстративный режим эксплуатации — реальную частоту определяют скорость изменения источников и риск:
- Ежедневный инкрементальный приём данных.
- Еженедельный полный повторный эмбеддинг для изменённых документов.
- Наблюдаемость по каждому запросу.
- Ежемесячный повторный прогон оценок; мониторинг трендов.
- Ежеквартальный обзор журнала запросов для поиска новых типов отказов.
Модель затрат — заполните её актуальными счетами или коммерческими предложениями:
- Приём данных: изменившиеся токены × цена эмбеддинга, плюс вычисления для разбора и OCR.
- Хранение и поиск: объём индекса, реплики, резервные копии и зарезервированная либо фактическая нагрузка.
- Каждый запрос: эмбеддинг + поиск + переранжирование + входные и выходные токены модели + сетевые и инструментальные расходы.
- Эксплуатация: инженерная работа, разметка оценок, реагирование на инциденты и поддержка источников данных.
Одобряйте расходы только тогда, когда измеренный результат задачи и сэкономленная работа оправдывают полную стоимость. Одних метрик поиска недостаточно, чтобы доказать окупаемость.
Поэтапное создание RAG с проверкой на каждой стадии
Если стартуете с нуля:
Этап 1: ограниченный прототип.
- Определить корпус и основной сценарий использования.
- Базовый приём данных (один источник).
- Базовая нарезка (фиксированный размер с перекрытием).
- Стандартная модель эмбеддинга.
- Только векторный поиск (без переранжирования).
- Простой промпт генерации.
Этап 2: подтверждение качества.
- Собрать размеченный набор оценки, достаточный для важных типов запросов и рисков.
- Определить режимы провалов.
- Добавить переранжирование.
- Добавить гибридный поиск.
- Улучшить нарезку.
Этап 3: готовность к эксплуатации.
- Непрерывный конвейер приёма данных.
- Наблюдаемость.
- Автоматизация оценки.
- Права / аутентификация.
- Мониторинг стоимости.
Не обещайте календарный срок для этих этапов. Переходите дальше только после прохождения проверок текущего этапа: сначала точность источников и права доступа, затем качество поиска и ответов, после этого безопасность, актуальность, удаление данных, ёмкость, восстановление и закреплённая ответственность за эксплуатацию.
Качество живёт на каждой стадии
RAG-система промышленного уровня — это шестистадийный конвейер, где качество нужно контролировать на каждом этапе. Надёжные системы отличают следующие подходы:
- Приём данных: тщательный парсинг, сохранение структуры, захват метаданных.
- Нарезка: подходящая стратегия для типа контента, часто иерархическая.
- Эмбеддинг: качественная модель, версионирование, план миграции.
- Извлечение: сравнение лексического, векторного и гибридного поиска, фильтров и преобразования запроса на размеченном наборе.
- Переранжирование: добавлять только при измеримом приросте качества, который оправдывает задержку, стоимость и сложность.
- Генерация: опора на источники, цитирование, запасной ответ для неизвестного.
- Оценка: на каждой стадии, постоянно.
Необходимые меры зависят от задачи и риска, но пропуск любого этапа требует явного обоснования и компенсирующей проверки. Заявление о готовности к публикации или эксплуатации должно указывать оценённый корпус, типы запросов, метрики, найденные ошибки, модель прав доступа и дату — а не неподтверждённый процент успеха или обещанный срок.



