# Recherche par fusion multimodale : choisir le bon moteur de récupération pour chaque requête

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

> Une requête comme « où est défini parseConfig » appelle une recherche différente de « comment fonctionne l'authentification ». Maguyva classe l'intention, pondère en conséquence quatre modalités de récupération, puis fusionne les résultats avec une Reciprocal Rank Fusion pondérée.

> Note sur la conception historique : cet article d’avril 2026 décrit la conception du routage à cette époque, pas le comportement par défaut actuel. La correction de septembre ci-dessous porte uniquement sur une affirmation de mesure. Pour le routage actuel, les préréglages et les commandes explicites des modalités, voir [Comment ça marche](https://maguyva.ai/fr/docs/how-it-works).

Une requête de recherche n'est pas une chose unique.

« où est défini `parseConfig` » veut un symbole exact — un emplacement précis, vite. « comment fonctionne l'authentification » veut du sens — l'ensemble du code apparenté qui explique un concept. « qu'est-ce qui casse si je modifie cette fonction » veut le graphe de dépendances. « trouver la chaîne `ECONNREFUSED` » veut une correspondance littérale, rien de plus subtil.

Grep excelle pour les correspondances littérales et se révèle utile pour certaines chasses aux références, mais ce n'est pas un graphe de dépendances et il ne comprend pas le sens. Les embeddings couvrent le versant sémantique, mais ce ne sont pas les bons outils pour les chaînes exactes et l'analyse d'impact. La plupart des outils de recherche de code choisissent un seul moteur et font vivre chaque requête avec ce choix. Maguyva ne choisit pas. Il détermine quel type de question vous avez posée, puis mélange quatre moteurs de récupération dans la proportion que cette question mérite.

## Quatre modalités

Sous le capot, il existe quatre façons indépendantes de trouver du code :

- **sémantique** — recherche vectorielle sur des embeddings binaires Voyage ; trouve le code par le sens.
- **textuelle** — correspondance de trigrammes ; trouve les littéraux, les chaînes d'erreur, les identifiants exacts.
- **structurelle** — requêtes AST ; trouve les définitions, les signatures et les constructions du langage.
- **par graphe** — le graphe de dépendances ; trouve les appelants, les appelés et le rayon d'impact.

Chacune excelle sur une classe de question différente. L'astuce consiste à décider dans quelle mesure faire confiance à chacune pour la requête que l'on a sous les yeux.

## Classification d'intention

Avant tout lancement de récupération, un classificateur léger trie la requête dans l'une de six intentions, avec un score de confiance. Il est délibérément peu coûteux — des heuristiques ordonnées, la première correspondance l'emporte — car il s'exécute sur le chemin critique et n'ajoute qu'une milliseconde ou deux :

- commence par `def `, `class `, `func `, `import `… → **find_definition** (confiance 0,95)
- « who calls » (qui appelle), « usages of » (usages de), « references to » (références à) → **find_references** (0,90)
- « impact », « blast radius » (rayon d'impact), « what depends on » (qu'est-ce qui dépend de) → **impact_analysis** (0,90)
- une chaîne entre guillemets `"string"` ou un jeton d'erreur comme `traceback` → **exact_match** (0,85–0,90)
- un identifiant `CamelCase` ou `snake_case` → **find_definition** (0,60–0,80)
- « how » (comment), « why » (pourquoi), « explain » (expliquer), « architecture » → **understand_code** (0,75)
- rien ne correspond → **understand_code**, faible confiance (0,40)

Chaque intention porte un profil de pondération réparti sur les quatre modalités. Voici les chiffres réels :

| Intention | sémantique | textuelle | structurelle (AST) | graphe |
|---|---|---|---|---|
| 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 |

Ainsi, « où est défini `parseConfig` » s'appuie fortement sur l'AST (0,6). « comment fonctionne l'authentification » s'appuie sur les vecteurs sémantiques (0,5). « qu'est-ce qui dépend de ceci » est presque entièrement du graphe (0,7). « trouver `ECONNREFUSED` » est presque entièrement du trigramme (0,9), le modèle d'embedding étant entièrement désactivé — parce que la similarité sémantique est précisément le mauvais outil pour une chaîne exacte.

## Le chemin rapide, et le chemin fusionné

Quand le classificateur est confiant — score ≥ 0,85 — et que la requête est ordinaire, Maguyva saute entièrement la fusion et route directement vers l'unique modalité dominante. « où est défini X » n'a pas besoin de quatre moteurs de récupération ; il lui faut l'index AST, tout de suite. Ce chemin direct est renvoyé sous la mention `fusion_strategy: "direct"`.

Tout ce qui est ambigu passe par la fusion. Les quatre modalités (ou trois, sur le préréglage par défaut) s'exécutent en parallèle, chacune renvoyant sa propre liste classée, et nous les combinons.

## Reciprocal Rank Fusion pondérée

Fusionner des moteurs de récupération hétérogènes est plus difficile qu'il n'y paraît : une similarité cosinus de 0,82, un score de trigramme de 137 et une centralité de graphe de 0,004 ne sont pas sur la même échelle, donc on ne peut pas simplement les additionner. La Reciprocal Rank Fusion contourne le problème en jetant les scores bruts et en ne conservant que le **rang** attribué par chaque moteur. La contribution d'un résultat pour une modalité est :

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

où `rank` est sa position dans la liste de cette modalité et `k` est une constante de lissage. Les contributions sont sommées entre modalités pour tout résultat trouvé par plus d'un moteur — l'accord entre moteurs de récupération remonte naturellement vers le haut. Nous utilisons **k = 40** sur le préréglage par défaut et 60 sur `thorough` (`quick` tourne uniquement en sémantique, donc la fusion n'y intervient jamais). Les travaux originaux sur la RRF ont retenu k = 60 pour la récupération généraliste ; notre valeur par défaut est légèrement plus tranchante, ce qui donne un peu plus de poids à l'accord entre modalités sur les résultats les mieux classés — et nous ne recommandons pas de la régler manuellement.

En plus de cela, les résultats portent un **bonus d'importance de graphe**. Un nœud central — une fonction sur laquelle s'appuie toute la base de code — devrait devancer un nœud feuille peu connu même à pertinence textuelle égale, donc nous multiplions chaque contribution par :

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

La centralité provient des métriques PageRank / degré précalculées du pipeline, et le bonus est plafonné à 1,5× pour qu'une fonction populaire ne puisse pas complètement enterrer une fonction obscure plus pertinente. Enfin, nous rétrogradons les résultats issus des chemins vendor, build et archive, et nous dédupliquons pour ne garder que le meilleur segment par fichier.

## Ce qui reste imparfait

Le classificateur d’intention est un ensemble d’expressions régulières, pas un modèle entraîné. Il traite les formes courantes de requêtes par des heuristiques, et une requête ambiguë se rabat sur `understand_code` et un mélange à dominante sémantique. Une version antérieure de cet article présentait comme obtenue une baisse du taux de requêtes sans résultat d’environ 15 % à moins de 5 %. La proposition de décembre 2025 indiquait en réalité une valeur initiale estimée et un objectif ; aucun jeu de requêtes ni relevé de mesure à l’appui n’est fourni. Nous retirons cette affirmation d’amélioration obtenue ; voir les [preuves sources et la correction](https://maguyva.ai/evidence/search-quality.md). Nous n’avons pas remplacé le classificateur par un modèle entraîné parce que la version économique est rapide et suffisante pour son rôle de routage prévu, et parce qu’un classificateur erroné mais sûr de lui est pire qu’un repli honnête. Les poids sont des hypothèses initiales choisies à la main, pas des valeurs apprises à partir de données de clics que nous ne collectons pas.

## Poser la question, pas choisir l'outil

Un agent ne devrait pas avoir à savoir s'il faut recourir à grep, aux embeddings ou au graphe d'appels — il devrait poser sa question en termes simples et obtenir la bonne réponse. La fusion multimodale est ce qui permet à `find_symbol`, à la recherche sémantique et à l'analyse de dépendances d'être accessibles via une seule interface de requête : le système lit la forme de la question et assemble discrètement le bon moteur de récupération pour elle. Le modèle qui note les embeddings compte, mais savoir quand *ne pas* les utiliser compte tout autant. Choisir le bon outil pour chaque requête est une forme de qualité à part entière, et c'est une responsabilité que nous préférons assumer plutôt que de la reporter sur l'appelant.

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