LLM знает мир, но не знает вашу компанию. RAG (Retrieval-Augmented Generation — генерация с дополнением через поиск) подмешивает релевантные куски ваших данных в промпт перед генерацией: запрос → поиск в БД → подмешивание (inject) найденного в контекст → генерация. Дальше — как устроен этот конвейер, чем его чинят на грязных документах и почему «улучшать retrieval» перестаёт помогать ровно там, где задача становится многошаговой.
1. RAG — это конвейер, а не модель
Базовое правило: качество retrieval (поиска) критичнее качества LLM — «мусор на входе = мусор на выходе». Можно взять флагманскую модель, но если поиск вернул не те чанки, ответ будет уверенно неверным. Поэтому RAG разбирают не как «умную модель», а как конвейер из двух слоёв:
INDEXING → docs → chunking → embeddings → vector store
offline, тяжёлый, один раз
│
▼
QUERY → запрос пользователя (online, на каждый запрос)
│
▼
SEARCH → hybrid search — recall (полнота): top-50…100 кандидатов
│
▼
RERANK → reranking — precision (точность): top-5 чанков
│
▼
GENERATE → inject в контекст → генерация с цитатамиИндексация — дорогая разовая операция: даже короткий документ нужно прогнать через нарезку и embedding-модель (модель векторного представления), и это заметно тяжелее одного поискового запроса. Поиск — лёгкий, его гоняют на каждый запрос. Это разделение определяет всю эксплуатацию: индекс строят один раз на мощном узле, сохраняют на диск и переиспользуют, а инференс крутят хоть на CPU.
Грубая аналогия: база знаний — учебник, retrieval — оглавление (находит нужную страницу), генерация — ученик, который читает и формулирует. Качество ответа упирается в качество оглавления, а не в эрудицию ученика. Но у этого правила есть граница, к которой вернёмся в финале.
2. Не только векторный поиск
Чистого векторного поиска в 2026 уже недостаточно. У каждого метода своя слепая зона: вектор «размазывает» точные ID, коды и имена собственные («ошибка GO-124», «договор №123»), а лексический BM25 пропускает синонимы и смысл. Hybrid search (гибридный поиск) ловит обе категории — берёт топ от каждого метода и сливает ранги через RRF (Reciprocal Rank Fusion — слияние по обратному рангу):
RRF(d) = 1/(k + rank_bm25(d)) + 1/(k + rank_vector(d)), k ≈ 60
Документ, высоко стоящий в обоих списках, получает максимальный итоговый ранг. Константа k (магическое число 60) сглаживает вклад низких позиций. Схема recall-этапа: BM25 топ-50 + Vector топ-50 → RRF топ-20.
Нюанс хранилища: PostgreSQL full-text search (tsvector/tsquery) — это лексический поиск, но он не равноценен полноценному вероятностному BM25 из dedicated-систем. Для честного production-hybrid чаще берут отдельный слой (Qdrant с нативным sparse/BM25, OpenSearch).
Смежные приёмы, когда запрос короткий или неоднозначный:
- Query expansion — запрос расширяют синонимами.
- HyDE (Hypothetical Document Embeddings) — LLM генерирует «гипотетический ответ», и поиск идёт уже по нему, а не по голому вопросу. Близкий вариант — Query2doc.
- Parent-document retrieval (small-to-large) — ищем по мелким чанкам (точность retrieval), а в LLM отдаём родительский крупный документ (полнота контекста). Прибавка Context Precision порядка 15–25%.
- Multi-hop (многошаговый поиск по цепочке документов) — связать факты из разных документов: туда обычный топ-K плохо дотягивается, это уже территория Agentic RAG и GraphRAG.
Связка Hybrid Search + Reranking покрывает примерно 80% use cases — это и есть минимальный продакшен-стандарт 2026.
3. Чанкинг и грязные документы
Чанкинг — первая точка, где ломается RAG. Граница чанка определяет, окажется ли нужный факт целиком в одном фрагменте или расколется пополам. Классический провал — анафоры: чанк «Его цена 500 руб» не знает, что «его» = товар X из соседнего фрагмента.
Наивный fixed-чанкинг 512/128 — не универсальный дефолт: он рвёт предложения и отделяет вопрос от ответа. Диагностика oversized-чанкинга проста: возьмите 15 запросов из production-логов и измерьте плотность релевантных предложений в топовом чанке — если ниже 25%, нарезка слишком широкая. Рабочие ориентиры размеров (не закон, а старт):
| Тип контента | Размер чанка | Overlap |
|---|---|---|
| Проза | 500–800 | 10–15% |
| Код | 200–400 | structure-aware (AST) |
| Регуляторика | 800–1200 | + parent-heading |
| Таблицы | row-by-row | + инъекция заголовков колонок |
Отдельная боль — грязные документы: сканы, фото, многоколоночные PDF, таблицы с объединёнными ячейками. Голый текстовый экстрактор разрушает структуру таблицы, и чанк теряет смысл. Здесь нужен layout-aware препроцессинг: Docling (локально, парсит таблицы и формулы, держит таблицу в изолированном чанке), LlamaParse (облако, чистый иерархический Markdown), Reducto (вложенные заголовки, merged cells, multi-page таблицы), DocStrange (OCR по низкокачественным сканам).
Чтобы чинить анафоры на уровне эмбеддинга, есть три пути — и третий снимает компромисс первых двух:
- Late Chunking (Jina, 2024) — весь документ сначала прогоняется через embedding-модель (self-attention видит всё), потом режется на чанки со span pooling. Каждый токен уже «знает» глобальный контекст, без доп. LLM-вызовов. Ограничение — контекстное окно модели.
- Contextual Retrieval (Anthropic, 2024) — LLM дописывает к каждому чанку краткий контекст документа перед эмбеддингом. Решает анафоры лучше всех, но дорого: LLM-вызов на каждый чанк. «Для VIP-документов».
- voyage-context-3 — нативные contextualized-эмбеддинги в один проход, без ручной аугментации и без лимита окна. На chunk-level retrieval обходит late chunking на 23.66% и contextual retrieval на 6.76%; на document-level — на 20.54% и 2.40%. Это снимает trade-off «дорогой Contextual против ограниченного окна Late Chunking».
4. Reranking: воронка точности
Векторный поиск (bi-encoder) кодирует вопрос и документ независимо и сравнивает косинусом — быстро, но грубо. Cross-encoder подаёт пару «вопрос + документ» в модель вместе, поэтому ранжирует точнее, но медленнее, и его нельзя гонять по всей базе: простой запрос на cross-encoder по всему корпусу может занять около двух минут.
Поэтому его применяют как сужающуюся воронку — только к уже отфильтрованному топу:
hybrid search ──▶ over-retrieve: 12–20 кандидатов
cross-encoder ──▶ rerank down: 6–10
финал ──▶ в промпт LLMЭто прямой контраст с дефолтным top-3: сначала набрать с запасом, потом точно отсеять. Reranking даёт стабильный +5–8% accuracy при контролируемой latency и при этом самый дешёвый по усилиям — поэтому входит в Production Baseline вместе с hybrid search. Целевые ориентиры: P95 < 3 c, TTFT (время до первого токена) < 500 мс.
from sentence_transformers import CrossEncoder
reranker = CrossEncoder("cross-encoder/ms-marco-MiniLM-L-6-v2")
def rerank(question, candidates, k=8):
pairs = [(question, c.page_content) for c in candidates] # пара вместе
scores = reranker.predict(pairs) # точный балл
ranked = sorted(zip(scores, candidates), key=lambda x: x[0], reverse=True)
return [c for _, c in ranked][:k]
Из мультиязычных reranker'ов текущий ориентир — jina-reranker-v3 (0.6B, listwise, BEIR 61.94 nDCG@10, покрывает русский). Но конкретные SOTA-модели устаревают быстро — на эту цифру не молиться.
5. Предобработка: метаданные, версии, дедупликация
Retrieval-конвейер легко недооценить со стороны «гигиены корпуса». Несколько вещей, без которых прод тихо деградирует:
- Метаданные на каждом чанке:
doc_id, заголовок секции,url/путь, timestamp, версия embedding-модели (детект дрейфа) и content hash. - Дедупликация в два слоя: идентичные файлы отсекаются SHA-256 по каноническому содержимому; near-duplicate ловят через MinHash + Jaccard и LSH-джойны (Locality-Sensitive Hashing) и выпиливают.
- Жизненный цикл (lifecycle) / каскадные удаления: векторные БД не делают нативный update. Когда документ изменён или удалён, нужен Document Registry (
doc_id → chunk_ids), чтобы найти и зачистить устаревшие чанки и эмбеддинги, иначе в выдаче всплывают мёртвые версии. - Embedding drift: устаревшие векторы тихо теряют 10–20 пунктов качества retrieval за год. Лечение — отслеживать версию модели в метаданных и переиндексировать.
- Faithfulness floor: целевая точность привязки ответа к источникам около 90%, абсолютный минимум безопасности — 70%; ниже него алерт должен остановить тихую деградацию качества.
Смена модели эмбеддингов — это, по сути, миграция схемы БД: старые векторы геометрически несовместимы с новыми запросами, нужна полная переиндексация. Делают её zero-downtime через alias: строят теневой индекс новой моделью, валидируют на бенчмарк-наборе, атомарно переключают alias, старый держат для отката.
6. Где хранить векторы
После чанкинга и эмбеддинга векторы надо где-то держать и быстро искать ближайших соседей (ANN). Выбор — по масштабу, скорости и потребности в фильтрации:
| БД | Масштаб | Сильная сторона |
|---|---|---|
| FAISS / Chroma | in-memory | Прототип, минимум настройки, persist на диск |
| pgvector (Postgres) | ≤~1M уверенно, до ~10M с тюнингом | Развёрнут везде; embeddings рядом с relational data |
| Qdrant | >100M, единицы мс* | Лучшая фильтрация по метаданным, нативный BM25 |
| Weaviate | — | Популярный универсал |
| Milvus | >100M, K8s | Лучшее масштабирование, RRF из коробки |
| Redis | — | Низкая latency, vector-поиск поверх кэша |
Про потолок pgvector важно без догматизма: он уверенно тянет малые и средние корпуса, особенно когда векторы — атрибут relational-строки и не нужна селективная фильтрация или hybrid. Комфортный ориентир — до ~1M векторов, до ~10M с тюнингом и компромиссами по index build / памяти / recall. Порог 50M не брать как дефолт без своего бенчмарка — строгие числа (включая «единицы мс» латентности Qdrant, отмеченные *) идут от вендора и в независимых замерах на 10M ближе к 20+ мс. Когда нужна filterable HNSW, sparse/BM25, multivector — переходят на dedicated store. Отдельная ловушка: ColBERT эмбеддит каждый токен, и 100k документов превращаются в 10M+ векторов — это уже за пределами pgvector.
7. Agentic RAG и GraphRAG: когда они оправданы
Линейный RAG — один проход. Две архитектуры из Enterprise-фазы надстраиваются над ним, но обе дорогие, и включать их «по умолчанию» — ошибка.
Agentic RAG ставит над поиском агентный цикл: query planner декомпозирует запрос, retrieval agent выбирает источник (vector / SQL / API), reflection через LLM-as-Judge проверяет полноту, и при нехватке данных запрос переписывается и поиск повторяется. Это снимает single-shot-ограничение на multi-step и cross-system задачах. Но у автономии есть цена: на верификации фактов (FEVER) у Agentic RAG recall падает до ~49% — агент рано решает, что данных достаточно, и останавливает поиск, тогда как детерминированный Enhanced RAG держит ~84%. Поэтому его не берут под жёсткий SLA, ограниченный бюджет или требование воспроизводимости (финансы, медицина).
GraphRAG вместо изолированных чанков строит граф знаний: LLM извлекает сущности и связи, и ответ собирается обходом по рёбрам. Незаменим для global / multi-hop вопросов («какие основные темы во всём архиве?», «как связаны X и Y из разных документов?»), где топ-K похожих чанков бессилен. Comprehensiveness на global-вопросах растёт на 72–83%. Расплата — отдельная база поверх векторной: стоимость памяти и индексации примерно ×2. Семейство расширяется (NodeRAG, RAPTOR, TreeRAG), доступно коробочно в RAGFlow и Dify.
Для advanced-сборки этих пайплайнов удобен LlamaIndex: QueryFusionRetriever оркестрирует семантико-лексический hybrid (dense + sparse BM25) из коробки, а поверх ложатся reranking и агентные flow.
8. Узкое место смещается в reasoning
Тезис «качество упирается в retrieval» верен, пока запрос простой. На multi-hop он перестаёт быть полным. Свежие замеры на Graph-RAG показывают разрыв: покрытие контекста (gold-ответ где-то в найденном тексте) достигает 77–91%, а итоговая точность ответа держится всего между 35% и 78%. То есть нужное уже найдено, но модель не может его связать.
retrieval coverage █████████████████░░░ 77–91% (нашли)
response accuracy ███████████░░░░░░░░░ 35–78% (ответили)
└─ разрыв = reasoning failureРазбор ошибок: 73–84% из них — это reasoning failures, а не retrieval failures. Сильный поиск не гарантирует сильный ответ. Лечение здесь — не «ещё лучше искать», а структурировать рассуждение: structured prompting (например, SPARQL chain-of-thought на графовых пайплайнах) даёт +7.6 п.п. на 2WikiMultiHopQA. Свежий приём ConRAG согласует relational / entity / textual сигналы в едином ранжировании и прибавляет до 10.2 пункта Accuracy на MuSiQue — самом сложном из multi-hop бенчмарков (2–4 шага рассуждения, похожие дистракторы).
Практический вывод: прежде чем усложнять retrieval, стоит померить, где именно ломается цепочка — в поиске или в рассуждении. Для этого появились process-level инструменты: RAGCap-Bench оценивает планирование, извлечение свидетельств, grounded inference (вывод с опорой на найденные источники) и устойчивость к шуму по отдельности; AgenticRAGTracer делает hop-aware трассировку и показывает, на каком конкретно хопе агент «схлопнулся» или «переусердствовал». Не доверяй одной цифре итоговой точности — она прячет, на каком этапе утекло качество.
9. Метрики: чем мерить качество
Финал §8 — частный случай общего правила: одна итоговая цифра прячет место утечки. Поэтому качество RAG меряют не одним числом, а дашбордом из ~12 показателей, разбитых по группам — и каждая группа отвечает на свой вопрос: нашли ли (Retrieval), хорошо ли ответили (Generation), как сработал конвейер целиком (End-to-End). Если итоговая точность просела, именно разбивка по группам показывает, где — в поиске, в рассуждении или в склейке контекста.
Главное правило старта: начни с Hit Rate. Если документы не находятся, остальные метрики бессмысленны — улучшать генерацию поверх пустого контекста нечего. Вторая по приоритету — Faithfulness: галлюцинации остаются главной проблемой RAG в проде, и ловит их именно она.
| Метрика | Группа | Цель | Что измеряет |
|---|---|---|---|
| Hit Rate@K | Retrieval | ≥ 0.85 | recall: попал ли релевантный документ в топ-K |
| MRR | Retrieval | ≥ 0.75 | позиция первого релевантного |
| NDCG@K | Retrieval | ≥ 0.70 | качество ранжирования с учётом позиции |
| Faithfulness | Generation | ≥ 0.85 | доля утверждений ответа, подтверждённых контекстом (галлюцинации) |
| Answer Relevancy | Generation | ≥ 0.80 | соответствие ответа вопросу |
| Answer Correctness | Generation | ≥ 0.75 | F1 + семантическое сходство с эталоном |
| Toxicity | Generation | ≤ 0.05 | безопасность ответа |
| Context Precision | End-to-End | ≥ 0.70 | доля релевантных чанков в контексте |
| Context Recall | End-to-End | ≥ 0.80 | покрытие всех нужных документов |
| Context F1 | End-to-End | ≥ 0.75 | баланс Precision и Recall |
| Noise Robustness | End-to-End | ≥ 0.90 | устойчивость к нерелевантным чанкам в контексте |
| Latency P95 / TTFT | Операц. | < 3 c / < 500 мс | время ответа end-to-end и до первого токена |
Пороги — ориентир из материалов, не закон: для русскоязычного домена реалистичные планки стоит откалибровать на своём наборе. Faithfulness здесь даётся как планка RAGAS (≥0.85); это согласуется с прод-floor из §5 — целевая привязка к источникам около 90%, а 0.85 — рабочий порог алерта, ниже которого систему уже нельзя выпускать.
Считать всё это руками не нужно. RAGAS — open-source стандарт де-факто: LLM-as-Judge оценивает Faithfulness, Answer Relevancy, Context Precision/Recall без ground truth, плюс автогенерирует синтетический eval-датасет из ваших документов (from ragas import evaluate). Рядом — DeepEval (pytest-стиль для CI/CD), RAGChecker (Amazon, claim-level диагностика), TruLens (RAG Triad). Цена offline-прогона на GPT-4o-судье — порядка $0.05–0.20 за запрос, можно сбить локальной моделью.
Метрики не считают разово — их встраивают в замкнутый цикл, и замер идёт на каждой фазе внедрения: не переходи к следующей технике, пока текущие пороги не взяты.
ЛОГИРОВАНИЕ → query, retrieved_chunks, answer, latency, feedback
│
▼
ОЦЕНКА → online (без GT): cosine, Faithfulness
offline: тест-датасет 50–200 вопросов
│
▼
МОНИТОРИНГ → дашборд + алерты при падении ниже порога
│
▼
УЛУЧШЕНИЕ → анализ провалов, A/B на одном параметре, реиндекс/промпт
│
└──▶ повторный замер → цикл с начала- Логирование — на каждый запрос сохраняют
query,retrieved_chunks,answer,latencyиfeedback(лайк/дизлайк, копирование ответа как implicit-сигнал). - Оценка — online без ground truth (cosine similarity, Faithfulness через LLM-as-Judge, Toxicity-классификатор на каждый ответ) и offline на тест-датасете 50–200 вопросов с эталонами, прогоняемом при каждой смене модели, промпта или индекса.
- Мониторинг — дашборд со скользящими средними и алерты: Faithfulness < 0.80 → critical, Hit Rate < 0.75 → warning, Latency P95 > 5 c → warning.
- Улучшение — классифицировать провал (retrieval / ranking / generation / missing knowledge), приоритизировать по частоте и бизнес-импакту, прогнать A/B, поменяв ровно один параметр, и сравнить все метрики до/после.
Без этого цикла RAG невозможно отличить от демо: цифры показывают не только текущее качество, но и где именно оно утекает между фазами конвейера.
Итог
- Качество ответа упирается в retrieval — но только пока запрос простой: мусор на входе = мусор на выходе, и флагманская модель этого не спасёт.
- Минимальный продакшен-стандарт 2026 — Hybrid Search + Reranking: он закрывает ~80% use cases и самый дешёвый по усилиям. Всё остальное (Agentic RAG, GraphRAG, contextualized-эмбеддинги) — надстройки под конкретную боль, не дефолт.
- На multi-hop узкое место смещается из поиска в reasoning: нужное уже найдено (coverage 77–91%), но модель не связывает факты (accuracy 35–78%). Лечат не «ещё лучше искать», а структурированием рассуждения.
- Мерить надо не одной цифрой, а дашбордом по группам (Retrieval / Generation / End-to-End), начиная с Hit Rate и Faithfulness, и встраивать замер в замкнутый цикл логирование → оценка → мониторинг → улучшение.
FAQ
Чем hybrid search лучше чисто векторного?
У каждого метода своя слепая зона: вектор «размазывает» точные ID, коды и имена собственные («ошибка GO-124», «договор №123»), а лексический BM25 пропускает синонимы и смысл. Hybrid берёт топ от обоих и сливает ранги через RRF (Reciprocal Rank Fusion), так что документ, высоко стоящий в обоих списках, получает максимальный итоговый ранг. Это и есть минимальный продакшен-baseline вместе с reranking.
Когда нужен GraphRAG, а когда он лишний?
GraphRAG оправдан на global и multi-hop вопросах — «какие основные темы во всём архиве?», «как связаны X и Y из разных документов?» — где топ-K похожих чанков бессилен; comprehensiveness там растёт на 72–83%. Расплата — отдельная база знаний поверх векторной, стоимость памяти и индексации примерно ×2. Для линейного «найди факт в документе» это избыточно: включать его по умолчанию — ошибка.
Какие метрики мерить у RAG?
Не одну итоговую цифру, а дашборд по трём группам: Retrieval (Hit Rate@K, MRR, NDCG), Generation (Faithfulness, Answer Relevancy, Toxicity) и End-to-End (Context Precision/Recall, Noise Robustness) плюс операционные Latency P95 / TTFT. Начинать с Hit Rate (если документы не находятся, остальное бессмысленно), вторая по приоритету — Faithfulness, она ловит галлюцинации. Считать удобно через RAGAS без ground truth.
pgvector или Qdrant?
По масштабу и потребности в фильтрации. pgvector хорош, когда векторы — атрибут relational-строки и не нужны селективная фильтрация или hybrid: комфортный ориентир до ~1M векторов, до ~10M с тюнингом и компромиссами по памяти и recall. Когда нужны filterable HNSW, нативный sparse/BM25 или multivector, либо корпус крупнее — переходят на dedicated store вроде Qdrant. Порог 50M не стоит брать как дефолт без своего бенчмарка.
Что делать с таблицами и сканами?
Голый текстовый экстрактор разрушает структуру таблицы, и чанк теряет смысл, поэтому нужен layout-aware препроцессинг: Docling (локально, парсит таблицы и формулы, держит таблицу в изолированном чанке), LlamaParse (облако, иерархический Markdown), Reducto (merged cells, multi-page таблицы), DocStrange (OCR по низкокачественным сканам). Таблицы режут row-by-row с инъекцией заголовков колонок, а структурные заголовки секций добавляют в текст чанка до эмбеддинга.
Когда брать Agentic RAG?
Когда задача multi-step или cross-system и нужно декомпозировать запрос, выбирать источник и при нехватке данных переписывать запрос и искать снова. Но у автономии есть цена: на верификации фактов (FEVER) recall у Agentic RAG падает до ~49% против ~84% у детерминированного pipeline — агент рано решает, что данных достаточно. Поэтому его не берут под жёсткий SLA, ограниченный бюджет или требование воспроизводимости (финансы, медицина).
Источники
- The Reasoning Bottleneck in Graph-RAG (arXiv 2603.14045) — на multi-hop узкое место не retrieval, а reasoning: coverage 77–91% при accuracy 35–78%, 73–84% ошибок — reasoning failures.
- voyage-context-3 — contextualized chunk embeddings (Voyage AI) — третий путь между Late Chunking и Contextual Retrieval: native contextualized-эмбеддинги в один проход.
- ConRAG — Consensus-Driven Multi-View Retrieval (arXiv 2605.28093) — согласование relational/entity/textual сигналов в едином ранжировании, до +10.2 Accuracy на MuSiQue.
- RAGCap-Bench (arXiv 2510.13910) — process-level eval для Agentic RAG: планирование, извлечение свидетельств, grounded inference, устойчивость к шуму по отдельности.
- AgenticRAGTracer (arXiv 2602.19127) — hop-aware трассировка: на каком конкретно хопе цепочка «схлопнулась» или «переусердствовала».
- Build an unstructured data pipeline for RAG (Databricks) — двухслойная дедупликация (SHA-256 + MinHash/LSH) и каскадные удаления устаревших чанков.
- RAG Anti-Patterns: 7 Failure Modes (Digital Applied) — антипаттерны прод-RAG с измеримыми порогами: oversized chunking, shallow retrieval, embedding drift, faithfulness floor.
Числовые ориентиры из текста — пороги метрик, бенчмарки векторных БД и проценты приростов — зависят от корпуса и профиля нагрузки; на своих данных и своей нагрузке они будут другими, калибруйте на собственном наборе.