Saltar al contenido
cd /blog

Búsqueda por fusión multimodal: eligiendo el recuperador correcto para cada consulta

[Búsqueda][Arquitectura]

> 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 como tracebackexact_match (0.85–0.90)
  • un identificador CamelCase o snake_casefind_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