# Ricerca a fusione multi-modale: scegliere il retriever giusto per ogni query

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

> Una query come 'dove è definito parseConfig' vuole una ricerca diversa da 'come funziona l'auth'. Maguyva classifica l'intento, pesa di conseguenza quattro modalità di retrieval, e fonde i risultati con una Reciprocal Rank Fusion pesata.

> Nota sul design storico: questo articolo dell'aprile 2026 descrive il design del routing di allora, non il comportamento predefinito attuale. La correzione di settembre riportata sotto riguarda esclusivamente un'affermazione su una misurazione. Per il routing attuale, i preset e i controlli espliciti delle modalità, si veda [Come funziona](https://maguyva.ai/it/docs/how-it-works).

Una query di ricerca non è una cosa sola.

"dove è definito `parseConfig`" vuole un simbolo esatto — una posizione precisa, veloce. "come funziona l'autenticazione" vuole significato — la distesa di codice correlato che spiega un concetto. "cosa si rompe se cambio questa funzione" vuole il grafo delle dipendenze. "trova la stringa `ECONNREFUSED`" vuole un match letterale, niente di furbo.

Grep è eccellente per i match letterali e utile per alcune cacce ai riferimenti, ma non è un grafo delle dipendenze e non capisce il significato. Gli embedding coprono il lato semantico, ma sono lo strumento sbagliato per stringhe esatte e analisi d'impatto. La maggior parte degli strumenti di ricerca sul codice sceglie un solo motore e impone quella scelta a ogni query. Maguyva non sceglie. Capisce che tipo di domanda hai fatto, poi combina quattro retriever nella proporzione che quella domanda merita.

## Quattro modalità

Sotto il cofano ci sono quattro modi indipendenti per trovare codice:

- **semantica** — ricerca vettoriale su embedding binari Voyage; trova codice per significato.
- **testuale** — matching a trigrammi; trova letterali, stringhe di errore, identificatori esatti.
- **strutturale** — query AST; trova definizioni, firme, e costrutti del linguaggio.
- **a grafo** — il grafo delle dipendenze; trova chiamanti, chiamati, e raggio d'impatto.

Ognuna eccelle su una classe diversa di domanda. Il trucco sta nel decidere quanto fidarsi di ciascuna per la query che hai davanti.

## Classificazione dell'intento

Prima che parta qualsiasi retrieval, un classificatore leggero smista la query in una di sei categorie di intento, con un punteggio di confidenza. È deliberatamente economico — euristiche ordinate, vince il primo match — perché gira sul percorso critico e aggiunge solo un millisecondo o due:

- inizia con `def `, `class `, `func `, `import `… → **find_definition** (confidenza 0,95)
- "who calls", "usages of", "references to" (chi chiama, usi di, riferimenti a) → **find_references** (0,90)
- "impact", "blast radius", "what depends on" (impatto, raggio d'impatto, cosa dipende da) → **impact_analysis** (0,90)
- una `"string"` tra virgolette o un token di errore come `traceback` → **exact_match** (0,85–0,90)
- un identificatore `CamelCase` o `snake_case` → **find_definition** (0,60–0,80)
- "how", "why", "explain", "architecture" (come, perché, spiega, architettura) → **understand_code** (0,75)
- niente corrisponde → **understand_code**, bassa confidenza (0,40)

Ogni intento porta un profilo di peso sulle quattro modalità. Questi sono i numeri reali:

| Intento | semantica | testuale | strutturale (AST) | a grafo |
|---|---|---|---|---|
| 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 |

Quindi "dove è definito `parseConfig`" pesa forte sull'AST (0,6). "come funziona l'auth" pesa sui vettori semantici (0,5). "cosa dipende da questo" è quasi tutto grafo (0,7). "trova `ECONNREFUSED`" è quasi tutto trigrammi (0,9), con il modello di embedding completamente spento — perché la similarità semantica è esattamente lo strumento sbagliato per una stringa esatta.

## Il percorso rapido, e il percorso fuso

Quando il classificatore è sicuro — punteggio ≥ 0,85 — e la query è di tipo normale, Maguyva salta del tutto la fusione e instrada direttamente verso la singola modalità dominante. "dove è definito X" non ha bisogno di quattro retriever; ha bisogno dell'indice AST, subito. Quel percorso diretto viene riportato indietro come `fusion_strategy: "direct"`.

Tutto ciò che è ambiguo passa attraverso la fusione. Le quattro modalità (o tre, sul preset predefinito) girano in parallelo, ciascuna restituendo la propria lista ordinata, e le combiniamo.

## Reciprocal Rank Fusion pesata

Fondere retriever eterogenei è più difficile di quanto sembri: una similarità coseno di 0,82 e un punteggio a trigrammi di 137 e una centralità di grafo di 0,004 non sono sulla stessa scala, quindi non puoi semplicemente sommarli. La Reciprocal Rank Fusion aggira il problema scartando i punteggi grezzi e mantenendo solo il **rank** che ogni motore ha assegnato. Il contributo di un risultato da una modalità è:

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

dove `rank` è la sua posizione nella lista di quella modalità e `k` è una costante di smoothing. I contributi vengono sommati tra le modalità per qualsiasi risultato che più di un motore ha trovato — l'accordo tra i retriever sale naturalmente in cima. Usiamo **k = 40** sul preset predefinito e 60 su `thorough` (`quick` gira solo in modalità semantica, quindi la fusione lì non entra mai in gioco). Il lavoro originale su RRF è arrivato a k = 60 per il retrieval general-purpose; il nostro default è leggermente più aggressivo, il che dà un po' più peso all'accordo tra le modalità nei risultati meglio classificati — e non consigliamo di regolarlo a mano.

Oltre a questo, i risultati portano un **boost di importanza del grafo**. Un hub — una funzione su cui si appoggia tutto il codebase — dovrebbe superare in classifica un nodo foglia poco noto anche a parità di rilevanza testuale, quindi moltiplichiamo ogni contributo per:

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

La centralità arriva dalle metriche PageRank/degree precalcolate dalla pipeline, e il boost è limitato a un massimo di 1,5× così che una funzione popolare non possa seppellire completamente una più rilevante ma oscura. Infine retrocediamo i risultati da percorsi vendor, build, e archive, e deduplichiamo al miglior chunk per file.

## Cosa resta imperfetto

Il classificatore dell'intento è un insieme di regex, non un modello addestrato. Gestisce le forme di query comuni con euristiche, mentre una query ambigua ricade su `understand_code` e su una combinazione orientata alla semantica. Una versione precedente di questo articolo descriveva come raggiunta una riduzione del tasso di ricerche senza risultati da circa il 15% a meno del 5%. La proposta del dicembre 2025 riportava in realtà una stima iniziale e un obiettivo; non viene fornito alcun insieme di query o resoconto delle misurazioni a supporto. Ritiriamo quell'affermazione di miglioramento raggiunto; si vedano le [evidenze della fonte e la correzione](https://maguyva.ai/evidence/search-quality.md). Non abbiamo sostituito il classificatore con un modello addestrato perché la versione economica è veloce e sufficientemente valida per il ruolo di routing previsto, e perché un classificatore sicuro di sé ma in errore è peggiore di un fallback onesto. I pesi stessi sono prior scelti a mano, non appresi da dati sui clic che non raccogliamo.

## Fai la domanda, senza dover scegliere lo strumento

Un agente non dovrebbe dover sapere se ricorrere a grep o agli embedding o al call graph — dovrebbe fare la sua domanda in termini semplici e ottenere la risposta giusta. La fusione multi-modale è ciò che permette a `find_symbol`, alla ricerca semantica, e all'analisi delle dipendenze di stare dietro un'unica interfaccia di query: il sistema legge la forma della domanda e assembla silenziosamente il retriever giusto per essa. Il modello che valuta gli embedding conta, ma conta anche sapere quando *non* usarli. Scegliere lo strumento giusto per ogni query è un tipo di qualità a sé, ed è uno che preferiamo possedere noi piuttosto che scaricare su chi chiama.

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