Урок курса
Гибридный поиск в RAG: BM25, векторы и reranking
Vector DB & RAG DeveloperВекторный поиск хорошо находит смысловые переформулировки, но может пропускать точные артикулы, имена и редкие термины. BM25 опирается на совпадение токенов и частоты: он силён для идентификаторов и ключевых слов, но слабее понимает синонимы. Гибридный retrieval запускает оба метода и объединяет кандидатов.
Один и тот же документ хранит текст, метаданные и dense embedding; для лексического пути используется inverted index или sparse vector. По запросу система получает два ranking list, после чего выполняет fusion. Так exact match не исчезает из-за семантики, а естественная формулировка не требует точного слова из документа.
Фильтры по tenant, доступу, языку и времени применяются до выдачи контекста модели. Гибридный score не заменяет авторизацию.
Fusion и RRF
Сырые score разных retriever живут в разных шкалах: cosine similarity ограничена, BM25 зависит от корпуса и запроса. Складывать их без нормализации ненадёжно. Reciprocal Rank Fusion использует позиции документов и повышает элементы, которые стоят высоко в нескольких списках.
RRF — хороший baseline, когда нет калиброванных score. Некоторые движки также поддерживают нормализованное смешивание с весом между lexical и vector частями. Вес настраивают по eval-набору, а не по одному красивому запросу.
Увеличение числа кандидатов повышает recall, но увеличивает latency и стоимость следующего этапа. Сначала измерьте Recall@k каждого пути и объединения, затем выберите разумный candidate pool.
Зачем reranking
Первичный retrieval должен быстро собрать несколько десятков кандидатов. Более дорогая cross-encoder или late-interaction модель затем оценивает пару «запрос — документ» точнее и переставляет top результатов. Reranker не найдёт документ, которого не было в candidate pool, поэтому retrieval и reranking измеряют отдельно.
Финальный pipeline часто выглядит так: фильтры доступа → dense и sparse retrieval → fusion → дедупликация → reranking → порог «не знаю» → контекст LLM. Каждый этап логирует идентификаторы и score, чтобы ошибку ответа можно было разложить на поиск и генерацию.
Гибридный поиск оправдан, если eval показывает улучшение на реальных вопросах. На маленькой чистой базе один BM25 или vector retriever может быть быстрее и не хуже.
Объединим два списка через RRF
Лексический поиск вернул документы A, B, C, а векторный — B, D, A. Для RRF каждому документу добавляют 1/(k+rank) из каждого списка, где позиция начинается с единицы. Сырые score двух поисков здесь не складываются. Параметр k=60 — выбранная настройка учебного примера, а не размер выдачи.
def rrf(rankings, k=60):
scores = {}
for ranking in rankings:
for rank, doc_id in enumerate(ranking, start=1):
scores[doc_id] = scores.get(doc_id, 0) + 1/(k+rank)
return sorted(scores, key=lambda doc_id: (-scores[doc_id], doc_id))
result = rrf([["A", "B", "C"], ["B", "D", "A"]])
print(result)
assert result[:2] == ["B", "A"]
B поднимается благодаря сильным позициям в обоих списках. C и D присутствуют только в одном. Если нужного документа не было ни в одном списке кандидатов, fusion его не создаст. Проверьте полноту объединения до дорогого reranker и сравните задержку при разных размерах candidate pool.
Продолжите разбор: Метрики качества RAG: Recall, MRR и Faithfulness.
Задача 1
Почему нельзя бездумно складывать сырой cosine score и BM25 score?
Продолжить с сохранением прогресса
Перейдите к уроку в учебном кабинете.
