# Wielomodalne wyszukiwanie z fuzją: dobór właściwego retrievera do każdego zapytania

Canonical source: https://maguyva.ai/pl/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

> Zapytanie w stylu „gdzie zdefiniowano parseConfig” potrzebuje innego wyszukiwania niż „jak działa uwierzytelnianie”. Maguyva klasyfikuje intencję, odpowiednio waży cztery tryby wyszukiwania i łączy wyniki za pomocą ważonej Reciprocal Rank Fusion.

> Uwaga o historycznym projekcie: ten artykuł z kwietnia 2026 r. opisuje ówczesny projekt routingu, a nie obecne zachowanie domyślne. Poniższa korekta z września dotyczy wyłącznie twierdzenia pomiarowego. Aktualny routing, presety i jawne sterowanie modalnościami opisano na stronie [Jak to działa](https://maguyva.ai/pl/docs/how-it-works).

Zapytanie wyszukiwania to nie jedna rzecz.

„gdzie zdefiniowano `parseConfig`” chce dokładnego symbolu — jednej precyzyjnej lokalizacji, szybko. „jak działa uwierzytelnianie” chce znaczenia — rozproszenia powiązanego kodu, które wyjaśnia koncept. „co się zepsuje, jeśli zmienię tę funkcję” chce grafu zależności. „znajdź ciąg `ECONNREFUSED`” chce dosłownego dopasowania, niczego sprytnego.

Grep jest świetny do dosłownych dopasowań i przydatny do części poszukiwań referencji, ale to nie graf zależności i nie rozumie znaczenia. Embeddingi pokrywają stronę semantyczną, ale są niewłaściwym narzędziem do dokładnych ciągów i analizy wpływu. Większość narzędzi do wyszukiwania kodu wybiera jeden silnik i zmusza każde zapytanie, by żyło z tym wyborem. Maguyva nie wybiera. Ustala, jakiego rodzaju pytanie zadałeś, a następnie miesza cztery retrievery w proporcji, na jaką to pytanie zasługuje.

## Cztery tryby

Pod maską istnieją cztery niezależne sposoby na znalezienie kodu:

- **semantyczny** — wyszukiwanie wektorowe na binarnych embeddingach Voyage; znajduje kod według znaczenia.
- **tekstowy** — dopasowywanie trigramowe; znajduje literały, ciągi błędów, dokładne identyfikatory.
- **strukturalny** — zapytania AST; znajduje definicje, sygnatury i konstrukty językowe.
- **grafowy** — graf zależności; znajduje wywołujących, wywoływanych i zasięg wpływu.

Każdy jest silny w innej klasie pytań. Sztuka polega na tym, by zdecydować, jak bardzo ufać każdemu z nich dla zapytania, które masz przed sobą.

## Klasyfikacja intencji

Zanim uruchomi się jakiekolwiek wyszukiwanie, lekki klasyfikator sortuje zapytanie do jednej z sześciu intencji, z wynikiem pewności. Jest celowo tani — uporządkowane heurystyki, pierwsze dopasowanie wygrywa — ponieważ działa na ścieżce krytycznej i dodaje tylko milisekundę lub dwie:

- zaczyna się od `def `, `class `, `func `, `import `… → **find_definition** (pewność 0,95)
- „who calls”, „usages of”, „references to” (pytania o wywołania, użycia i referencje) → **find_references** (0,90)
- „impact”, „blast radius”, „what depends on” (wpływ, zasięg wpływu, co od tego zależy) → **impact_analysis** (0,90)
- cytowany `"string"` lub token błędu jak `traceback` → **exact_match** (0,85–0,90)
- identyfikator `CamelCase` lub `snake_case` → **find_definition** (0,60–0,80)
- „how”, „why”, „explain”, „architecture” (pytania o sposób działania, przyczyny i architekturę) → **understand_code** (0,75)
- nic nie pasuje → **understand_code**, niska pewność (0,40)

Każda intencja niesie profil wag rozłożony na cztery tryby. Oto prawdziwe liczby:

| Intencja | semantyczny | tekstowy | strukturalny (AST) | grafowy |
|---|---|---|---|---|
| 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 |

Więc „gdzie zdefiniowano `parseConfig`” mocno opiera się na AST (0,6). „jak działa uwierzytelnianie” opiera się na wektorach semantycznych (0,5). „co od tego zależy” to niemal wyłącznie graf (0,7). „znajdź `ECONNREFUSED`” to niemal wyłącznie trigram (0,9), z modelem embeddingów całkowicie wyłączonym — ponieważ podobieństwo semantyczne jest dokładnie niewłaściwym narzędziem dla dokładnego ciągu.

## Szybka ścieżka i ścieżka fuzji

Gdy klasyfikator jest pewny — wynik ≥ 0,85 — a zapytanie jest zwyczajne, Maguyva całkowicie pomija fuzję i kieruje bezpośrednio do jednego dominującego trybu. „gdzie zdefiniowano X” nie potrzebuje czterech retrieverów; potrzebuje indeksu AST, natychmiast. Ta bezpośrednia ścieżka jest raportowana z powrotem jako `fusion_strategy: "direct"`.

Wszystko niejednoznaczne przechodzi przez fuzję. Cztery (lub trzy, w domyślnym presecie) tryby uruchamiają się równolegle, każdy zwracając własną uszeregowaną listę, a my je łączymy.

## Ważona Reciprocal Rank Fusion

Fuzja niejednorodnych retrieverów jest trudniejsza, niż się wydaje: podobieństwo kosinusowe 0,82, wynik trigramowy 137 i centralność grafu 0,004 nie są na tej samej skali, więc nie można ich po prostu dodać. Reciprocal Rank Fusion omija ten problem, odrzucając surowe wyniki i zachowując tylko **rangę**, jaką przypisał każdy silnik. Wkład wyniku z jednego trybu to:

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

gdzie `rank` to jego pozycja na liście danego trybu, a `k` to stała wygładzająca. Wkłady są sumowane po trybach dla każdego wyniku, który znalazł więcej niż jeden silnik — zgodność między retrieverami naturalnie unosi się na szczyt. Używamy **k = 40** w domyślnym presecie i 60 w `thorough` (`quick` działa wyłącznie semantycznie, więc fuzja nigdy się tam nie włącza). Oryginalna praca o RRF wylądowała na k = 60 dla wyszukiwania ogólnego przeznaczenia; nasze ustawienie domyślne nadaje nieco większą wagę wynikom zajmującym wysokie pozycje w kilku trybach — i nie polecamy ręcznego dostrajania tego.

Ponadto wyniki niosą **boost związany z ważnością w grafie**. Hub — funkcja, na której opiera się cała baza kodu — powinien wyprzedzać mało znany węzeł liściowy nawet przy tej samej trafności tekstowej, więc mnożymy każdy wkład przez:

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

Centralność pochodzi z wstępnie obliczonych metryk PageRank/stopnia w pipeline, a boost jest ograniczony do 1,5×, żeby popularna funkcja nie mogła całkowicie zagłuszyć bardziej trafnej, mniej znanej funkcji. Na koniec degradujemy wyniki ze ścieżek vendor, build i archiwalnych oraz deduplikujemy do najlepszego fragmentu na plik.

## Co wciąż jest niedoskonałe

Klasyfikator intencji to zbiór wyrażeń regularnych, nie wytrenowany model. Obsługuje typowe formy zapytań za pomocą heurystyk, a niejednoznaczne zapytanie wraca do `understand_code` i mieszanki z przewagą semantyki. Wcześniejsza wersja tego wpisu przedstawiała spadek odsetka wyszukiwań bez wyników z około 15% do poniżej 5% jako osiągnięty. Propozycja z grudnia 2025 r. zawierała w rzeczywistości szacunkowy punkt wyjścia i cel; nie dostarczono wspierającego zbioru zapytań ani raportu pomiarowego. Wycofujemy twierdzenie o osiągnięciu tej poprawy; zobacz [dowody źródłowe i korektę](https://maguyva.ai/evidence/search-quality.md). Nie zastąpiliśmy klasyfikatora wytrenowanym modelem, ponieważ tania wersja jest szybka i wystarczająco dobra do zamierzonej roli routingu, a klasyfikator, który myli się z pełnym przekonaniem, jest gorszy niż uczciwy fallback. Same wagi są ręcznie dobranymi wartościami a priori, a nie wynikiem uczenia na danych o kliknięciach, których nie zbieramy.

## Zadawaj pytania zamiast wybierać narzędzia

Agent nie powinien musieć wiedzieć, czy sięgnąć po grep, embeddingi, czy graf wywołań — powinien zadać swoje pytanie prostymi słowami i uzyskać właściwą odpowiedź. Fuzja wielomodalna jest tym, co pozwala, by `find_symbol`, wyszukiwanie semantyczne i analiza zależności były dostępne przez jeden interfejs zapytań: system odczytuje kształt pytania i po cichu składa dla niego właściwy retriever. Model, który ocenia embeddingi, ma znaczenie, ale tak samo ważne jest, by wiedzieć, kiedy *nie* ich używać. Dobór właściwego narzędzia do każdego zapytania to odrębny wymiar jakości i wolelibyśmy być za niego odpowiedzialni, niż przerzucać go na wywołującego.

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