Русский | 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 (медленнее настроенного таймаута). Обе возвращают ключевой порядок.
Закрытые ворота не стоят ничего. Окно кандидатов остаётся размером со страницу, провайдера никто не трогает, результат байт в байт совпадает с чекаутом без этого модуля.
Конфигурация
{
"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. С этой машины отвечают две команды:
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.
См. также
- Хранилище знаний — что такое общее хранилище и как туда попадают строки.
- Что НЕ гарантировано.