Read in:
Русский

Поиск trip2g против qmd и obsidian-hybrid-search

Что сделали: собрали двуязычный набор из 236 запросов к справке Obsidian, прогнали на нём trip2g, qmd и obsidian-hybrid-search и посчитали всё одним скриптом — цифрам из чужих README не верили. Нашли две причины, по которым trip2g проигрывал на русском. Добавили поиск по алиасам: в своей категории запросов он поднял nDCG@5 с 0,81 до 0,98. Самый большой выигрыш дали поля, которые передаёт агент по MCP: на русском основном наборе плюс 0,10, на остальных около 0,05. Читать, если у вас поиск по заметкам и вы хотите знать, как он выглядит рядом с соседями.

После статьи Как ищут другие остался вопрос, на который разбор кода не отвечает: а какой из этих поисков лучше? В README obsidian-hybrid-search есть сравнение с qmd: 0,733 против 0,659 по nDCG@5. Но это цифры автора, на его машине, и результатов прогона qmd в репозитории нет. Мы решили померить сами.

Набор

Автор obsidian-hybrid-search собрал хороший набор для проверки: 58 запросов к справке Obsidian, написанных вручную, с правильными и частично правильными ответами. Справка — это ровно наш случай: обычные заметки с frontmatter, вики-ссылками и алиасами.

В репозитории справки лежат её переводы на тридцать с лишним языков, в том числе русский: те же 176 страниц. У английской и русской страницы общий permalink, поэтому правильные ответы переносятся на русский без ручной разметки. Запросы мы перевели так, как их набрал бы человек, а не дословно.

