AI-инжиниринг
Advanced RAG: hybrid search, re-ranking, query expansion, self-RAG
Цели урока
- Понять почему naive RAG ломается на реальных данных и как диагностировать проблему
- Реализовать hybrid search (BM25 + dense vectors) с Reciprocal Rank Fusion
- Интегрировать cross-encoder reranking через Cohere или sentence-transformers
- Применять query transformation: HyDE (Gao 2022), multi-query, step-back prompting
- Освоить self-RAG и contextual compression для критичных доменов
Предварительные знания
- RAG pipeline, pgvector, embeddings
Базовый RAG находит похожий текст. Advanced RAG находит правильный ответ. Разница - precision@5: базовый 60-70%, с reranking + hybrid search 85-92%. Это граница между "работает" и "можно продавать". Notion AI перешёл от naive RAG к hybrid + reranking - hallucination rate упал на 40%. Perplexity строит свой поиск на этих же принципах и делает 100M+ запросов в месяц.
- Perplexity AI - hybrid search + re-ranking как core технологии поиска, 100M+ запросов/месяц
- Notion AI - переход от naive RAG к hybrid + reranking снизил hallucination rate на 40%
- Bloomberg GPT - domain-specific re-ranking для финансовых документов с терминологией
- Abridge и Nabla (медицинские AI) - self-RAG для верификации каждого утверждения перед показом врачу
- Anthropic Contextual Retrieval (2024) - комбинация BM25 + embeddings + contextual chunk summaries снижает пропуски на 67%
От Lewis 2020 до Contextual Retrieval 2024
**Lewis et al. 2020 (Facebook AI Research)** - оригинальный RAG paper: DPR encoder + BART generator, первый раз показано что retrieval + generation работает лучше чем pure LM на knowledge-intensive tasks. **Khattab et al. 2020 - ColBERT** - late interaction: вместо одного embedding на документ - матрица токен-embeddings, MaxSim для scoring. Точнее bi-encoder, быстрее cross-encoder. **Gao et al. 2022 - HyDE**: hypothetical document embedding как pre-retrieval трансформация, +5-10% recall на BEIR benchmark. **Anthropic Contextual Retrieval 2024**: BM25 + embeddings + LLM-generated chunk context (каждый chunk обогащается summary его места в документе) - снижение failed retrievals на 67%.
Где ломается Naive RAG
Naive RAG на демо выдаёт 95% accuracy - 10 тестовых вопросов, идеальные чанки, удобные запросы. Production - 55% на реальных пользователях. Первая мысль: поменять модель. Неверно. GPT-4 не поможет, если контекст не найден. Проблема всегда в retrieval.
| Сценарий | Проблема Naive RAG | Пример |
|---|---|---|
| Keyword mismatch | Vector search не находит exact terms | Запрос "ошибка 500 при оплате" - документ содержит "PaymentGatewayException", embedding не совпадает |
| Сложный вопрос | Один embedding не может выразить multi-hop query | "Как связаны лимиты rate-limit и стоимость API?" - нужны chunks из разных разделов |
| Неточный retrieval | Top-5 chunks содержат 2 релевантных и 3 шумовых | LLM отвлекается на нерелевантные chunks и даёт размытый ответ |
| Ambiguous query | Пользователь спрашивает неточно | "Как настроить авторизацию?" - OAuth? JWT? API keys? RBAC? |
Advanced RAG - это три слоя хирургии вокруг поиска. Не замена, а надстройка:
- **Pre-retrieval** - улучшить запрос до поиска (query transformation, HyDE от Gao et al. 2022)
- **Retrieval** - улучшить сам поиск (BM25 + dense hybrid, multi-query с RRF)
- **Post-retrieval** - отфильтровать результаты (cross-encoder reranking, contextual compression)
Каждый слой добавляется независимо. Hybrid search - всегда. Re-ranking - когда precision важнее latency. HyDE - когда вопросы короткие и неоднозначные. Комбинация всех трёх - Contextual Retrieval (Anthropic, 2024), снижение пропусков на 67%.
Пользователь спрашивает "ошибка при деплое", но документация описывает проблему как "DeploymentFailedException in CI/CD pipeline". Naive RAG не находит ответ. Какая это проблема?
Hybrid Search: BM25 + dense vectors
Два мира - два провала в одиночку. Vector search находит "как уменьшить задержку" рядом с "latency optimization" - семантика работает. Но спроси "ошибка E-4012" - embedding не знает что это за код. BM25 (TF-IDF на стероидах) найдёт E-4012 мгновенно, но не поймёт что "уменьшить задержку" и "optimize latency" - одно и то же.
| Метод | Сильные стороны | Слабые стороны |
|---|---|---|
| Vector (semantic) | "Как уменьшить задержку?" → находит "latency optimization" | Пропускает exact terms: "ошибка E-4012" |
| BM25 (keyword) | Точное совпадение: "E-4012" → находит документ с этим кодом | Не понимает синонимы: "уменьшить задержку" ≠ "latency" |
| Hybrid (BM25 + dense) | Комбинирует: семантику + ключевые слова | Нужна настройка весов (alpha) |
Склеивает два мира **Reciprocal Rank Fusion (RRF)** - не усредняет scores (они несопоставимы), а объединяет ранжирования. Документ на 1-м месте в BM25 и на 3-м в vector получает больше, чем тот что стоит на 10-м в обоих:
Реализация hybrid search с pgvector + PostgreSQL full-text search:
Параметр **alpha** подбирается эмпирически. Для технической документации (много кодов ошибок, имён API) - alpha 0.3-0.4 (больший вес keyword). Для общих вопросов - alpha 0.6-0.7 (больший вес semantic). Perplexity использует похожий баланс - это core их технологии поиска.
Больше chunks = лучше recall
Больше chunks = хуже precision, перегруженный reranker и больше шума в контексте
Можно взять top-100 вместо top-20 и recall формально вырастет - нужный документ попадёт в выборку. Но cross-encoder reranker перегружается: он делает forward pass для каждой пары (query, doc), и 100 пар вместо 20 - это +400ms latency. Плюс LLM получает контекст с шумом и начинает отвлекаться. Оптимальный диапазон - 15-25 кандидатов для reranker, 3-7 для финального контекста.
В hybrid search параметр alpha = 0.3 означает...
Re-ranking: cross-encoder и Cohere Rerank
Hybrid search вернул top-20 документов. Но ранжирование всё ещё грубое - cosine similarity между embedding запроса и embedding документа не учитывает тонкие смысловые связи. Bi-encoder кодирует query и doc независимо: они никогда не "видят" друг друга до момента сравнения.
Cross-encoder - другая архитектура. Query и document подаются вместе как один вход, модель делает полный attention между каждым токеном запроса и каждым токеном документа. Это медленно, но точно:
| Bi-encoder (embedding) | Cross-encoder (reranker) | |
|---|---|---|
| Как работает | Query и doc кодируются отдельно, сравниваются cosine similarity | Query и doc подаются вместе, модель выдаёт relevance score |
| Скорость | Быстро - doc уже закодирован, сравнение мгновенное | Медленно - нужен forward pass для каждой пары |
| Точность | Хорошая | Значительно лучше - модель видит оба текста одновременно |
| Масштаб | Миллионы документов | Top-20-50 кандидатов (post-retrieval) |
Паттерн **retrieve-and-rerank** - стандарт production RAG. Широкая сеть на этапе retrieval, точный фильтр на этапе reranking:
Cohere Rerank - 1 доллар за 1000 запросов (каждый до 100 документов). Для self-hosted варианта - **bge-reranker-v2-m3** от BAAI (sentence-transformers), запускается на GPU через Hugging Face Inference. На CPU с 20 документами - около 300ms.
Почему cross-encoder точнее bi-encoder для ранжирования?
Query Transformation: HyDE, Multi-Query, Step-back
Пользователь пишет "почему не работает деплой". Четыре слова. Embedding этих четырёх слов - точка в vector space, маленькая и плохо направленная. Реальный ответ - многостраничный документ с богатым словарём. Расстояние между точкой-вопросом и облаком-документом огромное. Query transformation меняет саму точку до поиска.
HyDE - Hypothetical Document Embedding
Идея Gao et al. (2022): не ищи по embedding запроса - сгенерируй **гипотетический ответ** и ищи по его embedding. Гипотетический ответ написан в стиле документации, богатый терминами, длинный - embedding такого текста гораздо ближе к реальным документам в vector space.
Multi-Query - разбить вопрос на подзапросы
Step-back Prompting - обобщить вопрос
Вместо конкретного "почему NestJS middleware не ловит ошибки async?" - сначала спросить "как работает error handling в NestJS middleware?". Более общий запрос находит фундаментальные документы, которые содержат ответ на конкретный вопрос как частный случай.
HyDE хорош для Q&A по документации - вопросы короткие, документы длинные. Multi-Query - для сложных аналитических вопросов с несколькими аспектами. Step-back - для вопросов "почему X не работает?" где причина в принципе, не в деталях. На практике в RAPTOR и Contextual Retrieval (Anthropic 2024) комбинируются все три.
HyDE (Hypothetical Document Embedding) ищет по embedding...
Self-RAG и Contextual Compression
Что если пользователь спрашивает "сколько будет 2+2?" - зачем вообще делать retrieval? И что если из найденных 5 chunks только один реально релевантен - зачем отдавать все пять в контекст? **Self-RAG** - паттерн где модель сама принимает оба решения: нужен ли поиск, и что из найденного использовать.
Self-RAG: модель как критик
- LLM получает вопрос и решает: нужен ли retrieval? (некоторые вопросы модель знает и без базы)
- Если да - retrieval, получить chunks
- LLM оценивает каждый chunk: релевантен? Полезен для ответа?
- Генерация ответа только на основе одобренных chunks
- LLM оценивает финальный ответ: подтверждён контекстом? Полезен пользователю?
Contextual Compression
Chunk на 500 токенов найден - но ответ на вопрос спрятан в 2 предложениях из 20. Остальные 18 - шум, который занимает context window и сбивает модель. **Contextual compression** (LangChain popularized, Anthropic Contextual Retrieval 2024) извлекает только нужное.
Self-RAG и compression добавляют extra LLM calls. В production это +200-500ms latency и +0.001-0.01 доллара за запрос. Использовать когда accuracy критичнее скорости - медицинские, юридические, финансовые чатботы. Abridge и Nabla применяют именно это для верификации каждого утверждения.
Contextual compression в RAG решает проблему...
RAG Fusion и Parent Document Retriever
RAG Fusion
RAG Fusion комбинирует Multi-Query и RRF в единый pipeline. Генерируется несколько вариантов запроса, каждый ищет отдельно, результаты объединяются через Reciprocal Rank Fusion. Идея: одна формулировка вопроса - одна точка в vector space. Четыре формулировки - четыре точки, покрывающие больше пространства.
Parent Document Retriever
Маленькие chunks - точный retrieval, плохой контекст. Большие chunks - плохой retrieval, хороший контекст. Классический trade-off. **Parent Document Retriever** разрезает его пополам: хранит маленькие chunks для поиска (128 токенов), большие parent chunks для контекста (1024 токена). Ищет по маленьким, отдаёт большие.
Сводка всех advanced RAG техник и когда их применять:
| Техника | Когда использовать | Overhead |
|---|---|---|
| Hybrid Search | Всегда - baseline для production | +10ms (SQL query) |
| Re-ranking | Когда precision важнее latency | +200ms, 0.001/query |
| HyDE | Q&A по документации | +300ms, 0.001/query |
| Multi-Query | Сложные аналитические вопросы | +300ms, 0.001/query |
| RAG Fusion | Максимальный recall, критичная accuracy | +500ms, 0.003/query |
| Self-RAG | Медицина, юриспруденция, финансы | +500ms, 0.005/query |
| Parent Doc Retriever | Длинные документы с контекстом | +5ms (другая схема данных) |
| Contextual Compression | Ограниченный context window | +200ms, 0.001/query |
Если в naive RAG ответы плохие - надо просто добавить больше документов в индекс. Чем больше corpus, тем лучше retrieval.
Качество retrieval упирается не в размер corpus, а в формулировку запроса и распределение релевантности. Hybrid search, re-ranking и query transformation бьют рост corpus в 3-5 раз чаще, чем сам corpus.
Интуиция от классических SQL/full-text баз: больше данных - точнее ответ. В RAG обратное: чем больше документов, тем выше вероятность, что наивный векторный поиск утонет в семантических близнецах и пропустит точное вхождение. Решает не объём, а ансамбль методов поверх одной и той же базы.
Parent Document Retriever ищет по маленьким chunks, но возвращает большие parent chunks. Зачем?
Больше chunks = лучше recall, можно брать top-100
Больше chunks = хуже precision, перегруженный reranker, больше шума в контексте
Recall формально растёт - нужный документ почти наверняка попадёт в top-100. Но cross-encoder делает forward pass для каждой пары (query, doc): 100 пар вместо 20 = +400ms. А главное - LLM получает контекст с шумом: 80 нерелевантных chunks среди 20 нужных и начинает галлюцинировать или давать размытые ответы. Оптимум: retrieve 15-25 кандидатов, rerank до 5-7, в контекст - не больше 5.
Ключевые выводы
- Naive RAG ломается на keyword mismatch, сложных вопросах и ambiguous queries - проблема всегда в retrieval, не в модели
- Hybrid search (BM25 + dense + RRF) - обязательный baseline для production, покрывает keyword mismatch без latency overhead
- Cross-encoder reranking (sentence-transformers, Cohere) даёт +15-20% precision на top-5: модель видит query и doc вместе
- HyDE (Gao 2022) ищет по embedding гипотетического ответа - для коротких вопросов по длинным документам
- Self-RAG + contextual compression - для медицины, юриспруденции, финансов где hallucination недопустима
- Contextual Retrieval (Anthropic 2024): BM25 + embeddings + chunk context summaries - 67% снижение пропусков
Вопросы для размышления
- В каком из четырёх сценариев провала Naive RAG (keyword mismatch, multi-hop, noisy retrieval, ambiguous query) пострадает продукт больше всего в конкретном проекте?
- Hybrid search добавляет +10ms, reranking +200ms, HyDE +300ms. Какой бюджет latency разумен для конкретного use case - и какую комбинацию выбрать?
- Self-RAG добавляет ~5 LLM calls на один пользовательский запрос. При каком сценарии accuracy стоит этой цены?
Что дальше
Retrieval настроен, но его качество критически зависит от того, как разбиты документы. Chunking strategy - следующий рычаг оптимизации.
- Chunking стратегии — Как правильно нарезать документы - fixed, recursive, semantic chunking
- Conversation Memory — RAG в контексте чатбота - как совмещать retrieval с историей разговора
- Evaluation — Как системно измерять качество RAG pipeline на golden datasets
Связанные уроки
- aie-12-rag-fundamentals — Базовый RAG - фундамент для advanced техник
- aie-09-embeddings — Dense retrieval строится на embedding quality
- aie-10-vector-databases — HNSW-индекс Qdrant критичен для hybrid search
- aie-14-chunking-strategies — Правильный chunking удваивает precision advanced RAG
- prob-04-bayes — Reranking - posterior update поверх prior retrieval
- ml-52-search-ranking — Cross-encoder reranking из search ranking теории
- db-26-caching
- db-28-search