Hoppa till innehåll
cd /blog

Multimodal fusionssökning: att välja rätt hämtare för varje sökfråga

[Sökning][Arkitektur]

> En sökfråga som 'var är parseConfig definierad' vill ha en annan typ av sökning än 'hur fungerar auth'. Maguyva klassificerar avsikten, viktar fyra hämtningslägen därefter, och slår samman resultaten med viktad Reciprocal Rank Fusion.

En sökfråga är inte en enda sak.

“var är parseConfig definierad” vill ha en exakt symbol — en precis plats, snabbt. “hur fungerar autentisering” vill ha mening — spridningen av relaterad kod som förklarar ett koncept. “vad går sönder om jag ändrar den här funktionen” vill ha beroendegrafen. “hitta strängen ECONNREFUSED” vill ha en bokstavlig träff, ingenting smart.

Grep är utmärkt för bokstavliga träffar och användbart för viss referensjakt, men det är ingen beroendegraf och det förstår inte mening. Inbäddningar täcker den semantiska sidan, men de är fel verktyg för exakta strängar och konsekvensanalys. De flesta kodsökverktyg väljer en motor och tvingar varje sökfråga att leva med det valet. Maguyva väljer inte. Det räknar ut vilken typ av fråga du ställde, och blandar sedan fyra hämtare i den proportion frågan förtjänar.

Fyra hämtningslägen

Under huven finns fyra oberoende sätt att hitta kod:

  • semantisk — vektorsökning över Voyage-binärinbäddningar; hittar kod efter mening.
  • text — trigrammatchning; hittar bokstavliga strängar, felmeddelanden, exakta identifierare.
  • strukturell — AST-queries; hittar definitioner, signaturer och språkkonstruktioner.
  • graf — beroendegrafen; hittar anropare, anropade och påverkansradie.

Var och en är stark på en egen klass av frågor. Konsten ligger i att avgöra hur mycket man ska lita på var och en för sökfrågan framför sig.

Avsiktsklassificering

Innan någon hämtning körs sorterar en lättviktig klassificerare sökfrågan i en av sex avsikter, med en konfidenspoäng. Den är medvetet billig — ordnad heuristik, första träff vinner — eftersom den körs i den heta vägen och bara lägger till en millisekund eller två:

  • börjar med def , class , func , import … → find_definition (konfidens 0,95)
  • “vem anropar”, “användningar av”, “referenser till” → find_references (0,90)
  • “påverkan”, “påverkansradie”, “vad beror på” → impact_analysis (0,90)
  • en citerad "string" eller ett felmeddelande-token som tracebackexact_match (0,85–0,90)
  • en identifierare av typen CamelCase eller snake_casefind_definition (0,60–0,80)
  • “hur”, “varför”, “förklara”, “arkitektur” → understand_code (0,75)
  • inget matchar → understand_code, låg konfidens (0,40)

Varje avsikt bär en viktprofil över de fyra hämtningslägena. Det här är de verkliga talen:

Avsikt semantisk text strukturell (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å “var är parseConfig definierad” lutar tungt mot AST (0,6). “hur fungerar auth” lutar mot semantiska vektorer (0,5). “vad beror på det här” är nästan helt graf (0,7). “hitta ECONNREFUSED” är nästan helt trigram (0,9), med inbäddningsmodellen helt avstängd — eftersom semantisk likhet är exakt fel verktyg för en exakt sträng.

Den snabba vägen och den sammanslagna vägen

När klassificeraren är säker — poäng ≥ 0,85 — och sökfrågan är en normal sådan, hoppar Maguyva över fusion helt och dirigerar direkt till det enda dominerande hämtningsläget. “var är X definierad” behöver inte fyra hämtare; den behöver AST-indexet, direkt. Den direkta vägen rapporteras tillbaka som fusion_strategy: "direct".

Allt tvetydigt går genom fusion. De fyra (eller tre, i standardinställningen) hämtningslägena körs parallellt, var och en returnerar sin egen rankade lista, och vi kombinerar dem.

Viktad Reciprocal Rank Fusion

Att slå samman heterogena hämtare är svårare än det låter: en cosinuslikhet på 0,82 och en trigrampoäng på 137 och en grafcentralitet på 0,004 ligger inte på samma skala, så man kan inte bara addera dem. Reciprocal Rank Fusion kringgår problemet genom att kasta bort de råa poängen och bara behålla den rangordning varje motor tilldelade. Ett resultats bidrag från ett hämtningsläge är:

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

där rank är dess position i det hämtningslägets lista och k är en utjämningskonstant. Bidrag summeras över hämtningslägen för varje resultat som mer än en motor hittade — samstämmighet mellan hämtare flyter naturligt upp till toppen. Vi använder k = 40 i standardinställningen och 60 för thorough (quick körs enbart semantiskt, så fusion aktiveras aldrig där). Det ursprungliga RRF-arbetet landade på k = 60 för generell hämtning; vi har som standard något skarpare, vilket ger topprankad samstämmighet mellan hämtningslägen lite mer tyngd — och vi rekommenderar inte att man finjusterar den för hand.

Utöver det bär resultaten en grafviktighetsboost. Ett nav — en funktion hela kodbasen lutar sig mot — bör rankas högre än ett obskyrt löv även vid samma textuella relevans, så vi multiplicerar varje bidrag med:

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

Centralitet kommer från pipelinens förberäknade PageRank-/gradmått, och boosten är begränsad till 1,5× så att en populär funktion inte helt kan begrava en mer relevant men obskyr sådan. Slutligen degraderar vi resultat från vendor-, build- och arkivsökvägar, och dedupar till den bästa styckningen per fil.

Vad som fortfarande är ofullständigt

Avsiktsklassificeraren är en stapel av reguljära uttryck, inte en tränad modell. Den täcker de vanliga formerna av en sökfråga väl — beslutet som introducerade den registrerade att andelen nollresultat sjönk från cirka 15 % till under 5 % — men den är heuristisk, och en genuint tvetydig sökfråga faller igenom till understand_code och en semantiskt lutande blandning. Det är ett säkert standardval, inte ett smart ett. Vi har inte ersatt den med en tränad klassificerare eftersom den billiga versionen är snabb och tillräckligt bra, och eftersom en fel-men-säker klassificerare är värre än en ärlig fallback. Vikterna själva är handvalda a priori-värden, inte inlärda från klickdata vi inte samlar in.

Ställ frågan, inte verktyget

En agent ska inte behöva veta om den ska ta till grep eller inbäddningar eller anropsgrafen — den ska ställa sin fråga i vanliga ord och få rätt svar. Multimodal fusion är det som låter find_symbol, semantisk sökning och beroendeanalys ligga bakom en och samma sökyta: systemet läser frågans form och sätter tyst ihop rätt hämtare för den. Modellen som poängsätter inbäddningarna spelar roll, men det gör även att veta när man inte ska använda dem. Att välja rätt verktyg för varje sökfråga är sin egen sorts kvalitet, och det är en vi hellre äger själva än skjuter över på anroparen.

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

Relaterad läsning

Mer från byggloggen för Maguyva