ПОЧЕМУ ОДНИХ ЭМБЕДДИНГОВ МАЛО
В 2023–2024 канонический RAG выглядел так: порезал документы на чанки, прогнал через эмбеддинг-модель, сложил в векторную БД, на запросе достал топ-k по косинусной близости, засунул в промпт. Работает — пока не начинаешь смотреть, что реально попадает в контекст.
Векторный (dense) поиск отлично ловит смысл, но systematically мажет там, где важна буквальность: артикулы, номера ошибок, имена функций, юридические термины, редкие аббревиатуры. Запрос «ошибка E4102 при деплое» эмбеддинг-модель растворяет в «общем облаке про деплой», и нужный чанк с точным кодом уезжает на 40-е место. Пользователь получает уверенный, но неверный ответ. Это и есть главный источник галлюцинаций — не сама LLM, а мусор на входе.
Отсюда две ортогональные техники, которые в 2026 стали стандартом: гибридный поиск (складываем лексический и семантический сигнал) и reranking (переупорядочиваем кандидатов моделью, которая видит запрос и документ вместе).
BM25 НИКУДА НЕ УШЁЛ
BM25 — это лексический ранжировщик на основе TF-IDF с насыщением частоты и нормализацией по длине документа. Ему почти 30 лет, и он до сих пор бьёт нейросети на точных совпадениях, потому что просто считает пересечение токенов. Ноль обучения, ноль GPU, миллисекунды на запрос.
Слабость BM25 — нулевое понимание синонимов и перефраза. Запрос «как ускорить обучение модели» не найдёт чанк про «оптимизацию тренировки нейросети», если там нет общих слов. Ровно там, где силён dense-поиск.
Поэтому их не противопоставляют, а складывают. Два ретривера, два списка кандидатов, одно объединённое ранжирование. Свежие релизы ретриверов и реранкеров удобно отслеживать в каталоге REDDYX — модели обновляются буквально каждую неделю.
КАК УСТРОЕН ГИБРИДНЫЙ ПОИСК
Гибридный поиск гоняет запрос через оба канала и мёржит результаты. Основной вопрос — как объединять два разных по природе скора: BM25 выдаёт неограниченные положительные числа, косинус — от -1 до 1. Сравнивать их напрямую нельзя.
Reciprocal Rank Fusion (RRF)
Самый устойчивый способ на 2026 — RRF. Он игнорирует абсолютные значения скоров и работает только с позициями в списках. Формула проста:
score(d) = Σ 1 / (k + rank_i(d))
# rank_i — позиция документа d в i-м списке (1-based)
# k — константа сглаживания, обычно 60
RRF не требует калибровки и нормализации, устойчив к выбросам, и именно поэтому его по умолчанию используют Elasticsearch, OpenSearch, Weaviate и Qdrant. Альтернатива — взвешенная сумма нормализованных скоров (min-max или z-score), но она капризна к распределению и требует подбора весов под домен.
RERANKING: ГЛАВНЫЙ РЫЧАГ КАЧЕСТВА
Гибридный поиск даёт хороший список кандидатов, но по-прежнему быстрый и «грубый»: bi-энкодер кодирует запрос и документ отдельно, теряя тонкие взаимодействия между ними. Reranker — это кросс-энкодер: он подаёт пару (запрос, документ) в модель одновременно, и attention видит их вместе. Точность выше на порядок, но и стоимость: считать надо для каждой пары отдельно, поэтому ранкером обрабатывают не всю базу, а только топ-50–100 кандидатов от первого этапа.
Схема двухступенчатая:
- Retrieval (recall-этап): гибридный поиск достаёт 50–100 кандидатов. Задача — не потерять релевантное, точность вторична.
- Rerank (precision-этап): кросс-энкодер переупорядочивает их и оставляет топ-3–8 для контекста LLM.
По замерам сообщества добавление reranking-этапа поверх обычного векторного поиска даёт самый заметный прирост качества среди всех «дешёвых» улучшений RAG — заметно выше, чем смена эмбеддинг-модели или тюнинг размера чанка. Это первое, что стоит внедрять.
РАБОЧИЙ ПРИМЕР НА PYTHON
Минимальный пайплайн: BM25 (rank-bm25) + dense (sentence-transformers) → RRF → кросс-энкодер-reranker. Без внешних сервисов, всё локально.
from rank_bm25 import BM25Okapi
from sentence_transformers import SentenceTransformer, CrossEncoder, util
import numpy as np
docs = [...] # список чанков (строки)
tokenized = [d.lower().split() for d in docs]
# --- Этап 0: индексы ---
bm25 = BM25Okapi(tokenized)
embedder = SentenceTransformer("intfloat/multilingual-e5-large")
doc_emb = embedder.encode(docs, normalize_embeddings=True)
reranker = CrossEncoder("BAAI/bge-reranker-v2-m3")
def rrf(ranked_lists, k=60, top=100):
scores = {}
for lst in ranked_lists:
for rank, doc_id in enumerate(lst, start=1):
scores[doc_id] = scores.get(doc_id, 0) + 1 / (k + rank)
return sorted(scores, key=scores.get, reverse=True)[:top]
def search(query, top_k=5):
# лексический список
bm25_scores = bm25.get_scores(query.lower().split())
bm25_rank = np.argsort(bm25_scores)[::-1][:100]
# семантический список
q_emb = embedder.encode(query, normalize_embeddings=True)
dense_scores = util.dot_score(q_emb, doc_emb)[0].numpy()
dense_rank = np.argsort(dense_scores)[::-1][:100]
# гибрид через RRF
candidates = rrf([list(bm25_rank), list(dense_rank)], top=50)
# reranking кросс-энкодером
pairs = [(query, docs[i]) for i in candidates]
rr = reranker.predict(pairs)
order = np.argsort(rr)[::-1][:top_k]
return [docs[candidates[i]] for i in order]
print(search("ошибка E4102 при деплое"))
Ключевая деталь: для e5-моделей на реальном проде добавляйте префиксы query: и passage: — без них качество эмбеддингов проседает. И держите reranker на топ-50, а не на всей выдаче: это баланс задержки и точности.
ЧТО ВЫБРАТЬ: СРАВНЕНИЕ ПОДХОДОВ
| Подход | Качество топ-k | Латентность | Стоимость | Когда брать |
|---|---|---|---|---|
| Только BM25 | Низкое на перефразе | ~1–5 мс | Копеечная | Код, логи, точные термины, MVP без GPU |
| Только dense | Среднее | ~10–30 мс | Средняя | Семантические запросы, чат-боты FAQ |
| Гибрид (BM25+dense, RRF) | Хорошее | ~15–40 мс | Средняя | Дефолт для большинства RAG |
| Гибрид + reranker | Высокое | +50–300 мс | Выше (GPU/API) | Прод, где важна точность ответа |
| Гибрид + LLM-as-reranker | Максимум | секунды | Дорого | Юридич./медицина, малый трафик |
Практическое правило 2026: почти всем нужен «гибрид + кросс-энкодер-reranker». LLM-реранкинг оставляйте для узких доменов с высокой ценой ошибки и низким QPS.
МЕТРИКИ: КАК ПОНЯТЬ, ЧТО СТАЛО ЛУЧШЕ
«На глаз стало лучше» — не аргумент. Соберите золотой набор из 50–200 пар «запрос → релевантные чанки» и меряйте retrieval отдельно от генерации:
- Recall@k — доля релевантных документов, попавших в топ-k. Главная метрика recall-этапа.
- nDCG@k — учитывает позицию: релевантное на 1-м месте ценнее, чем на 10-м. Главная метрика reranking-этапа.
- MRR — насколько высоко стоит первый релевантный документ.
- Faithfulness / groundedness — уже на уровне LLM: опирается ли ответ на переданный контекст. Считается связкой RAGAS или LLM-судьёй.
Разделение критично: если retrieval-метрики хорошие, а ответы плохие — проблема в промпте или модели, а не в поиске. Не тюньте вслепую всё сразу.
ТИПИЧНЫЕ ГРАБЛИ
- Reranker поверх мусора. Если recall-этап не достал релевантный документ в топ-100, ранкер его не воскресит. Сначала чините recall.
- Слишком мелкие чанки. На чанках в 1–2 предложения теряется контекст; на слишком крупных reranker размывает сигнал. Разумный старт — 256–512 токенов с перекрытием.
- Игнор языка. Для русского берите мультиязычные модели (e5, bge-m3), а BM25 прогоняйте через нормализацию/лемматизацию — иначе падежи убивают лексический матч.
- Реранк всей базы. Кросс-энкодер на 10k документов — это секунды и счёт за GPU. Только топ-50–100.
- Нет офлайн-оценки. Без золотого набора любое «улучшение» — вера, а не инженерия.
Частые вопросы
Что такое reranking в RAG простыми словами?
Reranking — второй этап поиска, где модель-кросс-энкодер заново упорядочивает уже найденных кандидатов, оценивая пару «запрос + документ» вместе. Это точнее обычного векторного поиска и поднимает релевантность топ-документов, которые уходят в контекст LLM.
Гибридный поиск всегда лучше векторного?
Почти всегда. Гибридный поиск складывает лексический сигнал BM25 (точные слова, коды, термины) и семантику эмбеддингов (смысл, синонимы), закрывая слабые места каждого. Проигрывает чистому dense лишь на редких доменах без точных терминов, где такой сценарий встречается редко.
Нужен ли reranking, если у меня хорошая эмбеддинг-модель?
Да. Даже сильный bi-энкодер кодирует запрос и документ раздельно и теряет тонкие связи между ними. Кросс-энкодер-reranker видит их одновременно и обычно даёт больший прирост качества, чем смена эмбеддинг-модели.
Насколько reranking замедляет ответ?
Локальный кросс-энкодер на топ-50 кандидатов добавляет примерно 50–300 мс на GPU; API-реранкеры — сопоставимо плюс сетевой round-trip. Для большинства продовых RAG это приемлемо, потому что прирост точности снижает число повторных запросов пользователя.
Если собираете RAG всерьёз, самое дешёвое улучшение на сегодня — добавить reranker поверх гибрида и начать мерить nDCG. Новые реранкеры, эмбеддинг-модели и retrieval-фреймворки я разбираю в Telegram-канале REDDYX AI — новые репозитории каждые 30-60 минут.