Ещё 120 запросов написал отдельный агент: он прошёл по справке и собрал случаи, на которых поиск обычно спотыкается.

  • запрос ровно совпадает с алиасом заметки;
  • опечатки: «backlnks», «обратные ссылкы»;
  • русская морфология: «как синхронизировать» против страницы «Синхронизация»;
  • перефразировки без общих слов со страницей;
  • вопрос на одном языке, ответ на другом;
  • ответ спрятан глубоко в длинной странице;
  • синтаксис: ==выделение==, $$, [[заметка#^блок]].

Каждый ответ он сверил с текстом страницы. Итого четыре набора: английский и русский основные по 58 запросов, английский и русский хитрые — 57 и 63.

Как выглядят хитрые запросы

Несколько примеров из прогона. В ячейках — на каком месте каждая система выдала нужную страницу; «—» значит, что в первой двадцатке её нет. Первые три запроса — к английской справке, остальные — к русской. Колонка «trip2g» — уже с поиском по алиасам.

Запрос В чём подвох Нужная страница obsidian-hybrid-search qmd + Qwen3 trip2g trip2g + поля агента
Insider builds это алиас страницы, а слово «Insider» есть на соседней странице про лицензию Early access versions 1 1 9 → 1 с алиасами 2
starred старое имя плагина, осталось только в алиасе Plugins/Starred Bookmarks 1 3 — 4
{{title}} эта переменная есть в трёх разных системах шаблонов Templates 15 2 15 2
синхранизация через айклауд опечатка и iCloud кириллицей Синхронизация заметок между устройствами 2 5 1 1
на телефоне и на ноутбуке разные версии одной и той же заметки ни слова «синхронизация», ни слова «устройства» Синхронизация заметок между устройствами 14 10 3 3
how long is version history kept вопрос по-английски, база русская Часто задаваемые вопросы (Sync) 10 1 1 1
$$ два символа и ни одного слова Расширенный синтаксис форматирования — 12 1 2
работает ли Obsidian без интернета отдельной страницы нет, ответ в подразделе Локальные и удалённые хранилища — — 2 5
вернуть старую версию заметки без подписки на Sync всё решает «без подписки»: рядом есть платная «История версий» Восстановление файлов 9 10 — 3

Хорошо видно, где trip2g слаб. «starred» он не находит вовсе: алиас записан как Plugins/Starred, а наш поиск по алиасам требует точного совпадения, тогда как obsidian-hybrid-search прощает разницу нечётким поиском. На вопросе «без подписки на Sync» trip2g уверенно выдаёт платную «Историю версий» — ту самую страницу, которую запрос исключает.

Как мерили

  • trip2g — наш гибридный поиск: полнотекстовый поиск bleve с русским и английским стеммингом, векторный поиск на bge-m3, слияние через RRF.
  • obsidian-hybrid-search 0.15.2 — SQLite, FTS5, векторы multilingual-e5-small, поиск по алиасам и триграммам.
  • qmd 2.8.3 — со своим расширением запроса и реранкером. На нашем сервере без видеокарты один запрос шёл от трёх до семи минут, поэтому qmd гоняли на MacBook с M3 Max и Metal. Два варианта: по умолчанию и с Qwen3-Embedding вместо английской embeddinggemma — это единственная настройка, которая нужна qmd для русского.

Все прогоны посчитаны одним скриптом с формулами из самого obsidian-hybrid-search: nDCG с оценкой 1 за правильный ответ и 0,5 за частично правильный.

Результаты

nDCG@5, чем больше, тем лучше:

Набор obsidian-hybrid-search qmd qmd + Qwen3-Embedding trip2g trip2g + алиасы trip2g + поля агента
английский основной 0,726 0,717 0,731 0,742 0,742 0,798
английский хитрый 0,722 0,733 0,764 0,767 0,785 0,815
русский основной 0,671 0,608 0,668 0,603 0,603 0,702
русский хитрый 0,684 0,670 0,771 0,815 0,815 0,866

Что выяснилось

Цифры из README разошлись с нашими. obsidian-hybrid-search воспроизвёл себя почти точно: 0,726 против заявленных 0,733. А qmd оказался заметно лучше, чем в том сравнении: 0,717 вместо 0,659. Разрыв в 0,07 превратился в 0,01. Мы не знаем, отчего так вышло: другая версия qmd, другая машина или другой режим. Поэтому и не стоит сравнивать по чужим таблицам.

На русском основном наборе trip2g проигрывал всем. Причин две, и обе видно по конкретным запросам.

  1. Полнотекстовый поиск требует, чтобы в тексте встретились все слова запроса. Запрос «свойства заметки во frontmatter» не находит в полнотекстовом поиске ничего: на русской странице этих четырёх слов в таких формах вместе нет. Остаётся один векторный поиск. obsidian-hybrid-search соединяет слова через OR и ищет их по префиксу, поэтому эту проблему не знает.
  2. Длинные страницы побеждают по лучшему куску. Заметка в векторном поиске получает оценку своего лучшего куска. У страницы про Obsidian CLI 163 куска, и хоть один из них оказывается близок почти к любому запросу. Эта страница и справка по Obsidian URI вылезали наверх в запросах, к которым не имели отношения.

Алиасы помогли точечно. trip2g раньше вообще не читал поле aliases из frontmatter. Мы добавили отдельный поиск по точному совпадению с названием или алиасом заметки, как у obsidian-hybrid-search, с весом 2 в RRF. В категории «запрос совпадает с алиасом» результат вырос с 0,813 до 0,980, в целом по английскому хитрому набору — с 0,767 до 0,785. За этим стоят два запроса, которые без алиасов не находились. На остальных наборах ничего не изменилось: ни плюса, ни минуса. На русском плюса нет, потому что русские алиасы в справке — это английские названия страниц, а такие запросы trip2g находил и так.

💡Главная находка: расширение запроса отдаём агенту

qmd переписывает запрос в три варианта своей моделью на 1,7 миллиарда параметров, прямо на сервере, на каждый запрос. Но у MCP-поиска по ту сторону всегда сидит агент — Claude, GPT или другая модель, во много раз больше. Он и так читает вопрос, знает контекст и язык. Если попросить его прислать эти три варианта вместе с запросом, решаются сразу две задачи: trip2g не нужно тащить в себя генеративную модель и считать её на каждый запрос, а варианты пишет модель намного сильнее. На нашем наборе это дало самый большой прирост из всего, что мы пробовали, — при сильном агенте. Со слабым (gpt-5.4-mini) прирост почти пропадает.

Главный выигрыш дали поля агента. Если агент, подключённый по MCP, передаёт вместе с запросом три необязательных поля — короткий запрос из двух-трёх слов, перефразировку и предложение, которое могло бы стоять в ответе, — trip2g выходит на первое место на всех четырёх наборах. На русском основном прирост десять пунктов, и это уверенно выше шума; на остальных наборах прирост около пяти пунктов, и на английском хитром он в пределах шума. Подробности, в том числе как мы подбирали имена полей, — в статье Расширение запроса можно отдать агенту.

qmd на русском без настройки проседает, а с одной настройкой выправляется. По умолчанию у qmd английская модель эмбеддингов, и на русском основном наборе он даёт 0,608. С Qwen3-Embedding — 0,668, на хитром русском — 0,771 вместо 0,670. Это одна переменная окружения, но после неё всю базу нужно перевекторизовать.

Что нашли по дороге

  • Баг в trip2g. Если во frontmatter стоит пустое description: или description: null, сервер не может собрать заметки, и падает вся пачка синхронизации из ста заметок. В справке Obsidian так оформлены по две страницы на язык. Для бенчмарка мы вычистили эти строки; сам баг будет исправлен отдельно.
  • obsidian-hybrid-search возвращает пути в Unicode-форме NFD: й и ё раскладываются на букву и знак. Если сравнивать пути побайтно, как сделали мы в первом прогоне, его русский результат занижается с 0,671 до 0,447.
  • obsidian-hybrid-search не видит русские теги в тексте: #note находится, #заметка нет. Теги во frontmatter работают.

Оговорки

  • Модели эмбеддингов разные. У trip2g bge-m3 на 568 миллионов параметров, у obsidian-hybrid-search — multilingual-e5-small, у qmd — embeddinggemma на 300 миллионов. Мы сравнивали системы в тех настройках, в которых их ставят, а не модели поиска как таковые.
  • Наборы небольшие. По 57–63 запроса на набор — это разница в пару запросов на каждые три пункта nDCG. Разницу в 0,01–0,02 между системами мы бы не считали значимой. Бутстрап подтверждает уверенно только прирост от полей агента на русском основном наборе.
  • Хитрые запросы и поля агента писали модели. Хитрые запросы проверены по тексту справки, но независимая перепроверка их разметки ещё не закончена. Поля агента писал отдельный агент, которому дали только вопросы и описания полей; с gpt-5.4-mini в роли агента прирост колеблется от минус пяти до плюс пяти пунктов.

Как повторить

Скрипты, наборы запросов и сырые результаты прогонов лежат в репозитории: scripts/obsidian-help-bench/, testdata/eval/obsidian-help/, docs/superpowers/eval-runs/obsidian-help/. Сама справка Obsidian не лицензирована для распространения, поэтому скрипт скачивает её сам, на том же коммите, по которому размечались ответы.