# Мультимодальний пошук зі злиттям: обираємо правильний ретрівер для кожного запиту

Canonical source: https://maguyva.ai/uk/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/uk/docs/how-it-works).

Пошуковий запит — це не одна річ.

«де визначено `parseConfig`» хоче точний символ — одне точне місце, швидко. «як працює автентифікація» хоче сенс — розкидані пов'язані фрагменти коду, що пояснюють концепцію. «що зламається, якщо я зміню цю функцію» хоче граф залежностей. «знайди рядок `ECONNREFUSED`» хоче буквальний збіг, нічого хитрого.

Grep чудовий для буквальних збігів і корисний для деяких пошуків посилань, але це не граф залежностей, і він не розуміє сенсу. Ембеддинги покривають семантичний бік, але вони — неправильний інструмент для точних рядків та аналізу впливу. Більшість інструментів пошуку коду обирають один рушій і змушують кожен запит жити з цим вибором. Maguyva не обирає. Він з'ясовує, яке саме запитання ви поставили, а потім змішує чотири ретрівери в пропорції, якої заслуговує це запитання.

## Чотири режими

Під капотом є чотири незалежні способи знайти код:

- **semantic** — векторний пошук за бінарними ембеддингами Voyage; знаходить код за сенсом.
- **text** — триграмне зіставлення; знаходить літерали, рядки помилок, точні ідентифікатори.
- **structural** — запити AST; знаходить визначення, сигнатури та мовні конструкції.
- **graph** — граф залежностей; знаходить викликачів, викликаних та радіус впливу.

Кожен сильний у своєму класі запитань. Хитрість у тому, щоб вирішити, наскільки довіряти кожному для конкретного запиту перед вами.

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

Перш ніж запуститься будь-який пошук, легкий класифікатор сортує запит в один з шести намірів, з оцінкою впевненості. Він навмисно дешевий — упорядковані евристики, перший збіг перемагає — бо працює на гарячому шляху й додає лише мілісекунду-дві:

- починається з `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)

Кожен намір несе профіль ваг серед чотирьох режимів. Ось реальні цифри:

| Намір | semantic | text | structural (AST) | graph |
|---|---|---|---|---|
| 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). «що залежить від цього» майже повністю graph (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×, щоб популярна функція не могла повністю поховати релевантнішу, але малопомітну. Насамкінець ми знижуємо ранг результатів зі шляхів vendor, build та archive, і усуваємо дублікати, залишаючи найкращий фрагмент на файл.

## Що досі недосконале

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

## Ставте запитання, а вибір інструмента залиште системі

Агенту не повинно бути потрібно знати, чи тягнутися до grep, чи до ембеддингів, чи до графа викликів — він має ставити своє запитання простими словами й отримувати правильну відповідь. Мультимодальне злиття — це те, що дозволяє `find_symbol`, семантичному пошуку та аналізу залежностей працювати через єдиний інтерфейс запитів: система читає форму запитання й тихо збирає для нього правильний ретрівер. Модель, що оцінює ембеддинги, важлива, але так само важливо знати, коли *не* використовувати їх. Обрати правильний інструмент для кожного запиту — це свій особливий вид якості, і ми радше візьмемо його на себе, ніж перекладемо на того, хто викликає.

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