Reranking

Реранкинг — второй этап поиска в RAG: после быстрого отбора кандидатов более тяжёлая модель (cross-encoder) переупорядочивает их по точной релевантности. Метафора — «сужающаяся воронка»: 100 → 20 → 5.

Суть

Векторный поиск (bi-encoder) быстрый, но грубый: он кодирует вопрос и документ независимо и сравнивает косинусом. Cross-encoder подаёт пару «вопрос + документ» в модель вместе, поэтому ранжирует точнее — но медленнее, и его нельзя гонять по всей базе.

Зачем это нужно

Если запустить cross-encoder на всех документах базы, поиск простого запроса может занять ~2 минуты. Поэтому его применяют только к уже отфильтрованному топу: даёт стабильный +5-8% accuracy при контролируемой latency.

Как работает (воронка)

  1. Recall-этап — гибридный поиск (Hybrid Search, BM25 + vector) отбирает топ-50…100 кандидатов.
  2. Precision-этап — cross-encoder переранжирует топ-20.
  3. Финал — топ-5 наиболее релевантных чанков уходят в промпт LLM.
  • Multi-stage Reranking — каскад: быстрый bi-encoder → cross-encoder → (опционально) LLM-фильтр. Каждая ступень дороже и точнее предыдущей.
  • Trade-off: «быстрый, но неточный RAG бесполезен» — реранкинг и есть способ докупить точность за приемлемую задержку (целевые P95 < 3 c, см. RAG Metrics).
  • Реранкинг — самая дешёвая по усилиям техника с стабильным приростом, поэтому входит в Production Baseline 2026 (вместе с Hybrid Search).

Чем реранкать: ландшафт по состоянию на 2026-08

Механика воронки выше не устаревает, конкретные модели — да.

Вариант Когда брать Оговорки
BGE-reranker-v2-m3 дефолт для self-host, самый развёрнутый open-weight Apache 2.0, 50-100 мс на GPU, мультиязычный — рабочий выбор для RU+EN
Qwen3-Reranker максимум качества на open weights верх open-weight лидерборда, ~77% nDCG@10 в среднем по BEIR; коммерция разрешена с ограничениями по масштабу
Cohere Rerank 4 managed без своей инфраструктуры оплата за объём, на масштабе заметно дороже self-host

По чему выбирать, а не по лидерборду: прирост на своём корпусе в RAG-эвале, сквозная задержка на топ-50, лицензия и языковые пары в данных. Средний nDCG@10 по BEIR — агрегат по чужим доменам; на узком корпусе порядок моделей регулярно другой.

  • Multilingual reranker (на момент источников): jina-reranker-v3 — 0.6B, listwise, «last but not late» (causal attention по запросу и всем кандидатам в одном окне, без late-interaction как у ColBERT), BEIR 61.94 nDCG@10; покрывает и русский. ⚠️ Конкретные SOTA-модели быстро устаревают.
  • Top-k как production-эвристика (пример, не универсальный стандарт): top-50 (BM25) → top-20 (vector) → top-5 (после reranker); целевые latency P95 < 3 c, TTFT < 500 мс.

Русскоязычный корпус: чем реранкать и почему сравнения нет

Прямого сравнения реранкеров на русском по-прежнему не опубликовано — проверено 2026-08-22. Это не пробел поиска, а состояние области: русскоязычные разборы называют модели, но не меряют их между собой на русском корпусе. Поэтому выбор делается по косвенным признакам и проверяется на своих данных.

Что называют в практике для русского: BGE-reranker-v2-m3 как мультиязычный self-host по умолчанию (он же дефолт из раздела выше), Cohere rerank-multilingual-v3.0 и Voyage rerank-2 как managed-варианты. Все три мультиязычные, ни один не специализирован под русский.

Отсюда рабочая процедура вместо чужого бенчмарка: взять свой eval-набор со срезами (RAG Metrics), прогнать две-три модели на одном и том же списке кандидатов и сравнить прирост Recall@10 и MRR. Это занимает час и даёт ответ, которого в литературе нет, потому что прирост реранкера сильно зависит от того, насколько плохо отработал первый этап: на хорошем гибридном поиске он меньше, чем на голом векторном.

Пример

Двухступенчатая воронка: bi-encoder быстро отбирает top-10 кандидатов, cross-encoder (принимает пару «вопрос+документ» вместе) точно переранжирует их в top-k.

from sentence_transformers import CrossEncoder
reranker = CrossEncoder("cross-encoder/ms-marco-MiniLM-L-6-v2")

def reranking_retrieve(question, k=3):
    candidates = vectorstore.similarity_search(question, k=10)   # bi-encoder: грубо, top-10
    pairs  = [(question, c.page_content) for c in candidates]
    scores = reranker.predict(pairs)                             # cross-encoder: точно
    ranked = sorted(zip(scores, candidates), key=lambda x: x[0], reverse=True)
    return [d.metadata["doc_id"] for _, d in ranked][:k]

Связано с

  • RAG — реранкинг = precision-этап retrieval
  • Hybrid Search — поставляет кандидатов на реранкинг
  • Embeddings — bi-encoder (быстрый отбор) работает на эмбеддингах