Production RAG: ingestion, embedding, retrieval, reranking, eval
Production-пайплайн RAG — это шесть стадий, у каждой свои паттерны, определяющие качество. Архитектура, выборы на каждой стадии и дисциплина итеративной оценки, которая отличает работающий RAG от разочаровывающего.
Если вы выкатили простую RAG-систему, вы знаете паттерн: нарезать документы на чанки, заэмбеддить, положить в векторную БД, на запрос достать top-k, засунуть в промпт. Для демо работает. В продакшене часто разочаровывает.
Разрыв между demo-RAG и production-RAG огромен. Реальные документы грязные. Реальные запросы неоднозначные. Качество дико варьируется по типам запросов. Стоимость и latency — реальные ограничения. Обновления и версионирование имеют значение.
Эта статья — о том, как выглядит production-grade RAG-пайплайн: шесть стадий, паттерны на каждой и дисциплина оценки, которая отличает работающие системы от разочаровывающих.
Пайплайн
У production RAG-системы шесть логических стадий:
[Documents]
↓
1. Ingestion (parsing, cleaning, metadata extraction)
↓
2. Chunking (splitting into retrieval units)
↓
3. Embedding (vectors + storage)
↓
[Query]
↓
4. Retrieval (semantic + lexical + filters)
↓
5. Reranking (top-k → top-N)
↓
6. Generation (LLM + context)
↓
[Response]У каждой стадии — свои проблемы качества. Улучшение любой одной улучшает всю систему.
Пройдём по каждой.
Стадия 1: ingestion
Мусор на входе — мусор на выходе. Качество вашего ingestion определяет потолок всего, что идёт ниже.
Типы источников, с которыми вы столкнётесь:
- PDF (в основном худший).
- Word-документы.
- HTML-страницы.
- Markdown.
- Таблицы.
- Слайды (PowerPoint, Google Slides).
- Письма.
- Код.
- Структурированные данные (CSV, JSON).
У каждого свои сложности парсинга.
Парсинг PDF. PDF — формат презентации, а не данных. Они печально известны сложностью парсинга. Стратегии:
- Текстовые PDF:
pdfplumber,pymupdf,unstructured. Работают для чистого текста. - Сканированные PDF: OCR с Tesseract, Google Cloud Vision или AWS Textract.
- Layout-aware:
LayoutLM, Mathpix или LLM-парсинг (GPT-4 vision) для сложных макетов (таблицы, несколько колонок).
Современный подход для сложных PDF: использовать LLM с поддержкой зрения для OCR и структурирования контента. Стоит дороже, но качество драматически лучше традиционного OCR.
Обработка таблиц. Таблицы в неструктурированном тексте — боль. Варианты:
- Конвертировать в markdown-таблицы (сохраняет структуру).
- Линеаризовать в прозу («Row 1: customer A had 50 orders…»).
- Считать отдельными извлекаемыми единицами со структурированной схемой.
Правильный выбор зависит от того, какие запросы вы будете получать.
Извлечение метаданных. У каждого документа есть метаданные, которые важны:
- Заголовок, автор, дата, версия.
- Тема, категория, теги.
- Source URL или местоположение.
- Права / видимость.
Захватывайте это на ingestion. Это станет параметрами фильтрации на retrieval.
Очистка. Срезайте boilerplate:
- Header-ы/footer-ы, повторяющиеся на каждой странице.
- Навигация, реклама, баннеры с cookie.
- Страницы оглавления.
- Пустые или дублированные параграфы.
Чистый корпус retrieve-ится лучше. Boilerplate приводит к ложным срабатываниям.
Проверки качества.
- Извлёк ли парсер реально текст? (Некоторые PDF возвращают пустые строки.)
- Есть ли проблемы с кодировкой?
- Сохранены ли таблицы?
- Подписаны ли картинки или пропущены?
Встройте sanity-чеки в ingestion-пайплайн. Ловите битые парсы до того, как они загрязнят индекс.
Стадия 2: chunking
У вас есть чистые документы. Теперь делите их на retrieval-единицы (чанки).
Фундаментальный компромисс:
- Мелкие чанки: точный retrieval (сфокусированный на вопросе), но без контекста.
- Крупные чанки: больше контекста, но менее точно (нужная часть закопана).
Обе крайности теряют качество. Sweet spot варьируется по типу контента.
Распространённые стратегии:
Фиксированный размер с overlap. Резать на чанки по N токенов (300–800 токенов) с overlap 50–100 токенов. Просто, работает как baseline.
Sentence-aware. Резать по границам предложений (используя nltk, spaCy или аналог). Избегает разрывов в середине предложения.
По параграфам. Каждый параграф — чанк. Работает для документов с хорошо структурированными параграфами.
Иерархический (мелкий + крупный). Два индекса:
- Мелкие чанки (300 токенов) для точного retrieval.
- Крупные чанки (1500 токенов) или целые секции для контекста. На retrieval — достаём мелкие; в LLM подаём родительский крупный.
Учёт структуры документа. Используйте структуру документа (заголовки, секции) для формирования чанков. Каждая секция — чанк, иерархия сохраняется.
Семантический chunking. Используйте эмбеддинги, чтобы найти естественные точки разрыва (где меняется тема). Дороже, но даёт лучшие чанки для некоторого контента.
LLM-summarization chunking. Длинные документы суммируются в иерархические чанки на нескольких уровнях (резюме параграфа, резюме секции, резюме документа). LLM генерирует это один раз при ingestion.
Правильная стратегия зависит от контента. Статьи и вики: параграфы или иерархия. Код: уровень функций. Разговоры: по репликам. Технические доки: учёт структуры.
Метаданные чанков. Каждый чанк должен нести:
- ID и URL исходного документа.
- Путь секции (chapter > section > subsection).
- Номер страницы (для цитирования).
- Заголовки выше этого чанка (для контекста).
- Метаданные уровня документа (дата, автор, тип).
Эти метаданные позволяют фильтровать и цитировать в финальном ответе.
Стадия 3: embedding
Превращаем чанки в векторы.
Выбор embedding-модели.
В 2026:
- OpenAI text-embedding-3-large: сильный универсал, дорогой.
- Voyage Voyage-3: конкурентоспособен, часто лучше на техническом контенте.
- Cohere embed-v4: сильный мультиязычный.
- BAAI bge-large: сильный open-source.
- Nomic embed-text: хороший open-source, бесплатный для self-host.
- Доменно-специализированные модели: для кода (Voyage Code, OpenAI text-embedding-3-large тоже работает для кода), юридический, медицинский и т. д.
Выбор модели: тестируйте на своём домене. Не считайте, что лидер ладдерборда лучший для ваших данных.
Размерность embedding. Модели предлагают 256–3072 измерений. Выше размерность = выше качество, выше стоимость/хранение. Для большинства случаев sweet spot 768–1536.
Версионирование. Embedding-модели обновляются; вы можете захотеть переключиться. Планируйте это:
- Отслеживайте, какая версия модели произвела каждый вектор.
- Заново эмбеддите всё при миграции (или используйте dual-index переход).
- Не смешивайте векторы разных моделей в одном поиске.
Стоимость. Эмбеддинг миллионов чанков стоит денег. €0.10–€0.50 за миллион токенов (зависит от провайдера). Для больших корпусов считайте заранее.
Хранение. Векторы большие. 1M чанков × 1536 измерений × 4 байта = 6GB. Планируйте хранилище соответственно.
Стадия 4: retrieval
Приходит запрос. Нужно найти релевантные чанки.
Векторный поиск (dense retrieval). Эмбеддим запрос, ищем ближайших соседей. Ловит семантическое сходство. Стандарт.
Поиск по ключевым словам (BM25 / lexical). Традиционный текстовый поиск. Ловит точные совпадения, редкие термины, конкретные имена. Часто дополняет векторный.
Гибридный поиск. Запускаем оба, сливаем результаты. 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])На практике гибрид обыгрывает чисто векторный или чисто keyword-поиск на большинстве бенчмарков. Используйте по умолчанию.
Фильтрация по метаданным. Фильтруйте retrieval по метаданным до скоринга. «Только документы с 2025+». «Только документы из доступного пользователю набора». «Только документы типа policy».
Критично для мульти-тенантных систем: каждый запрос ограничен документами, доступными тенанту.
Понимание запроса. Препроцессинг запроса:
- Коррекция опечаток. «engineerin» → «engineering».
- Раскрытие аббревиатур. «MFA» → «Multi-Factor Authentication MFA».
- Синонимы / расширение запроса. Генерируем альтернативные формулировки; ищем по каждой.
- Декомпозиция. Многосоставные вопросы разбиваются на подзапросы, каждый retrieve-ится отдельно.
Эти шаги препроцессинга значительно улучшают retrieval на реальных запросах.
HyDE (Hypothetical Document Embeddings). Сгенерировать с помощью LLM фейковый «идеальный ответ». Заэмбеддить его. Использовать результат как retrieval-запрос. Часто работает лучше, чем эмбеддинг вопроса напрямую, потому что фейковый ответ ближе к реальным документам-ответам.
def hyde_retrieve(query, k=20):
hypothetical = llm("Write a passage answering: " + query)
embedding = embed(hypothetical)
return vector_search(embedding, k=k)Компромисс: дополнительный LLM-вызов на запрос (latency, стоимость). Стоит того для сложных запросов; перебор для простых.
Multi-vector retrieval. Вместо одного вектора на чанк — несколько (например, один для чанка, один для резюме, один для гипотетических вопросов). Добавляет хранение и сложность, но улучшает retrieval для некоторого контента.
Стадия 5: reranking
После начального retrieval (top-k, k=20–50) используйте reranker, чтобы более аккуратно оценить кандидатов.
Зачем rerank?
Начальный retrieval быстрый, но неточный. Векторная близость — грубая прокси для релевантности. Reranker (обычно cross-encoder модель) читает запрос и каждого кандидата вместе, выдавая более точную оценку.
Варианты reranker:
- Cohere Rerank. Индустриальный стандарт. Хорошее качество, хостируемое.
- Voyage Rerank. Сильный конкурент.
- BGE Rerank-large. Open-source, бесплатный для self-host.
- MiniLM cross-encoders. Меньше, быстрее, ниже качество.
- LLM-based reranking. Используем маленькую LLM для скоринга пар query-кандидат. Самое высокое качество, самая высокая стоимость.
Эффект.
По нашему опыту, добавление reranking улучшает качество end-задачи на 10–20% в большинстве RAG-систем. Почти всегда стоит того по latency и стоимости.
Типичная конфигурация:
- Достать 30–50 кандидатов гибридным поиском.
- Rerank до top 5–10.
- Передать топ-результаты в LLM.
Адаптивный reranking.
Для запросов, где топ-1 результат имеет высокое векторное сходство (явное совпадение), reranking пропускайте. Для запросов с близкими скорами (несколько кандидатов вровень) — rerank-ите агрессивно.
Стадия 6: генерация
LLM производит ответ, используя retrieved-контекст.
Структура промпта:
You are an assistant answering questions based on the provided context.
Context:
[Document 1 - title, URL]
[chunk 1 content]
[Document 2 - title, URL]
[chunk 2 content]
...
User question: {query}
Instructions:
- Answer based only on the provided context.
- If the context doesn't contain the answer, say so. Don't make up information.
- Cite sources using [Source N] notation.
- Be concise but complete.Паттерны промпта, которые имеют значение:
- Тегирование источников. Каждый чанк контекста тегируется источником для цитирования.
- Инструкции по grounding. Явно говорим модели использовать только контекст.
- Требование цитирования. Принуждаем к цитированию. Ловит галлюцинации.
- Fallback-инструкция. «If the context doesn't have the answer, say so.» Предотвращает confabulation.
Обработка цитирования.
Распространённый паттерн: включить ссылки на источники в ответ. UI рендерит их как кликабельные.
"The company's remote work policy allows up to 4 days/week from home [1].
[1]: https://wiki.company.com/policies/remote-work"Это делает ответ верифицируемым. Пользователи больше доверяют grounded-ответам.
Управление размером контекста.
Длинные контексты деградируют. Большинство моделей лучше работают со сфокусированными контекстами (5–10 высокорелевантных чанков), а не со свалкой всего. Качество падает с распуханием контекста.
Если приходится включать много контекста, суммируйте менее релевантные элементы, а не включайте их целиком.
Оценка на каждой стадии
Нельзя оптимизировать то, что не измеряешь. Каждой стадии нужны свои evals.
Eval ingestion. Распарсились ли документы корректно? Сэмплируйте документы; проверьте, что ключевой контент сохранён.
Eval chunking. Правильного ли размера чанки? Сохраняют ли контекст? Рвутся ли по разумным границам?
Eval embedding. Эмбеддятся ли похожие концепции похоже? На тест-сете заведомо похожих и заведомо разных пар — соответствует ли cosine similarity ожиданиям?
Eval retrieval. Для данного запроса — есть ли релевантные чанки в top-K? Типичная метрика: recall@K (какой % запросов имеет хотя бы один релевантный чанк в top K).
Eval reranking. Среди retrieved-кандидатов — ставит ли reranker самый релевантный первым? Метрика: NDCG (normalized discounted cumulative gain) или MRR (mean reciprocal rank).
Eval генерации. При данном контексте и запросе — выдаёт ли LLM корректный ответ? Метрики: faithfulness (использует ли ответ контекст?), correctness (верен ли ответ?), helpfulness (отвечает ли он на намерение пользователя?).
End-to-end eval. При реальном запросе — выдаёт ли система правильный ответ? Самое важное; зависит от всех стадий.
Вам нужны тест-сеты для каждой стадии. Типичный bootstrap:
- Собрать 50–100 запросов с известно-хорошими ответами.
- Для каждого запроса определить, какой документ(ы) содержит ответ.
- Использовать это для оценки retrieval (достали ли мы нужные документы?) и генерации (верен ли ответ?).
Инструменты: Ragas, TruLens, кастомные наборы. Конкретный инструмент менее важен, чем сам факт запуска evals.
Операционные вопросы
Несколько production-реалий:
Indexing-пайплайны. Приходят новые документы. Re-embed. Обновить индексы. Обработать удаления и обновления. Этот пайплайн работает постоянно, не только на сетапе. Стройте его как систему, а не как одноразовый скрипт.
Latency.
Типичная разбивка:
- Embedding (запрос): 50–200 мс.
- Векторный поиск: 50–200 мс.
- Reranking: 200–500 мс (зависит от K).
- Генерация: 1–5 с (зависит от контекста и модели).
- Итого: 1.5–6 с.
Оптимизация:
- Кэшировать эмбеддинги частых запросов.
- Кэшировать reranking для повторяющихся пар query-candidate.
- Стримить генерацию.
- Параллелить, где возможно.
Для latency < 2 с обычно нужны быстрая embedding-модель, быстрая векторная БД и либо быстрый reranker, либо пропуск reranking для закэшированных/простых запросов.
Стоимость.
Стоимость на запрос:
- Embedding: ~€0.0001.
- Векторный поиск: ~€0.0001 (зависит от инфраструктуры).
- Reranking: €0.001–0.01 (зависит от модели).
- Генерация: €0.005–0.05 (зависит от модели и контекста).
- Итого: ~€0.01–0.05 на запрос.
На масштабе это складывается. Миллион запросов = €10K–50K.
Оптимизация стоимости:
- Кэшировать.
- Использовать более дешёвые модели там, где качество позволяет.
- Сжимать контекст (резюме вместо полных чанков).
Обновления. Документы меняются. Паттерны:
- Версионирование: у каждого документа есть версия; старые версии хранятся или удаляются по политике.
- Инкрементальное: новые версии re-chunk-ятся и re-embed-ятся; старые чанки удаляются.
- Diff-aware: только изменённые секции переобрабатываются.
Для сценариев с большим объёмом обновлений это существенно.
Права. RAG над приватными данными: у документов есть контроли доступа; retrieval должен их уважать.
- Фильтрация по метаданным (у каждого чанка есть метаданные accessible-by).
- Tenant-изолированные индексы для жёсткой изоляции.
- Audit logging того, кто к чему обращался.
Не доверяйте LLM энфорсить права. Энфорсите на retrieval.
Типичные режимы провалов
Несколько паттернов, которые мы видим в провальных RAG-системах:
Провал 1: плохой chunking. Чанки рвутся посреди мысли, таблица разбита между чанками, заголовки отделены от своего содержания. Фикс: лучшая стратегия chunking.
Провал 2: retrieval промахивается мимо ответа. Релевантный чанк существует, но не находится. Часто — несоответствие запроса и документа. Фикс: HyDE, расширение запроса, лучшие эмбеддинги.
Провал 3: правильные чанки, неверный порядок. Релевантный чанк извлечён, но ранжирован низко. LLM использует более высокоранжированные нерелевантные чанки. Фикс: reranking.
Провал 4: правильные чанки, галлюцинация. Чанки правильные, но LLM придумывает дополнительную информацию. Фикс: более строгий grounding-промпт, требования цитирования, более низкая температура.
Провал 5: правильные чанки, неверный ответ. Чанки содержат ответ, но LLM извлекает неверную часть. Фикс: лучший промпт генерации, возможно — модель побольше.
Провал 6: устаревшие данные. Индекс не обновлялся. Фикс: непрерывный ingestion-пайплайн.
Провал 7: утечки прав. Пользователи видят чанки, которые им не положены. Фикс: энфорсить фильтрацию на retrieval; audit.
Провал 8: разгон затрат. Latency и стоимость выросли с ростом корпуса и трафика. Фикс: кэширование, маршрутизация моделей, возможно — архитектурные изменения.
Разобранный пример: RAG для корпоративной базы знаний
Чтобы было конкретно: типичная RAG-система над корпоративной базой знаний.
Корпус: ~50 000 документов (wiki-страницы, политики, runbook-и, заметки встреч, slack-треды).
Пайплайн:
- Ingestion: - Notion: экспорт через API. - Slack: archive export, отфильтрованный до релевантных каналов. - Google Drive: API-экспорт согласованных папок. - Очистка: убрать сообщения только из эмодзи, boilerplate-подписи.
- Chunking: - Иерархический: мелкие чанки уровня параграфа, родительские чанки уровня секции. - ~250K общих чанков на мелком уровне. - Метаданные: источник, путь секции, last_updated, accessible_to.
- Embedding: - Voyage-3 embeddings, 1024 измерения. - Хранение в Pinecone (managed). - Еженедельный re-embedding для изменённых документов.
- Retrieval: - Гибридный: векторный поиск + BM25 (через гибрид Pinecone). - HyDE для запроса. - Фильтр по метаданным для доступности. - Top 30 извлекается.
- Reranking: - Cohere Rerank. - Top 30 → top 8.
- Генерация: - Claude 4 Sonnet. - Top 8 родительских чанков (не мелких) подаются как контекст. - Цитирование требуется в ответе.
Eval:
- 100 вручную составленных пар query/answer.
- Retrieval recall@30: ~92%.
- Корректность финального ответа: ~85%.
- Faithfulness (без галлюцинаций): ~95%.
Операции:
- Ежедневный инкрементальный ingestion.
- Еженедельный полный re-embedding для изменённых документов.
- Per-query observability.
- Ежемесячный re-run eval; мониторинг трендов.
- Ежеквартальный обзор query log для поиска новых паттернов провалов.
Стоимость:
- Embedding: ~€500/месяц (в steady-state).
- Хостинг векторной БД: ~€800/месяц (Pinecone).
- Retrieval + генерация на запрос: ~€0.02.
- Итого: ~€2 500/месяц + €0.02 × объём запросов.
Для большинства компаний это полностью оправдано приростом продуктивности.
План 90-дневной сборки RAG
Если стартуете с нуля:
Дни 1–30: MVP.
- Определить корпус и основной use case.
- Базовый ingestion (один источник).
- Базовый chunking (фиксированный размер с overlap).
- Стандартная embedding-модель.
- Только векторный поиск (без rerank).
- Простой промпт генерации.
Дни 31–60: качество.
- Вручную собрать eval-сет (50–100 запросов).
- Определить режимы провалов.
- Добавить reranking.
- Добавить гибридный поиск.
- Улучшить chunking.
Дни 61–90: операции.
- Непрерывный ingestion-пайплайн.
- Observability.
- Автоматизация eval.
- Права/auth.
- Мониторинг стоимости.
Через 90 дней у вас система, которую можно не просто демонстрировать, а реально использовать. Реальные пользователи могут на неё опереться.
Главный вывод
Production RAG — это шестистадийный пайплайн с проблемами качества на каждой стадии. Паттерны, которые отличают работающие системы от разочаровывающих:
- Ingestion: тщательный парсинг, сохранение структуры, захват метаданных.
- Chunking: подходящая стратегия для типа контента, часто иерархическая.
- Embedding: качественная модель, версионирование, план миграции.
- Retrieval: гибрид по умолчанию, понимание запроса, фильтрация по метаданным.
- Reranking: почти всегда того стоит.
- Генерация: grounding, цитирование, fallback для неизвестного.
- Eval: на каждой стадии, постоянно.
Это не опциональный полишинг. Это разница между RAG, который попадает в 60% качества ответа, и RAG, который попадает в 90%.
Вложение реальное — production-сборка RAG занимает месяцы, не недели. Награда — система, которой ваши пользователи могут реально доверять. Это и есть значимый порог.