Búsqueda por fusión multimodal: eligiendo el recuperador correcto para cada consulta
> Una consulta como 'dónde está definido parseConfig' necesita una búsqueda distinta a 'cómo funciona la autenticación'. Maguyva clasifica la intención, pondera en consecuencia cuatro modalidades de recuperación, y fusiona los resultados con Reciprocal Rank Fusion ponderada.
Una consulta de búsqueda no es una sola cosa.
«dónde está definido parseConfig» quiere un símbolo exacto — una ubicación precisa, rápido. «cómo funciona la autenticación» quiere significado — el conjunto de código relacionado que explica un concepto. «qué se rompe si cambio esta función» quiere el grafo de dependencias. «encontrar la cadena ECONNREFUSED» quiere una coincidencia literal, nada elaborado.
Grep es excelente para coincidencias literales y útil para algunas búsquedas de referencias, pero no es un grafo de dependencias y no entiende el significado. Los embeddings cubren el lado semántico, pero son la herramienta equivocada para cadenas exactas y análisis de impacto. La mayoría de las herramientas de búsqueda de código eligen un solo motor y hacen que cada consulta viva con esa elección. Maguyva no elige. Determina qué tipo de pregunta hiciste y luego combina cuatro recuperadores en la proporción que esa pregunta merece.
Cuatro modalidades
Por debajo hay cuatro formas independientes de encontrar código:
- semántica — búsqueda vectorial sobre embeddings binarios de Voyage; encuentra código por significado.
- texto — coincidencia por trigramas; encuentra literales, cadenas de error, identificadores exactos.
- estructural — consultas AST; encuentra definiciones, firmas y construcciones del lenguaje.
- grafo — el grafo de dependencias; encuentra quién llama, a quién se llama, y el radio de impacto.
Cada una es fuerte en una clase distinta de pregunta. El truco está en decidir cuánto confiar en cada una para la consulta que tienes delante.
Clasificación de intención
Antes de que corra cualquier recuperación, un clasificador ligero ordena la consulta en una de seis intenciones, con un puntaje de confianza. Es deliberadamente barato — heurísticas ordenadas, gana la primera coincidencia — porque corre en la ruta crítica y solo agrega uno o dos milisegundos:
- empieza con
def,class,func,import… → find_definition (confianza 0.95) - «quién llama», «usos de», «referencias a» → find_references (0.90)
- «impacto», «radio de impacto», «qué depende de» → impact_analysis (0.90)
- un
"string"entre comillas o un token de error comotraceback→ exact_match (0.85–0.90) - un identificador
CamelCaseosnake_case→ find_definition (0.60–0.80) - «cómo», «por qué», «explica», «arquitectura» → understand_code (0.75)
- nada coincide → understand_code, confianza baja (0.40)
Cada intención lleva un perfil de ponderación entre las cuatro modalidades. Estos son los números reales:
| Intención | semántica | texto | estructural (AST) | grafo |
|---|---|---|---|---|
| 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 |
Así que «dónde está definido parseConfig» se apoya fuerte en AST (0.6). «cómo funciona la autenticación» se apoya en vectores semánticos (0.5). «qué depende de esto» es casi todo grafo (0.7). «encontrar ECONNREFUSED» es casi todo trigramas (0.9), con el modelo de embeddings completamente apagado — porque la similitud semántica es exactamente la herramienta equivocada para una cadena exacta.
La ruta rápida y la ruta fusionada
Cuando el clasificador tiene confianza — puntaje ≥ 0.85 — y la consulta es normal, Maguyva se salta la fusión por completo y enruta directo a la única modalidad dominante. «dónde está definido X» no necesita cuatro recuperadores; necesita el índice AST, ya. Esa ruta directa se reporta como fusion_strategy: "direct".
Todo lo ambiguo pasa por la fusión. Las cuatro modalidades (o tres, en el preajuste predeterminado) corren en paralelo, cada una devolviendo su propia lista clasificada, y las combinamos.
Reciprocal Rank Fusion ponderada
Fusionar recuperadores heterogéneos es más difícil de lo que suena: una similitud coseno de 0.82, un puntaje de trigramas de 137 y una centralidad de grafo de 0.004 no están en la misma escala, así que no puedes simplemente sumarlos. Reciprocal Rank Fusion evita el problema descartando los puntajes crudos y conservando solo el rango que cada motor asignó. La contribución de un resultado desde una modalidad es:
contribution = weight × 1 / (k + rank + 1)
donde rank es su posición en la lista de esa modalidad y k es una constante de suavizado. Las contribuciones se suman entre modalidades para cualquier resultado que más de un motor encontró — el acuerdo entre recuperadores flota naturalmente hacia arriba. Usamos k = 40 en el preajuste predeterminado y 60 en thorough (quick corre solo en modo semántico, así que la fusión nunca entra en juego ahí). El trabajo original de RRF llegó a k = 60 para recuperación de propósito general; nosotros por defecto somos un poco más agudos, lo que le da a los acuerdos mejor clasificados entre modalidades un poco más de peso — y no recomendamos ajustarlo a mano.
Además de eso, los resultados llevan un impulso de importancia de grafo. Un hub — una función de la que depende toda la base de código — debería superar a una hoja oscura incluso con la misma relevancia textual, así que multiplicamos cada contribución por:
boost = min(1 + 0.3 × ln(1 + centrality), 1.5)
La centralidad viene de las métricas precalculadas de PageRank/grado del pipeline, y el impulso está limitado a 1.5× para que una función popular no pueda enterrar por completo a una más relevante pero oscura. Finalmente degradamos los resultados de rutas de vendor, build y archivo, y deduplicamos al mejor fragmento por archivo.
Lo que todavía es imperfecto
El clasificador de intención es una pila de expresiones regulares, no un modelo entrenado. Cubre bien las formas comunes de una consulta — la decisión que lo introdujo registró que la tasa de resultados vacíos bajó de aproximadamente 15% a menos del 5% — pero es heurístico, y una consulta genuinamente ambigua cae en understand_code y una mezcla inclinada hacia lo semántico. Eso es un valor por defecto seguro, no uno inteligente. No lo hemos reemplazado con un clasificador entrenado porque la versión barata es rápida y suficientemente buena, y porque un clasificador equivocado pero confiado es peor que un respaldo honesto. Los pesos mismos son priors elegidos a mano, no aprendidos de datos de clics que no recolectamos.
Pregunta lo que necesitas, no la herramienta
Un agente no debería tener que saber si recurrir a grep, a embeddings o al grafo de llamadas — debería hacer su pregunta en términos simples y obtener la respuesta correcta. La fusión multimodal es lo que permite que find_symbol, la búsqueda semántica y el análisis de dependencias convivan detrás de una sola superficie de consulta: el sistema lee la forma de la pregunta y ensambla en silencio el recuperador correcto para ella. El modelo que puntúa los embeddings importa, pero también importa saber cuándo no usarlos. Elegir la herramienta correcta para cada consulta es su propio tipo de calidad, y es una que preferimos asumir nosotros en lugar de delegarla a quien hace la llamada.
// you bring the question. it brings the tools.
Lectura relacionada
Más del registro de build de Maguyva
Por qué actualizamos la búsqueda de código a voyage-4-large_
Migramos nuestros embeddings de código a voyage-4-large — actualmente en la cima de la tabla pública de RTEB para recuperación de código. La versión honesta: el compromiso que hacemos, qué indexamos realmente, y por qué pagamos por embeddings premium.
Autosuperación recursiva de lenguajes: afinando la inteligencia de código en ~280 lenguajes_
Damos soporte de inteligencia de código para ~280 lenguajes. Ningún humano puede auditar eso a mano. Así que construimos un bucle de autosuperación recursiva de lenguajes — verificación puntual, LLM como juez, corregir una cosa, revalidar — y lo ejecutamos con una flota de agentes aislados hasta que la extracción sea realmente correcta, no solo verde.
Observabilidad de agentes: hooks, Alloy y Grafana_
Conectamos Claude Code y Codex a un único stack de Grafana con OpenTelemetry y Alloy, y luego usamos trazas y registros para encontrar y corregir problemas de comportamiento de los agentes en su origen.