Skip to content

Русский | English

Семантический re-rank: опциональный, за воротами и выключенный, пока не окупается ​

Ключевой поиск по общему хранилищу знаний — это FTS5 с полевыми весами BM25F (bm25(fts_memory, 10.0, 1.0, 3.0) — заголовок важнее тегов, теги важнее содержимого). Страница описывает опциональный слой, который может ПЕРЕСОБРАТЬ порядок уже найденного, почему он опционален и какие трое ворот держат его в стороне.

Почему re-rank, а не поиск ​

План этой возможности был «чистые локальные embeddings». Опубликованные данные развернули его:

ДанныеЧто говорят
Онлайн-A/B Cursor, 2025-11-06+12,5% офлайн-точности извлечения, +0,3% по удержанию кода; +2,6% только на базах свыше тысячи файлов
SourcegraphУБРАЛ embeddings в пользу BM25F поверх code-graph
Короткие ключевые запросысемантика обрушивается до nDCG@10 около нуля — а это доминирующая форма запроса агента
CORE-Bench, июнь 2026выигрывает гибрид, ни один метод не доминирует

Поэтому спиной остаётся ключевой путь. Слой умеет только пересобирать порядок кандидатов, которых вернул FTS5: он не может ни добавить строку, которую FTS5 пропустил, ни убрать ту, которую нашёл.

Трое ворот ​

ВоротаЗакрыты, когдаПочему
no-providerнет блока semantic_rerank либо enabled: falseвыключено по умолчанию — это и есть обещание нулевых зависимостей
small-corpusменьше 1000 живых строкниже этого размера эффекта не существует, round-trip купит только задержку
keyword-queryменьше 4 слов либо нет служебного словаровно та форма, на которой семантика проваливается

Ещё две причины появляются в телеметрии уже после запуска слоя: provider-failed (недоступен или отвечает не той формой) и over-budget (медленнее настроенного таймаута). Обе возвращают ключевой порядок.

Закрытые ворота не стоят ничего. Окно кандидатов остаётся размером со страницу, провайдера никто не трогает, результат байт в байт совпадает с чекаутом без этого модуля.

Конфигурация ​

json
{
  "semantic_rerank": {
    "enabled": true,
    "endpoint": "http://127.0.0.1:11434/api/embed",
    "model": "nomic-embed-text",
    "timeout_s": 2.0
  }
}

Точка должна принимать {"model": ..., "input": [...]} и отвечать {"embeddings": [[...]]} на всю пачку. Пачкой намеренно: по запросу на кандидата — это двадцать round-trip внутри двухсекундного бюджета, а так их два.

Как складываются два порядка ​

Reciprocal Rank Fusion, 1/(60 + ранг), сумма по обоим порядкам. Не взвешенная сумма оценок: BM25 и косинус лежат в разных шкалах, а калибровка требует размеченных данных, которых у проекта нет, — выдуманный вес был бы числом без доказательства. Ничьи разрешаются в пользу ключевого порядка, поэтому равная оценка никогда не перетасовывает решение FTS5.

Замер на своём трафике ​

Вендорским бенчмаркам здесь не верят: LoCoMo дискредитирован, baseline без памяти обошёл Mem0 73:68. С этой машины отвечают две команды:

bash
python scripts/semantic_rerank.py --probe     # что говорят ворота про эту базу
python scripts/semantic_rerank.py --report    # что слой реально сделал

Замер на момент написания: в общем хранилище 45 живых строк против порога 1000 — 4,5% от него, — провайдер не настроен. Обе формы запроса встречают закрытые ворота, то есть частота срабатывания здесь нулевая. Это находка, а не пробел: слой существует выключенным, пока база не сделает его оплату осмысленной.

Задержка ключевого пути, 20 поисков по живому хранилищу: медиана 6,3 мс, p95 46,2 мс при бюджете 2 с.

Что не записывается никогда ​

Текст запроса. Строка поиска может нести секрет, и файл телеметрии — последнее место, где об этом хочется узнать. semantic_rerank.jsonl пишет ворота, размер базы, число кандидатов, сменилась ли верхняя страница и миллисекунды — формы и исходы, но не слова. Срок жизни объявлен вместе с остальными сайдкарами в scripts/telemetry_retention.py.

См. также ​