# Мультимодальный фьюжн-поиск: выбор правильного ретривера для каждого запроса

Canonical source: https://maguyva.ai/ru/blog/multi-modal-fusion-search

License: text under [CC BY 4.0](https://creativecommons.org/licenses/by/4.0/); code samples under [MIT-0](https://opensource.org/license/mit-0); the Maguyva name, logos, brand assets and other code are not covered. Details: [maguyva.ai/license](https://maguyva.ai/license).

Date: 2026-04-15 · Author: Maguyva Team

> Запрос вроде «где определён parseConfig» требует другого поиска, чем «как работает аутентификация». Maguyva классифицирует намерение, соответствующим образом взвешивает четыре модальности поиска и сливает результаты с помощью взвешенного Reciprocal Rank Fusion.

> Примечание об историческом устройстве системы: эта статья от апреля 2026 года описывает устройство маршрутизации на тот момент, а не текущее поведение по умолчанию. Сентябрьское исправление ниже касается только утверждения об измерениях. О текущей маршрутизации, предустановках и явном управлении модальностями см. [Как это работает](https://maguyva.ai/ru/docs/how-it-works).

Поисковые запросы бывают разными.

«где определён `parseConfig`» хочет точный символ — одно точное место, быстро. «как работает аутентификация» хочет смысл — набор связанного кода, который объясняет концепцию. «что сломается, если я изменю эту функцию» хочет граф зависимостей. «найти строку `ECONNREFUSED`» хочет буквальное совпадение, ничего умного.

Grep отлично справляется с буквальными совпадениями и полезен для некоторых поисков ссылок, но это не граф зависимостей, и он не понимает смысл. Эмбеддинги покрывают семантическую сторону, но это неверный инструмент для точных строк и анализа влияния. Большинство инструментов поиска по коду выбирают один движок и заставляют каждый запрос жить с этим выбором. Maguyva не выбирает. Он выясняет, какого рода вопрос вы задали, а затем смешивает четыре ретривера в той пропорции, которую заслуживает этот вопрос.

## Четыре модальности

Под капотом есть четыре независимых способа найти код:

- **семантический** — векторный поиск по бинарным эмбеддингам Voyage; находит код по смыслу.
- **текстовый** — триграммное сопоставление; находит литералы, строки ошибок, точные идентификаторы.
- **структурный** — запросы к AST; находит определения, сигнатуры и языковые конструкции.
- **графовый** — граф зависимостей; находит вызывающих, вызываемых и радиус влияния.

Каждый силён в своём классе вопросов. Хитрость в том, чтобы решить, насколько доверять каждому из них для конкретного запроса перед вами.

## Классификация намерения

Прежде чем запускается любое извлечение, лёгкий классификатор относит запрос к одному из шести типов намерения, с оценкой уверенности. Он намеренно дешёвый — упорядоченные эвристики, побеждает первое совпадение — потому что он работает на горячем пути и добавляет всего миллисекунду-другую:

- начинается с `def `, `class `, `func `, `import `… → **find_definition** (уверенность 0.95)
- "who calls" («кто вызывает»), "usages of" («использования»), "references to" («ссылки на») → **find_references** (0.90)
- "impact" («влияние»), "blast radius" («радиус влияния»), "what depends on" («что зависит от») → **impact_analysis** (0.90)
- строка в кавычках `"string"` или токен ошибки вроде `traceback` → **exact_match** (0.85–0.90)
- идентификатор `CamelCase` или `snake_case` → **find_definition** (0.60–0.80)
- "how" («как»), "why" («почему»), "explain" («объясни»), "architecture" («архитектура») → **understand_code** (0.75)
- ничего не совпало → **understand_code**, низкая уверенность (0.40)

Каждое намерение несёт профиль весов по четырём модальностям. Вот реальные цифры:

| Намерение | семантический | текстовый | структурный (AST) | графовый |
|---|---|---|---|---|
| find_definition | 0.2 | 0.1 | **0.6** | 0.1 |
| find_references | 0.1 | 0.2 | 0.2 | **0.5** |
| understand_code | **0.5** | 0.2 | 0.2 | 0.1 |
| find_similar | **0.4** | 0.3 | 0.2 | 0.1 |
| impact_analysis | 0.1 | 0.1 | 0.1 | **0.7** |
| exact_match | 0.0 | **0.9** | 0.1 | 0.0 |

Так что «где определён `parseConfig`» сильно опирается на AST (0.6). «как работает аутентификация» опирается на семантические векторы (0.5). «что зависит от этого» — почти целиком граф (0.7). «найти `ECONNREFUSED`» — почти целиком триграммы (0.9), при этом модель эмбеддингов полностью отключена — потому что семантическое сходство здесь ровно неверный инструмент для точной строки.

## Быстрый путь и путь с объединением результатов

Когда классификатор уверен — оценка ≥ 0.85 — и запрос обычный, Maguyva полностью пропускает слияние и направляет запрос прямо в единственную доминирующую модальность. «где определён X» не нужны четыре ретривера; ему нужен AST-индекс, прямо сейчас. Этот прямой путь возвращается как `fusion_strategy: "direct"`.

Всё неоднозначное проходит через слияние. Четыре (или три, в дефолтном пресете) модальности запускаются параллельно, каждая возвращает свой собственный ранжированный список, и мы их объединяем.

## Взвешенное Reciprocal Rank Fusion

Слияние разнородных ретриверов сложнее, чем кажется: косинусное сходство 0.82, триграммная оценка 137 и графовая центральность 0.004 находятся не в одной шкале, так что их нельзя просто сложить. Reciprocal Rank Fusion обходит проблему, отбрасывая сырые оценки и оставляя только **ранг**, присвоенный каждым движком. Вклад результата от одной модальности равен:

```text
contribution = weight × 1 / (k + rank + 1)
```

где `rank` — его позиция в списке этой модальности, а `k` — константа сглаживания. Вклады суммируются по модальностям для любого результата, который нашёл больше чем один движок — согласие между ретриверами естественным образом всплывает наверх. Мы используем **k = 40** на дефолтном пресете и 60 на `thorough` (`quick` работает только в семантическом режиме, так что слияние там никогда не срабатывает). Оригинальная работа по RRF остановилась на k = 60 для поиска общего назначения; мы по умолчанию сильнее выделяем верхние позиции, что даёт немного больше веса результатам, высоко ранжированным несколькими модальностями — и мы не рекомендуем настраивать его вручную.

Поверх этого результаты несут **буст по графовой значимости**. Хаб — функция, на которую опирается вся кодовая база, — должен обгонять малоизвестный листовой узел даже при той же текстовой релевантности, поэтому мы умножаем каждый вклад на:

```text
boost = min(1 + 0.3 × ln(1 + centrality), 1.5)
```

Центральность берётся из предвычисленных метрик PageRank/степени вершины конвейера, а буст ограничен сверху 1.5×, чтобы популярная функция не могла полностью похоронить более релевантную, но малоизвестную. Наконец, мы понижаем результаты из путей вендоров, сборки и архивов и убираем дубликаты, оставляя лучший фрагмент на файл.

## Что всё ещё несовершенно

Классификатор намерений — это стек регулярных выражений, а не обученная модель. Он обрабатывает распространённые формы запросов с помощью эвристик, а неоднозначный запрос переходит к `understand_code` и смеси с преобладанием семантики. В прежней версии этой статьи снижение доли запросов без результатов примерно с 15% до менее чем 5% было описано как достигнутое. На самом деле в предложении от декабря 2025 года были указаны оценка исходного уровня и целевое значение; подтверждающий набор запросов или протокол измерений не предоставлен. Мы отзываем это утверждение о достигнутом улучшении; см. [исходные подтверждающие материалы и исправление (на английском)](https://maguyva.ai/evidence/search-quality.md). Мы не заменили классификатор обученной моделью, потому что дешёвая версия достаточно быстра и пригодна для своей задачи маршрутизации, а неверный, но уверенный классификатор хуже честного запасного варианта. Сами веса — это подобранные вручную априорные значения, а не значения, обученные на данных о кликах, которые мы не собираем.

## Задавайте вопрос, а не выбирайте инструмент

Агенту не должно быть нужно знать, тянуться ли ему к grep, эмбеддингам или графу вызовов — он должен задать свой вопрос простыми словами и получить правильный ответ. Именно мультимодальное слияние позволяет `find_symbol`, семантическому поиску и анализу зависимостей находиться за единым интерфейсом запросов: система считывает форму вопроса и тихо собирает под неё правильный ретривер. Модель, которая оценивает эмбеддинги, важна, но не менее важно знать, когда их *не* использовать. Выбор правильного инструмента под каждый запрос — это отдельный вид качества, и мы предпочитаем владеть им сами, а не перекладывать на вызывающую сторону.

`// you bring the question. it brings the tools.`
