Lumaktaw papunta sa content
cd /blog

Multi-Modal Fusion Search: Pagpili ng Tamang Retriever Para sa Bawat Query

[Search][Architecture]

> Ibang klaseng search ang gusto ng query tulad ng 'saan na-define ang parseConfig' kumpara sa 'paano gumagana ang auth'. Ini-classify ng Maguyva ang intent, tinitimbang ang apat na retrieval modality ayon dito, at pinagsasama ang mga resulta gamit ang weighted Reciprocal Rank Fusion.

Hindi iisang bagay ang isang search query.

Ang “saan na-define ang parseConfig” ay gustong makakuha ng eksaktong symbol — isang precise na lokasyon, mabilis. Ang “paano gumagana ang authentication” ay gustong makakuha ng kahulugan — ang kalat ng related code na nagpapaliwanag ng isang konsepto. Ang “ano ang masisira kung babaguhin ko ang function na ito” ay gustong makakuha ng dependency graph. Ang “hanapin ang string na ECONNREFUSED” ay gustong makakuha ng literal na match, walang kalokohan.

Mahusay ang grep para sa mga literal na match at kapaki-pakinabang para sa ilang reference hunt, pero hindi ito isang dependency graph at hindi nito naiintindihan ang kahulugan. Sinasakop ng embeddings ang semantic side, pero mali itong tool para sa mga eksaktong string at impact analysis. Karamihan sa mga code search tool, pumipili ng isang engine at pinapabuhay ang bawat query sa pagpiling iyon. Hindi pumipili ang Maguyva. Sinusuri nito kung anong klaseng tanong ang itinanong mo, tapos hinahalo ang apat na retriever sa proporsyong nararapat sa tanong na iyon.

Apat na Modalidad

Sa ilalim ng makina, may apat na independent na paraan para makahanap ng code:

  • semantic — vector search sa Voyage binary embeddings; naghahanap ng code batay sa kahulugan.
  • text — trigram matching; naghahanap ng mga literal, error string, eksaktong identifier.
  • structural — AST queries; naghahanap ng mga definition, signature, at language construct.
  • graph — ang dependency graph; naghahanap ng mga caller, callee, at saklaw ng epekto.

Malakas ang bawat isa sa ibang klase ng tanong. Ang trick, magdesisyon kung gaano dapat pagkatiwalaan ang bawat isa para sa query na nasa harap mo.

Pag-uuri ng intent

Bago tumakbo ang anumang retrieval, ini-uri ng isang lightweight classifier ang query papunta sa isa sa anim na intent, kasama ng confidence score. Sadyang mura ito — ordered heuristics, ang unang match ang mananalo — dahil tumatakbo ito sa hot path at nagdaragdag lang ng isa o dalawang millisecond:

  • nagsisimula sa def , class , func , import … → find_definition (confidence 0.95)
  • “who calls”, “usages of”, “references to” → find_references (0.90)
  • “impact”, “saklaw ng epekto”, “ano ang umaasa rito” → impact_analysis (0.90)
  • isang quoted "string" o isang error token tulad ng tracebackexact_match (0.85–0.90)
  • isang CamelCase o snake_case identifier → find_definition (0.60–0.80)
  • “how”, “why”, “explain”, “architecture” → understand_code (0.75)
  • walang tumugma → understand_code, mababang confidence (0.40)

May weight profile ang bawat intent sa apat na modalidad. Ito ang mga totoong numero:

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

Kaya ang “saan na-define ang parseConfig” ay lubos na umaasa sa AST (0.6). Ang “paano gumagana ang auth” ay umaasa sa semantic vectors (0.5). Ang “ano ang umaasa dito” ay halos puro graph (0.7). Ang “hanapin ang ECONNREFUSED” ay halos puro trigram (0.9), na naka-off nang tuluyan ang embedding model — dahil eksaktong maling tool ang semantic similarity para sa isang eksaktong string.

Ang Mabilis na Path, at ang Fused na Path

Kapag confident ang classifier — score ≥ 0.85 — at karaniwan lang ang query, nililiktawan ng Maguyva ang fusion nang tuluyan at diretsong ini-route papunta sa iisang dominanteng modalidad. Ang “saan na-define ang X” ay hindi kailangan ng apat na retriever; kailangan nito ang AST index, ngayon na. Iniuulat pabalik ang direktang path na iyon bilang fusion_strategy: "direct".

