Přeskočit na obsah
cd /blog

Multi-modální fúzní vyhledávání: pro každý dotaz ten správný retriever

[Vyhledávání][Architektura]

> Dotaz jako „kde je definovaný parseConfig“ chce jiné vyhledávání než „jak funguje autentizace“. Maguyva klasifikuje záměr, podle toho zváží čtyři vyhledávací modality a výsledky sloučí pomocí vážené Reciprocal Rank Fusion.

Vyhledávací dotaz není jedna věc.

„kde je definovaný parseConfig“ chce přesný symbol — jedno přesné místo, rychle. „jak funguje autentizace“ chce význam — rozptýlený související kód, který vysvětluje koncept. „co se rozbije, když změním tuhle funkci“ chce graf závislostí. „najdi řetězec ECONNREFUSED“ chce doslovnou shodu, nic chytrého.

Grep je výborný na doslovné shody a užitečný pro některé pátrání po referencích, ale není to graf závislostí a nerozumí významu. Embeddingy pokrývají sémantickou stránku, ale jsou špatný nástroj na přesné řetězce a analýzu dopadu. Většina nástrojů pro vyhledávání v kódu si vybere jeden engine a nechá s tou volbou žít každý dotaz. Maguyva si nevybírá. Zjistí, jaký typ otázky jste položili, a pak smísí čtyři retrievery v poměru, jaký si daná otázka zaslouží.

Čtyři modality

Pod kapotou existují čtyři nezávislé způsoby, jak najít kód:

  • sémantický — vektorové vyhledávání nad binárními Voyage embeddingy; hledá kód podle významu.
  • textový — trigramová shoda; hledá literály, chybové řetězce, přesné identifikátory.
  • strukturální — dotazy AST; hledá definice, signatury a jazykové konstrukce.
  • grafový — graf závislostí; hledá volající, volané a poloměr dopadu.

Každý je silný na jiné třídě otázek. Trik je rozhodnout, nakolik každému z nich věřit pro dotaz, který máte před sebou.

Klasifikace záměru

Než proběhne jakékoliv vyhledávání, odlehčený klasifikátor zařadí dotaz do jednoho ze šesti záměrů, s mírou jistoty. Je záměrně levný — seřazené heuristiky, vyhrává první shoda — protože běží na hot path a přidává jen milisekundu nebo dvě:

  • začíná na def , class , func , import … → find_definition (jistota 0,95)
  • „kdo volá“, „použití“, „reference na“ → find_references (0,90)
  • „dopad“, „poloměr dopadu“, „co na tomhle závisí“ → impact_analysis (0,90)
  • "string" v uvozovkách nebo chybový token jako tracebackexact_match (0,85–0,90)
  • identifikátor ve tvaru CamelCase nebo snake_casefind_definition (0,60–0,80)
  • „jak“, „proč“, „vysvětli“, „architektura“ → understand_code (0,75)
  • nic nesedí → understand_code, nízká jistota (0,40)

Každý záměr nese profil vah napříč čtyřmi modalitami. Toto jsou skutečná čísla:

Záměr sémantický textový strukturální (AST) grafový
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

Takže „kde je definovaný parseConfig“ se výrazně opírá o AST (0,6). „jak funguje autentizace“ se opírá o sémantické vektory (0,5). „co na tomhle závisí“ je skoro celé grafové (0,7). „najdi ECONNREFUSED“ je skoro celé trigramové (0,9), embedding model je úplně vypnutý — protože sémantická podobnost je přesně ten špatný nástroj na přesný řetězec.

Rychlá cesta a fúzovaná cesta

Když si je klasifikátor jistý — skóre ≥ 0,85 — a dotaz je běžný, Maguyva fúzi úplně přeskočí a routuje přímo na jednu dominantní modalitu. „kde je definované X“ nepotřebuje čtyři retrievery; potřebuje AST index, hned. Tahle přímá cesta se hlásí zpátky jako fusion_strategy: "direct".

Všechno nejednoznačné prochází fúzí. Čtyři (nebo tři, na výchozím presetu) modality běží paralelně, každá vrací svůj vlastní seřazený seznam, a my je kombinujeme.

Vážená Reciprocal Rank Fusion

Fúzovat heterogenní retrievery je těžší, než to zní: kosinová podobnost 0,82, trigramové skóre 137 a grafová centralita 0,004 nejsou na stejné škále, takže je nemůžete jen sečíst. Reciprocal Rank Fusion tenhle problém obchází tím, že zahodí syrová skóre a ponechá si jen pořadí, které danému výsledku přidělil každý engine. Příspěvek výsledku z jedné modality je:

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

kde rank je jeho pozice v seznamu dané modality a k je vyhlazovací konstanta. Příspěvky se sčítají napříč modalitami pro každý výsledek, který našel víc než jeden engine — shoda mezi retrievery se přirozeně vyplave nahoru. Používáme k = 40 na výchozím presetu a 60 na thorough (quick běží čistě sémanticky, takže se tam fúze nikdy nezapojí). Původní práce o RRF přistála na k = 60 pro obecné vyhledávání; my ve výchozím stavu jdeme mírně ostřeji, což dává shodě mezi modalitami na nejvyšších pozicích trochu větší váhu — a nedoporučujeme to ručně dolaďovat.

Nad tím vším výsledky nesou ještě boost za grafovou důležitost. Hub — funkce, o kterou se opírá celá kódová základna — by měla porazit obskurní list i při stejné textové relevanci, takže každý příspěvek násobíme:

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

Centralita pochází z předpočítaných metrik PageRank/stupně z pipeline a boost je oříznutý na 1,5×, aby populární funkce úplně nepohřbila relevantnější, ale obskurnější výsledek. Nakonec degradujeme výsledky z cest vendor, build a archive a deduplikujeme na nejlepší chunk na soubor.

Co je pořád nedokonalé

Klasifikátor záměru je hromada regexů, ne naučený model. Běžné tvary dotazu pokrývá dobře — rozhodnutí, které ho zavedlo, zaznamenalo, že míra nulových výsledků klesla zhruba z 15 % pod 5 % — ale je heuristický, a opravdu nejednoznačný dotaz propadne do understand_code a semanticky nakloněné směsi. To je bezpečný výchozí stav, ne chytrý. Nenahradili jsme ho natrénovaným klasifikátorem, protože levná verze je rychlá a dost dobrá, a protože klasifikátor, který se plete, ale je si jistý, je horší než upřímný fallback. Samotné váhy jsou ručně zvolené priory, ne naučené z dat o kliknutích, která nesbíráme.

Ptejte se na otázku, ne na nástroj

Agent by neměl muset vědět, jestli sáhnout po grepu, embeddingech, nebo grafu volání — měl by položit svou otázku prostými slovy a dostat správnou odpověď. Multi-modální fúze je to, co umožňuje find_symbol, sémantickému vyhledávání a analýze závislostí sedět za jedním dotazovacím rozhraním: systém přečte tvar otázky a tiše sestaví ten správný retriever. Na modelu, který embeddingy skóruje, záleží, ale záleží i na tom, kdy je nepoužít. Vybrat pro každý dotaz správný nástroj je svůj vlastní druh kvality, a raději ho vlastníme sami, než abychom ho přehazovali na volajícího.

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

Související čtení

Další ze stavebního deníku Maguyva