Multimodale fusiezoek: voor elke query de juiste retriever kiezen
> Een query zoals 'waar is parseConfig gedefinieerd' vraagt om een ander soort zoeken dan 'hoe werkt auth'. Maguyva classificeert de intentie, weegt vier retrievalmodaliteiten dienovereenkomstig, en voegt de resultaten samen met gewogen Reciprocal Rank Fusion.
Een zoekquery is niet één ding.
“waar is parseConfig gedefinieerd” wil een exact symbool — één precieze locatie, snel. “hoe werkt authenticatie” wil betekenis — de spreiding aan gerelateerde code die een concept verklaart. “wat breekt er als ik deze functie verander” wil de afhankelijkheidsgraaf. “vind de string ECONNREFUSED” wil een letterlijke match, niets slims.
Grep is uitstekend voor letterlijke matches en nuttig voor sommige referentiejachten, maar het is geen afhankelijkheidsgraaf en het begrijpt geen betekenis. Embeddings dekken de semantische kant, maar ze zijn het verkeerde gereedschap voor exacte strings en impactanalyse. De meeste codezoektools kiezen één engine en laten elke query met die keuze leven. Maguyva kiest niet. Het bepaalt wat voor soort vraag je stelde, en mengt dan vier retrievers in de verhouding die die vraag verdient.
Vier modaliteiten
Onder de motorkap zijn er vier onafhankelijke manieren om code te vinden:
- semantic — vectorzoeken over Voyage binary embeddings; vindt code op betekenis.
- text — trigram-matching; vindt letterlijke tekst, foutmeldingen, exacte identifiers.
- structural — AST-queries; vindt definities, signatures en taalconstructies.
- graph — de afhankelijkheidsgraaf; vindt callers, callees en impactradius.
Elk is sterk op een andere klasse vragen. De truc is bepalen hoeveel je elk moet vertrouwen voor de query die voor je ligt.
Intentieclassificatie
Voordat er enige retrieval draait, sorteert een lichtgewicht classifier de query in één van zes intenties, met een confidence-score. Die is bewust goedkoop — geordende heuristieken, eerste match wint — omdat hij op het hot path draait en slechts een milliseconde of twee toevoegt:
- begint met
def,class,func,import… → find_definition (confidence 0,95) - “who calls”, “usages of”, “references to” → find_references (0,90)
- “impact”, “blast radius”, “what depends on” → impact_analysis (0,90)
- een geciteerde
"string"of een errortoken zoalstraceback→ exact_match (0,85–0,90) - een
CamelCase- ofsnake_case-identifier → find_definition (0,60–0,80) - “how”, “why”, “explain”, “architecture” → understand_code (0,75)
- niets matcht → understand_code, lage confidence (0,40)
Elke intentie draagt een gewichtsprofiel over de vier modaliteiten. Dit zijn de echte cijfers:
| Intent | 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 |
Dus “waar is parseConfig gedefinieerd” leunt zwaar op AST (0,6). “hoe werkt auth” leunt op semantische vectoren (0,5). “wat hangt hiervan af” is bijna volledig graph (0,7). “vind ECONNREFUSED” is bijna volledig trigram (0,9), waarbij het embeddingmodel volledig is uitgeschakeld — omdat semantische gelijkenis precies het verkeerde gereedschap is voor een exacte string.
Het snelle pad, en het samengevoegde pad
Wanneer de classifier zeker is — score ≥ 0,85 — en de query een normale is, slaat Maguyva fusie volledig over en routeert direct naar de enkele dominante modaliteit. “where is X defined” heeft geen vier retrievers nodig; het heeft de AST-index nodig, nu meteen. Dat directe pad wordt teruggerapporteerd als fusion_strategy: "direct".
Alles wat ambigu is gaat door fusie. De vier (of drie, bij de standaardpreset) modaliteiten draaien parallel, elk met een eigen gerangschikte lijst, en we combineren ze.
Weighted Reciprocal Rank Fusion
Heterogene retrievers samenvoegen is lastiger dan het klinkt: een cosine similarity van 0,82 en een trigramscore van 137 en een graafcentraliteit van 0,004 zitten niet op dezelfde schaal, dus je kunt ze niet zomaar optellen. Reciprocal Rank Fusion omzeilt het probleem door de ruwe scores weg te gooien en alleen de rank te behouden die elke engine toekende. De bijdrage van een resultaat uit één modaliteit is:
contribution = weight × 1 / (k + rank + 1)
waarbij rank zijn positie is in de lijst van die modaliteit en k een smoothing-constante is. Bijdragen worden opgeteld over modaliteiten heen voor elk resultaat dat door meer dan één engine werd gevonden — overeenstemming tussen retrievers stijgt van nature naar boven. We gebruiken k = 40 bij de standaardpreset en 60 bij thorough (quick draait alleen semantisch, dus fusie komt daar nooit in beeld). Het oorspronkelijke RRF-werk kwam uit op k = 60 voor algemeen-doel-retrieval; wij hanteren standaard iets scherper, wat topgerangschikte overeenstemming tussen modaliteiten iets meer gewicht geeft — en we raden af om er handmatig aan te sleutelen.
Daarbovenop dragen resultaten een boost op graafbelang. Een hub — een functie waar de hele codebase op leunt — hoort een obscuur blad te overtreffen, zelfs bij gelijke tekstuele relevantie, dus vermenigvuldigen we elke bijdrage met:
boost = min(1 + 0.3 × ln(1 + centrality), 1.5)
Centraliteit komt van de vooraf berekende PageRank-/degree-metrieken van de pipeline, en de boost wordt begrensd op 1,5× zodat een populaire functie een relevantere obscure functie niet volledig kan begraven. Tot slot degraderen we resultaten uit vendor-, build- en archiefpaden, en dedupliceren we naar het beste chunk per bestand.
Wat nog altijd niet perfect is
De intentieclassifier is een stapel regexes, geen getraind model. Hij dekt de gangbare vormen van een query goed — de beslissing die hem introduceerde noteerde dat het nulresultatenpercentage daalde van ruwweg 15% naar onder de 5% — maar hij is heuristisch, en een echt ambigue query valt terug op understand_code en een semantisch geneigde mix. Dat is een veilige standaard, geen slimme. We hebben hem niet vervangen door een getraind model, omdat de goedkope versie snel en goed genoeg is, en omdat een fout-maar-zelfverzekerde classifier erger is dan een eerlijke fallback. De gewichten zelf zijn handmatig gekozen priors, niet geleerd van clickdata die we niet verzamelen.
Stel de vraag, niet de tool
Een agent zou niet hoeven weten of hij naar grep of embeddings of de call graph moet grijpen — hij zou zijn vraag in gewone taal moeten kunnen stellen en het juiste antwoord moeten krijgen. Multimodale fusie is wat find_symbol, semantisch zoeken en afhankelijkheidsanalyse achter één query-oppervlak laat zitten: het systeem leest de vorm van de vraag en stelt in stilte de juiste retriever ervoor samen. Het model dat de embeddings scoort, is belangrijk, maar weten wanneer je ze niet moet gebruiken is dat net zo goed. Voor elke query het juiste gereedschap kiezen, is een eigen soort kwaliteit, en dat is er een die we liever zelf dragen dan doorschuiven naar de aanroeper.
// you bring the question. it brings the tools.
Gerelateerde artikelen
Meer uit het bouwlogboek van Maguyva
Waarom we code search hebben geüpgraded naar voyage-4-large_
We hebben onze code-embeddings verplaatst naar voyage-4-large — momenteel bovenaan het publieke RTEB code-retrieval-leaderboard. De eerlijke versie: de afweging die we maken, wat we daadwerkelijk indexeren, en waarom we betalen voor premium embeddings.
Recursieve zelfverbetering voor taal: Code Intelligence slijpen over ~280 talen_
We ondersteunen code intelligence voor ~280 talen. Geen mens kan dat handmatig auditen. Dus bouwden we een recursieve zelfverbeteringslus voor taal — steekproeven nemen, LLM-as-judge, één ding repareren, opnieuw valideren — en draaien die met een vloot geïsoleerde agents totdat extractie daadwerkelijk klopt, niet alleen groen is.
Agent-observability: hooks, Alloy en Grafana_
We hebben Claude Code en Codex verbonden met één Grafana-stack via OpenTelemetry en Alloy, en gebruikten vervolgens traces en logs om agentgedragsproblemen bij de bron op te sporen en te verhelpen.