Dumadaan sa fusion ang lahat ng ambiguous. Tumatakbo nang parallel ang apat (o tatlo, sa default preset) na modalidad, bawat isa ay nagbabalik ng sariling ranked list, at pinagsasama namin ang mga ito.

Weighted Reciprocal Rank Fusion

Mas mahirap kaysa sa tunog nito ang pagsasama ng heterogeneous na mga retriever: hindi magkapareho ang scale ng isang cosine similarity na 0.82 at isang trigram score na 137 at isang graph centrality na 0.004, kaya hindi mo lang puwedeng idagdag ang mga ito. Nililikas ng Reciprocal Rank Fusion ang problema sa pamamagitan ng pagtapon sa raw scores at pananatili lang ng rank na ibinigay ng bawat engine. Ganito ang kontribusyon ng isang resulta mula sa isang modalidad:

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

kung saan ang rank ay ang posisyon nito sa listahan ng modalidad na iyon at ang k ay isang smoothing constant. Sinusuma ang mga kontribusyon sa lahat ng modalidad para sa anumang resulta na nahanap ng mahigit isang engine — kusang lumulutang pataas ang pagkakasundo sa pagitan ng mga retriever. Gumagamit kami ng k = 40 sa default preset at 60 sa thorough (tumatakbong semantic-only lang ang quick, kaya hindi na kumikilos ang fusion diyan). Napunta ang original na RRF work sa k = 60 para sa general-purpose retrieval; medyo mas matalim ang default namin, na nagbibigay ng kaunting dagdag na weight sa top-ranked na pagkakasundo sa pagitan ng mga modalidad — at hindi namin inirerekomenda ang hand-tuning nito.

Dagdag pa rito, may dalang boost mula sa kahalagahan ng graph ang mga resulta. Ang isang hub — isang function na inaasahan ng buong codebase — ay dapat mangibabaw sa isang obscure na leaf kahit magkapareho ang textual relevance, kaya minu-multiply namin ang bawat kontribusyon ng:

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

Nagmumula ang centrality sa precomputed PageRank/degree metrics ng pipeline, at naka-clamp ang boost sa 1.5× para hindi kayang lubusang ilibing ng isang popular na function ang isang mas relevant na obscure na function. Sa wakas, dine-demote namin ang mga resulta mula sa vendor, build, at archive paths, at dine-dedupe papunta sa pinakamagandang chunk kada file.

Ano ang Hindi Pa Perpekto

Ang intent classifier ay isang stack ng regexes, hindi isang learned model. Mahusay nitong sinasaklaw ang mga karaniwang hugis ng query — naitala ng desisyon na nagpakilala nito ang pagbaba ng zero-result rate mula sa humigit-kumulang 15% papunta sa mas mababa sa 5% — pero heuristic ito, at ang isang tunay na ambiguous na query, nahuhulog papunta sa understand_code at isang semantic-leaning na halo. Isang safe na default iyon, hindi isang matalino. Hindi pa namin ito pinalitan ng isang trained classifier dahil mabilis at sapat na maganda ang murang bersyon, at dahil mas masahol ang isang mali-pero-confident na classifier kaysa sa isang tapat na fallback. Ang mga weight mismo, hand-chosen na priors, hindi natutunan mula sa click data na hindi naman namin kinokolekta.

Itanong ang Tanong, Hindi ang Tool

Hindi dapat kailanganin ng isang agent na malaman kung sasabak sa grep o embeddings o sa call graph — dapat itanong nito ang tanong sa simpleng termino at makakuha ng tamang sagot. Ang multi-modal fusion ang nagbibigay-daan sa find_symbol, semantic search, at dependency analysis na maupo sa likod ng iisang query surface: binabasa ng system ang hugis ng tanong at tahimik na binubuo ang tamang retriever para dito. Mahalaga ang model na nagsu-score sa embeddings, pero mahalaga rin ang pagkaalam kung kailan hindi ito gagamitin. Ang pagpili ng tamang tool para sa bawat query, sarili nitong klase ng kalidad, at mas gusto naming angkinin ito kaysa itulak papunta sa caller.

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

Kaugnay na babasahin

Higit pa mula sa build log ng Maguyva