Retrieval-слой под book-as-context: почему vector-only не годится
Задача конкретная: поиск по корпусу книг для AI-агента. Vector-only ломается предсказуемо. Разбор архитектурного решения — взять форк готового инструмента, встроить как MCP-слой, понять механику достаточно глубоко, чтобы знать, где он тоже ломается.
Проблема
Когда строишь book-as-context (агент работает с корпусом книг через MCP), первый инстинкт — взять vector-only RAG. На демо-корпусе работает. На реальных технических книгах ломается предсказуемо:
- точные термины, коды, имена глав теряются — эмбеддинг «размывает» лексическое совпадение;
- запрос-навигация («найди главу про two-phase commit у Таненбаума») и запрос-исследование («что пишут про durability») требуют разной механики;
- свежий раздел, добавленный вчера, проигрывает «усреднённо похожим» из старых заметок.
Методика
Вместо написания retrieval с нуля — форк obsidian-hybrid-search, встроенный как MCP-сервер под агентный слой book-as-context. Три сигнала вместо одного:
- 1. BM25 — лексическое совпадение. Точные термины, коды, имена глав не теряются.
- 2. Семантические эмбеддинги — близость по смыслу. Синонимы, перефразировки, концептуальные связи.
- 3. Fuzzy title / alias — навигация к конкретной заметке по имени.
Режимы под тип запроса:
hybrid (по умолчанию),
semantic,
fulltext,
title.
Слияние через RRF-фьюжн: заметка, высоко ранжированная в любом из переформулированных запросов агента, всплывает наверх.
obsidian-search "event bus durability" --mode hybrid --limit 5
{
"path": "tanenbaum/12-message-delivery.md",
"score": 0.87, // итоговый hybrid (RRF)
"matchedBy": ["semantic", "bm25"],
"scores": { "semantic": 0.81, "bm25": 0.93, "fuzzy_title": null }
}
Здесь bm25 (0.93) > semantic (0.81): точное лексическое совпадение
вытянуло наверх результат, который vector-only поставил бы ниже. Видимость по-компонентных scores —
это не отладочный вывод, а то, на основании чего агент решает, доверять ли результату.
Артефакт
Форк github.com/dobryakov/obsidian-hybrid-search (TypeScript): CLI + MCP-сервер с README. Встроен как retrieval-слой под book-as-context — агентный слой поверх описан в разборе book-as-context.
Где ломается
- Веса слияния — не универсальны. Баланс BM25/semantic зависит от корпуса: на коде и на технической прозе разный — нужно калибровать под свои данные.
- Reranking стоит латентности — оправдан, когда агенту важен точный порядок результатов, иначе лишняя задержка.
- Форк не поддерживается upstream. Любые изменения схемы индекса — на тебе. Это осознанный выбор: понимать инструмент достаточно глубоко, чтобы его поддерживать.
Для кого и почему
Если вы строите AI-assisted систему поверх собственного корпуса — книг, вики, документации — этот разбор про retrieval-слой под неё. Не про то, как написать поиск, а про архитектурное решение: взять форк вместо велосипеда, понять механику достаточно глубоко, встроить как слой. В enterprise intake тот же приём — L2 corpus в clarify gate Jira (#11).
Хотите разобраться с retrieval-слоем под вашу AI-систему?
Архитектурный разбор: где vector-only ломается на вашем корпусе, что взять вместо него и как встроить в агентный workflow.
Написать на почтуДругие разборы
Серия инженерных разборов: реальная задача → методика → работающий артефакт → честный разбор, где он ломается.
К серии →