Интеллектуальный поиск (v0.18 BM25 + русская морфология)
Forgeplan поставляется с поисковым движком производственного уровня, который находит артефакты по смыслу, а не только по точному совпадению строк. Версия v0.18.0 заменяет написанный вручную прототип крейтом bm25 (v2.3.2), добавляет стемминг Snowball для русского языка, удаляет шаблонный “шум” из индекса и сокращает пакетное ранжирование с O(N²) до O(N). Это руководство объясняет, как он работает и как использовать его из CLI и MCP.
Для кого это
Заголовок раздела «Для кого это»- Вы написали артефакт несколько месяцев назад и помните только “что-то про аутентификацию”.
- Вы работаете в двуязычной команде (английский + русский) и хотите, чтобы оба запроса находили один и тот же PRD.
- Вы создаете ИИ-агента на базе MCP-сервера Forgeplan и нуждаетесь в детерминированном, быстром извлечении данных.
Производственный движок BM25 (v0.18.0)
Заголовок раздела «Производственный движок BM25 (v0.18.0)»До v0.18 Forgeplan поставлялся с 140-строчной реализацией BM25, написанной вручную. Она работала, но ее было трудно настраивать, у нее не было встроенного стеммера, и она оценивала записи по одной - O(N²) на практике, как только рабочее пространство разрасталось до сотни артефактов. Версия v0.18.0 заменяет этот прототип производственным крейтом bm25 v2.3.2, который приносит четыре конкретных преимущества:
- Корректное взвешивание терминов. Производственный TF-IDF / BM25 с нормализацией по длине вместо нашего наивного подсчета подстрок.
- Пакетный поиск O(N). Один вызов
search_scores()ранжирует весь корпус за один проход. Рабочее пространство со 193 артефактами возвращает результат за 0.23с, что значительно быстрее многосекундных задержек, из-за которыхsearchказался медленным. - Встроенный стек токенизатора. Сегментация Unicode, удаление стоп-слов и (что крайне важно) подключаемые стеммеры - это то, что обеспечивает русскую морфологию в следующем разделе.
- Удаление шаблонного “шума”.
strip_indexing_noise()запускается перед токенизацией и удаляет YAML-фронтматтер, строки{placeholder}, строки|table|и HTML-комментарии из индексируемого текста. Это исправляет PROB-030: в v0.17forgeplan search "auth"находил каждый PRD, у которого в фронтматтере былоauthor:, заполняя результаты ложными срабатываниями. v0.18 индексирует только реальное содержимое тела документа.
Эти четыре изменения внесены в v0.18.0 и покрыты регрессионными тестами в crates/forgeplan-core/src/search/ - см. запись в CHANGELOG.
Как это работает
Заголовок раздела «Как это работает»Интеллектуальный поиск запускает трехэтапный конвейер для каждого запроса:
- Лексический (BM25) - крейт
bm25ранжирует документы по частоте терминов, обратной частоте документов и нормализации по длине. Это поведение по умолчанию, которое покрывает 90% случаев. - Усилители - точное совпадение ID, фильтр по типу, фильтр по статусу и недавность корректируют лексическую оценку перед окончательной сортировкой.
- Расширение графа - результаты опционально обогащаются соседними артефактами через типизированные ссылки (
informs,refines,supersedes), так что попадание в PRD также выводит его RFC и доказательство.
Сверху может быть наложен опциональный семантический этап, основанный на эмбеддингах BGE-M3 - см. раздел Семантический поиск ниже.
Токенизация и стемминг
Заголовок раздела «Токенизация и стемминг»Каждый индексируемый документ и каждый запрос проходят через один и тот же конвейер:
raw text → strip_indexing_noise() // frontmatter, {placeholders}, |tables|, <!-- html --> → whichlang detect // 17 languages, per-document and per-query → lowercase + unicode split → Snowball stem (per detected language) → stop-word filter → BM25 term vectorПоскольку обе стороны используют один и тот же стеммер, "аутентификация" в теле PRD и "аутентификации" в вашем запросе сводятся к одному и тому же корню и совпадают.
Удаление шаблонного “шума”
Заголовок раздела «Удаление шаблонного “шума”»PRD и RFC, созданные из шаблонов Forgeplan, содержат много структурного шаблонного текста, который раньше загрязнял индекс:
- YAML-фронтматтер (
id:,author:,status:…) - Строки-заполнители, такие как
{problem statement}, оставшиеся в заготовках артефактов - Таблицы Markdown (
| FR | Description | Status |) - HTML-комментарии из подсказок шаблонов
До v0.18 запрос auth совпадал со словом author: в каждом блоке фронтматтера. strip_indexing_noise() удаляет эти разделы до того, как они достигнут токенизатора, поэтому лексические оценки отражают только реальное содержимое. Это отслеживается как PROB-030.
Пакетный поиск O(N)
Заголовок раздела «Пакетный поиск O(N)»Реализация v0.17 вызывала .score() для каждой записи, что приводило к поведению O(N²) в рабочих пространствах с сотнями артефактов. v0.18 использует search_scores() из крейта bm25, который ранжирует весь корпус за один проход:
- Рабочее пространство со 193 артефактами: 0.23с от начала до конца
- Больше никаких сообщений “поиск тормозит после 100 PRD”
Многоязычная морфология
Заголовок раздела «Многоязычная морфология»v0.18.0 включает LanguageMode::Detect в токенизаторе BM25: каждый документ и каждый запрос проверяются whichlang, который на лету выбирает правильный Snowball stemmer. Семнадцать языков поддерживаются “из коробки” - английский, русский, немецкий, французский, испанский, итальянский, португальский, голландский, шведский, норвежский, датский, финский, венгерский, румынский, турецкий, арабский и общий запасной вариант.
Поскольку корень является общим для каждой словоформы, все следующие русские запросы возвращают один и тот же PRD:
$ forgeplan search "аутентификация"PRD-019 Auth middleware (matches "аутентификации" via stem "аутентификац")EVID-023 Auth benchmark (matches "аутентификацию")Запрос "аутентификация" (именительный падеж) и сохраненная форма "аутентификации" (родительный / дательный падеж) обе нормализуются до корня аутентификац. Тот же механизм, те же оценки, никакой конфигурации не требуется в смешанном языковом рабочем пространстве.
Русская морфология на практике
Заголовок раздела «Русская морфология на практике»Создайте PRD на русском языке:
forgeplan new prd "Система аутентификации"Затем ищите с любой словоформой:
forgeplan search "аутентификация" # именительный падежforgeplan search "аутентификации" # родительный / дательный падежforgeplan search "аутентификацией" # творительный падежforgeplan search "аутентифицировать" # глагольная формаВсе четыре запроса возвращают один и тот же PRD с одинаковой оценкой, потому что русский Snowball stemmer сводит каждую форму к общему корню. То же самое верно и для английского языка (authenticate, authentication, authenticating все сводятся к authent).
Язык определяется для каждого документа и для каждого запроса с помощью whichlang, поэтому смешанное рабочее пространство с английскими PRD и русскими ADR работает без какой-либо конфигурации.
Использование CLI
Заголовок раздела «Использование CLI»Все примеры предполагают, что вы находитесь внутри рабочего пространства Forgeplan.
1. Простой запрос
Заголовок раздела «1. Простой запрос»forgeplan search "bm25 russian morphology"Выводит 10 лучших результатов с указанием типа, ID, заголовка и оценки.
2. Фильтрация по типу
Заголовок раздела «2. Фильтрация по типу»forgeplan search "auth" --kind prdforgeplan search "auth" --kind rfcforgeplan search "auth" --kind evidenceВозвращаются только артефакты запрошенного типа. Полезно, когда вам нужен RFC, объясняющий, как создается PRD.
3. Фильтрация по статусу
Заголовок раздела «3. Фильтрация по статусу»forgeplan search "tags canonicalization" --status activeforgeplan search "deprecated semantics" --status deprecatedОбъедините с --kind для точного определения области:
forgeplan search "scoring" --kind prd --status active4. Ограничение результатов
Заголовок раздела «4. Ограничение результатов»forgeplan search "LanceDB" --limit 5По умолчанию установлено значение 10. Для скриптов установите --limit 1, чтобы получить единственный лучший результат.
5. Переиндексация после ручных изменений
Заголовок раздела «5. Переиндексация после ручных изменений»Если вы редактировали файлы markdown вне CLI (например, в вашем редакторе), перестройте индекс BM25 один раз:
forgeplan reindexПереиндексация считывает каждый файл в .forgeplan/, повторно запускает strip_indexing_noise() и записывает новую таблицу частот терминов. См. PROB-027 для канонического исправления, которое устранило зависимость от активной папки lance/ во время переиндексации.
Использование MCP
Заголовок раздела «Использование MCP»Сервер MCP предоставляет тот же движок в виде инструмента под названием forgeplan_search. Агенты могут вызывать его через stdio. Пример вызова JSON:
{ "name": "forgeplan_search", "arguments": { "query": "semantic scoring intelligence", "kind": "prd", "status": "active", "limit": 5 }}Форма ответа (сокращено):
{ "hits": [ { "id": "PRD-040", "kind": "prd", "title": "Scoring Intelligence", "status": "active", "score": 12.83, "path": ".forgeplan/prds/prd-040-scoring-intelligence.md" } ], "total": 1, "took_ms": 47}Все флаги CLI (kind, status, limit) доступны в качестве аргументов. По умолчанию, если фильтр не указан, выполняется “поиск всех типов, возврат 10 лучших”.
Семантический поиск (флаг функции)
Заголовок раздела «Семантический поиск (флаг функции)»Для запросов, где лексического совпадения недостаточно (синонимы, перефразирование, кросс-языковое сходство), Forgeplan может накладывать плотные эмбеддинги BGE-M3 поверх BM25. Это опциональная функция, поскольку эмбеддинги добавляют ~400 МБ к бинарному файлу и требуют кэша fastembed при первом запуске.
Соберите из исходников с этой функцией:
cargo install forgeplan-cli --features semantic-searchЗатем выполните поиск - флаг прозрачен; BM25 все еще работает, и семантические результаты объединяются:
forgeplan search "how do I prove a decision is sound"Когда функция отключена, та же команда все равно работает - Forgeplan возвращается к поиску только по лексике с мягким сообщением в логе. Этот корректный откат является причиной того, почему PRD-042 (векторный поиск FPF KB) может поставлять один и тот же бинарный файл как минимальным, так и семантическим пользователям.
Устранение неполадок
Заголовок раздела «Устранение неполадок»Оценка всегда 0 или нет результатов.
Запустите forgeplan reindex. Наиболее частая причина - редактирование markdown вне CLI, из-за чего индекс содержит просроченные частоты терминов.
Запрос на русском языке ничего не находит, но тот же английский термин работает.
Убедитесь, что документ был проиндексирован после v0.18.0 - более ранние версии не имели русского стеммера. Запустите forgeplan reindex один раз после обновления.
Слишком много нерелевантных результатов из полей фронтматтера.
Вы используете бинарный файл до v0.18. strip_indexing_noise() исправляет это. Обновитесь и переиндексируйте.
Поиск медленный в большом рабочем пространстве.
v0.18 имеет сложность O(N) - рабочее пространство со 193 артефактами должно возвращать результат менее чем за секунду. Если вы видите задержку в несколько секунд, создайте PROB и приложите вывод forgeplan health плюс количество файлов в .forgeplan/.
Я удалил файл, но он все еще отображается в результатах. Переиндексируйте. Хранилище BM25 является производным состоянием; удаления необходимо воспроизвести.
Связанные артефакты
Заголовок раздела «Связанные артефакты»- PRD-039 - Smart Search v2 (оригинальный дизайн v0.17)
- PRD-040 - Scoring Intelligence (сигналы ранжирования, поступающие в поиск)
- PRD-042 - FPF KB vector search (флаг функции семантического поиска)
- PROB-026 - Tag canonicalization (нормализация на стороне запроса)
- PROB-027 - Reindex without
lance/folder - PROB-030 -
authprefix false positives from frontmatter - CHANGELOG v0.18.0 - примечания к выпуску производственного BM25 + русской морфологии