Технологии

Переранжирование: как поднять качество поиска без смены базы

Rerank — самый дешёвый способ улучшить RAG: он не трогает индекс и не требует переиндексации. Разбираем двухэтапную схему и честно называем цену в задержке ответа.

Типичная картина в плохо работающем RAG: нужный фрагмент находится, но оказывается седьмым, а в промпт уходят первые пять. Индекс в порядке, эмбеддер в порядке, данные на месте — виноват порядок. И это единственная поломка поиска, которую можно починить, не трогая ни хранилище, ни модель-эмбеддер, ни нарезку текста.

Инструмент называется переранжированием.

#Короткий ответ

Переранжирование — это второй проход по уже найденным кандидатам. Первый этап (полнотекстовый поиск, векторный или их слияние) отбирает три-четыре десятка кандидатов быстро и грубо. Второй этап берёт каждую пару «запрос — кандидат» и оценивает релевантность отдельной моделью, которая читает запрос и текст вместе, а не сравнивает два заранее посчитанных вектора.

Разница в этом слове — «вместе». Эмбеддер кодирует документ один раз, ничего не зная о будущем запросе. Cross-encoder видит и то и другое одновременно, поэтому улавливает соответствие, которого не видно при сравнении независимых векторов: отрицания, роли участников, оговорки.

#Как устроен двухэтапный поиск

запрос
  ├─ этап 1: отбор кандидатов (быстро, приблизительно)
  │    полнотекстовый bm25 → 40 штук
  │    векторный KNN      → 40 штук
  │    слияние рангов     → ~60 уникальных
  └─ этап 2: переранжирование (медленно, точно)
       cross-encoder оценивает 60 пар «запрос — текст»
       сортировка по оценке
       → в промпт уходят первые 5–6

Асимметрия этапов — не компромисс, а суть подхода. Первый этап обязан быть дешёвым, потому что просматривает всё хранилище. Второй может быть дорогим, потому что просматривает шесть десятков записей. Заменить первый этап вторым нельзя: cross-encoder по всему архиву — это чтение каждого документа на каждый запрос.

#Что это даёт и чем платит

Что меняется Направление Замечание
Порядок в топе улучшается заметно самый сильный эффект — когда кандидатов много, а мест мало
Полнота выдачи не меняется вовсе чего нет среди кандидатов, того не будет и после
Задержка ответа растёт добавляется на каждый запрос, а не на индексацию
Место на диске не меняется индекс не трогается
Стоимость внедрения одна модель без переиндексации и миграций

Вторая строка — главное ограничение и главное недоразумение. Переранжирование не исправляет плохой отбор: если нужный фрагмент не попал в сорок кандидатов, никакая пересортировка его не вернёт. Прежде чем ставить rerank, проверьте, что нужное вообще находится хоть на каком-то месте, — иначе вы чините не ту поломку. Как отладить сам отбор, разобрано в разборе гибридного поиска и в материале про стратегии чанкинга.

#Цена в задержке — и почему она решающая

Здесь честная часть. Cross-encoder делает столько же проходов, сколько у вас кандидатов: шестьдесят кандидатов — шестьдесят прогонов модели. На процессоре с небольшой многоязычной моделью это заметно на глаз; на слабой машине — заметно неприятно.

Конкретных миллисекунд здесь не будет: они зависят от модели, длины фрагментов, числа кандидатов и вашего железа, и любая цифра из чужого блога вас обманет. Методика замера простая и занимает пять минут:

  1. Замерьте время ответа ассистента без rerank на десяти реальных вопросах, запишите медиану.
  2. Включите rerank с тем же числом кандидатов, повторите те же десять вопросов.
  3. Разница медиан — ваша цена. Соотнесите её с тем, сколько запросов реально выиграли по качеству.

Второй шаг обязательно на тех же вопросах: сравнение с разными наборами не значит ничего.

Практическое правило: переранжирование окупается там, где ответ и так не мгновенный. Если ассистент отвечает секунду, лишние полсекунды — треть времени ожидания. Если ответ идёт восемь секунд, потому что локальная модель генерирует текст, лишние полсекунды не заметит никто. Что именно составляет задержку до первого символа, разобрано в справке про латентность и TTFT.

#Как это сделано в Persona

Переранжирование в продукте есть, и оно выключено по умолчанию — это стоит сказать прямо, потому что «есть функция» и «функция работает у вас» разные вещи.

