Hopp til innhold
cd /blog

Multimodalt fusjonssøk: Velge riktig retriever for hvert søk

[Søk][Arkitektur]

> Et søk som «hvor er parseConfig definert» krever et annet søk enn «hvordan fungerer autentisering». Maguyva klassifiserer intensjonen, vekter fire retrieval-modaliteter deretter, og fusjonerer resultatene med vektet Reciprocal Rank Fusion.

Et søk er ikke bare én ting.

«hvor er parseConfig definert» vil ha et eksakt symbol — ett presist sted, raskt. «hvordan fungerer autentisering» vil ha mening — spredningen av relatert kode som forklarer et konsept. «hva ryker hvis jeg endrer denne funksjonen» vil ha avhengighetsgrafen. «finn strengen ECONNREFUSED» vil ha et bokstavelig treff, ikke noe smart.

Grep er utmerket for bokstavelige treff og nyttig for enkelte referansejakter, men det er ikke en avhengighetsgraf, og det forstår ikke mening. Embeddinger dekker den semantiske siden, men de er feil verktøy for eksakte strenger og konsekvensanalyse. De fleste kodesøkverktøy velger én motor og lar hvert søk leve med det valget. Maguyva velger ikke. Det finner ut hva slags spørsmål du stilte, og blander deretter fire retrievere i den andelen spørsmålet fortjener.

Fire modaliteter

Under panseret finnes det fire uavhengige måter å finne kode på:

  • semantisk — vektorsøk over Voyage binære embeddinger; finner kode etter mening.
  • tekst — trigram-matching; finner bokstaver, feilstrenger, eksakte identifikatorer.
  • strukturelt — AST-spørringer; finner definisjoner, signaturer og språkkonstruksjoner.
  • graf — avhengighetsgrafen; finner kallere, kalte, og blast radius.

Hver er sterk på en annen klasse spørsmål. Trikset er å avgjøre hvor mye man skal stole på hver enkelt for søket foran deg.

Intensjonsklassifisering

Før noen retrieval kjører, sorterer en lettvekts-klassifiserer søket inn i én av seks intensjoner, med en konfidensscore. Den er bevisst billig — ordnede heuristikker, første treff vinner — fordi den kjører på den kritiske stien og bare legger til ett eller to millisekunder:

  • starter med def , class , func , import … → find_definition (konfidens 0,95)
  • «who calls», «usages of», «references to» → find_references (0,90)
  • «impact», «blast radius», «what depends on» → impact_analysis (0,90)
  • et sitert "string" eller et feiltoken som tracebackexact_match (0,85–0,90)
  • en CamelCase- eller snake_case-identifikator → find_definition (0,60–0,80)
  • «how», «why», «explain», «architecture» → understand_code (0,75)
  • ingenting matcher → understand_code, lav konfidens (0,40)

Hver intensjon har en vektprofil på tvers av de fire modalitetene. Dette er de reelle tallene:

Intensjon semantisk tekst strukturelt (AST) graf
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

Så «hvor er parseConfig definert» lener seg tungt på AST (0,6). «hvordan fungerer autentisering» lener seg på semantiske vektorer (0,5). «hva avhenger av dette» er nesten bare graf (0,7). «finn ECONNREFUSED» er nesten bare trigram (0,9), med embedding-modellen helt slått av — fordi semantisk likhet er akkurat feil verktøy for en eksakt streng.

Hurtigstien, og den fusjonerte stien

Når klassifiseringen er sikker — score ≥ 0,85 — og søket er et normalt et, hopper Maguyva over fusjon helt og ruter rett til den ene dominerende modaliteten. «hvor er X definert» trenger ikke fire retrievere; det trenger AST-indeksen, nå. Den direkte stien rapporteres tilbake som fusion_strategy: "direct".

Alt som er tvetydig går gjennom fusjon. De fire (eller tre, på standardoppsettet) modalitetene kjører parallelt, hver returnerer sin egen rangerte liste, og vi kombinerer dem.

Vektet Reciprocal Rank Fusion

Å fusjonere heterogene retrievere er vanskeligere enn det høres ut som: en kosinuslikhet på 0,82 og en trigram-score på 137 og en graf-sentralitet på 0,004 er ikke på samme skala, så du kan ikke bare legge dem sammen. Reciprocal Rank Fusion omgår problemet ved å kaste bort de rå scorene og bare beholde rangeringen hver motor tildelte. Et resultats bidrag fra én modalitet er:

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

hvor rank er dets posisjon i den modalitetens liste, og k er en utjevningskonstant. Bidrag summeres på tvers av modaliteter for ethvert resultat som mer enn én motor fant — enighet mellom retrievere flyter naturlig til toppen. Vi bruker k = 40 på standardoppsettet og 60 på thorough (quick kjører kun semantisk, så fusjon utløses aldri der). Det originale RRF-arbeidet landet på k = 60 for generell retrieval; vi har som standard en litt skarpere verdi, som gir toppangert enighet mellom modaliteter litt mer vekt — og vi anbefaler ikke å håndjustere den.

I tillegg får resultater en graf-viktighetsboost. Et knutepunkt — en funksjon hele kodebasen lener seg på — bør rangere høyere enn et obskurt løvblad selv ved samme tekstlige relevans, så vi multipliserer hvert bidrag med:

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

Sentraliteten kommer fra pipelinens forhåndsberegnede PageRank/grad-metrikker, og boosten er klemt fast på 1,5× slik at en populær funksjon ikke helt kan begrave en mer relevant, obskur en. Til slutt nedgraderer vi resultater fra vendor-, build- og arkivstier, og fjerner duplikater slik at kun det beste utdraget per fil beholdes.

Hva som fortsatt er ufullkomment

Intensjonsklassifisereren er en stabel med regulære uttrykk, ikke en lært modell. Den dekker de vanlige formene på et søk godt — beslutningen som introduserte den, registrerte at null-resultat-raten falt fra rundt 15 % til under 5 % — men den er heuristisk, og et genuint tvetydig søk faller gjennom til understand_code og en semantisk-vridd blanding. Det er en trygg standard, ikke en smart en. Vi har ikke erstattet den med en trent klassifiserer fordi den billige versjonen er rask og god nok, og fordi en feil-men-sikker klassifiserer er verre enn en ærlig fallback. Vektene selv er håndvalgte antakelser (priors), ikke lært fra klikkdata vi ikke samler inn.

Still spørsmålet, ikke verktøyet

En agent burde ikke måtte vite om den skal gripe til grep eller embeddinger eller kallgrafen — den burde stille spørsmålet sitt med enkle ord og få riktig svar. Multimodal fusjon er det som lar find_symbol, semantisk søk og avhengighetsanalyse sitte bak én søkeoverflate: systemet leser formen på spørsmålet og setter stille sammen riktig retriever for det. Modellen som scorer embeddingene betyr noe, men det gjør også det å vite når man ikke skal bruke dem. Å velge riktig verktøy for hvert søk er sin egen type kvalitet, og det er en vi heller vil eie selv enn å skyve over på den som kaller.

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

Relatert lesning

Mer fra Maguyva-byggeloggen