/
Вопросы и ответы
/
RAG
/

Как реализовать re-ranking в RAG

Как реализовать re-ranking в RAG

11 часов назад

Никита Вихров

Ответы

0

Как реализовать re-ranking в RAG

Векторный поиск быстрый, но грубый. Он находит документы, похожие по смыслу — но не всегда самые релевантные. Re-ranking — это второй проход: берём топ-20 кандидатов от поиска и пересортируем их более точной моделью.

Результат: в контекст попадают действительно лучшие документы, а не просто «похожие».


Почему векторный поиск ошибается

Embedding-модели обучены на общих данных. Они хорошо улавливают тему, но плохо понимают тонкие различия внутри неё.

question = "Как откатить миграцию базы данных?"

# Векторный поиск может вернуть в таком порядке:
# 1. "Как создать миграцию" — score 0.82 (похоже по теме)
# 2. "Как откатить транзакцию" — score 0.79 (похоже по слову "откатить")
# 3. "Как откатить миграцию через alembic downgrade" — score 0.76 (правильный ответ!)

# Re-ranker понимает смысл глубже и правильно ставит #3 на первое место

Cross-encoder как re-ranker

Cross-encoder — модель, которая принимает пару (вопрос, документ) и выдаёт оценку релевантности. Работает медленнее embedding-поиска, но точнее.

from sentence_transformers import CrossEncoder
from sentence_transformers import SentenceTransformer
import numpy as np

# Быстрый bi-encoder для первичного поиска
bi_encoder = SentenceTransformer("all-MiniLM-L6-v2")

# Точный cross-encoder для re-ranking
cross_encoder = CrossEncoder("cross-encoder/ms-marco-MiniLM-L-6-v2")


def search_and_rerank(question: str, documents: list, top_k: int = 3) -> list:
    # Шаг 1: быстрый поиск — берём широко, топ-20
    question_emb = bi_encoder.encode([question])[0]
    doc_embeddings = bi_encoder.encode([d["content"] for d in documents])

    similarities = [
        np.dot(question_emb, doc_emb) / (np.linalg.norm(question_emb) * np.linalg.norm(doc_emb))
        for doc_emb in doc_embeddings
    ]

    # Топ-20 кандидатов
    top_20_indices = np.argsort(similarities)[::-1][:20]
    candidates = [documents[i] for i in top_20_indices]

    # Шаг 2: re-ranking — оцениваем каждую пару (вопрос, документ)
    pairs = [[question, doc["content"]] for doc in candidates]
    rerank_scores = cross_encoder.predict(pairs)

    # Сортируем по оценке cross-encoder
    ranked = sorted(
        zip(candidates, rerank_scores),
        key=lambda x: x[1],
        reverse=True
    )

    return [doc for doc, score in ranked[:top_k]]

Re-ranking через LLM

Если не хочется ставить отдельную модель — можно использовать LLM как re-ranker:

from anthropic import Anthropic

client = Anthropic()

def llm_rerank(question: str, candidates: list, top_k: int = 3) -> list:
    docs_text = "\n\n".join([
        f"[{i+1}] {doc['content']}"
        for i, doc in enumerate(candidates)
    ])

    response = client.messages.create(
        model="claude-opus-4-5",
        max_tokens=256,
        messages=[{
            "role": "user",
            "content": f"""Вопрос: {question}

Документы:
{docs_text}

Выбери {top_k} наиболее релевантных документа для ответа на вопрос.
Отвечай только номерами через запятую, например: 3, 1, 5
Порядок важен: сначала самый релевантный."""
        }]
    )

    indices_str = response.content[0].text.strip()
    indices = [int(x.strip()) - 1 for x in indices_str.split(",")]
    return [candidates[i] for i in indices if i < len(candidates)]

Когда re-ranking даёт ощутимый прирост

  • Документы по одной теме с похожими формулировками — bi-encoder их путает, cross-encoder различает
  • Длинные документы — embedding усредняет весь текст, важная часть «тонет»
  • Специализированная терминология — общие embedding-модели плохо её понимают

Когда можно обойтись без re-ranking

  • Документов мало и они чётко разграничены по темам
  • Поиск и так даёт хорошие результаты (проверь логами)
  • Latency критична — re-ranking добавляет 100–500ms

Практически все эти техники разбираются на курсе «ИИ для разработчиков» на Хекслете — не в теории, а на реальном проекте с живым кодом. Автор курса Кирилл Мокевнин разбирает как именно выстроить pipeline от поиска до генерации в продакшн-условиях.

11 часов назад

Никита Вихров

+7 800 100 22 47

бесплатно по РФ

+7 495 085 21 62

бесплатно по Москве

108813 г. Москва, вн.тер.г. поселение Московский,
г. Московский, ул. Солнечная, д. 3А, стр. 1, помещ. 20Б/3
ОГРН 1217300010476
ИНН 7325174845