Механика: после слияния списков функция _rerank в app/memory_vec.py проверяет ключ настройки reranker_enabled, и только при явно выставленной единице подгружает cross-encoder из пакета fastembed (модель Xenova/bge-reranker-base, многоязычная, считается на процессоре). Если пакета нет, ключ не выставлен или модель не загрузилась — список возвращается в исходном порядке, без ошибки и без сообщения.

Три следствия, о которых честно стоит знать:

  • Пакет не входит в зависимости. Как и расширение для векторов, cross-encoder — необязательная часть; отсутствие его не ломает ничего.
  • Отдельного переключателя на странице настроек нет. Ключ выставляется как настройка движка памяти, а не отдельной галочкой.
  • Переранжирование применяется только в векторно-гибридной ветке. В режиме по умолчанию — поиске по ключевым словам — этот шаг не вызывается вовсе.

Логика та же, что и с векторами: необязательная надстройка, которая при отсутствии условий тихо не срабатывает. Плата за это — неочевидность: понять, работает ли rerank, по качеству выдачи нельзя, надо смотреть конфигурацию.

#Чем ещё можно поправить порядок

Cross-encoder — не единственный способ, и не всегда лучший для персональной памяти.

Учёт свежести и важности. Для памяти о человеке недавнее и важное почти всегда предпочтительнее старого и проходного, независимо от текстового сходства. В Persona есть такая пересортировка: она комбинирует убывание по времени с момента последнего обращения, оценку важности записи и релевантность из слияния, нормируя все три составляющие по набору кандидатов. Она тоже опциональна и по умолчанию выключена.

Разнообразие. Жадный проход, который выбирает следующий фрагмент с поправкой на непохожесть на уже выбранные, спасает от выдачи из пяти перефразов одного факта. Стоит он почти ничего — только сравнение текстов между собой.

Фильтр эха. Дешёвый и недооценённый приём: выбросить из кандидатов те записи, которые по сути повторяют сам вопрос, а не отвечают на него. В переписке один и тот же вопрос задают многократно, и без фильтра он вытесняет из выдачи собственно ответ.

Три приёма выше стоят на порядки дешевле cross-encoder и часто дают на персональных данных сопоставимый эффект. Начинать разумно с них.

#Частые вопросы

#Чем cross-encoder отличается от модели-эмбеддера?

Эмбеддер кодирует текст независимо от запроса, поэтому его результат можно посчитать заранее и сохранить. Cross-encoder обрабатывает запрос и документ вместе и потому не кэшируется — он работает только во время запроса. Отсюда и разница в скорости, и разница в точности. Какие эмбеддеры отвечают за первый этап и чем они различаются на русском — в обзоре embed-моделей.

#Сколько кандидатов подавать на переранжирование?

Столько, сколько вы готовы оплатить временем: стоимость линейна по числу кандидатов. Разумный старт — несколько десятков, из которых в промпт уходят единицы. Меньше двадцати обычно не имеет смысла: если правильный ответ и так в первой пятёрке, переранжировать нечего.

#Заменяет ли rerank слияние результатов?

Нет, они на разных этапах. Слияние собирает кандидатов из нескольких источников по рангам — разбор в материале про RRF. Переранжирование упорядочивает уже собранный список по содержанию. Схема «слияние, потом rerank» стандартна.

#Нужен ли GPU?

Для небольших многоязычных cross-encoder — нет, они считаются на процессоре. Но именно на процессоре цена в задержке максимальна, поэтому решение «ставить или нет» стоит принимать после замера на своём железе, а не по факту «модель маленькая».

#Может ли переранжирование ухудшить выдачу?

Может, и это не редкость: модель, обученная преимущественно на английском, на русском тексте способна переставить релевантное вниз. Поэтому замер делается до постоянного включения, а не после.

#Что даёт больше — rerank или улучшение отбора?

Почти всегда улучшение отбора. Переранжирование работает с тем, что уже нашлось, а самая частая поломка — что нужное не находится вовсе. Общая схема слоёв, где видно место каждого этапа, описана в статье о том, как работает память у ИИ.

#Вывод

Ставьте переранжирование, если нужный фрагмент у вас находится, но не попадает в первую пятёрку, и если ответ ассистента и без того идёт секунды. Не ставьте, если проблема в том, что фрагмент не находится вообще — сначала чините отбор. И перед постоянным включением обязательно замерьте оба показателя на одних и тех же десяти вопросах: и прирост попаданий, и добавленную задержку. Без второго числа решение принимается вслепую.

Хотите ИИ, который помнит вас?

Persona держит контекст между чатами и работает на вашем железе — если развернуть её у себя и подключить локальную модель.

Создать аккаунт