Busca com Fusão Multimodal: Escolhendo o Retriever Certo Para Cada Consulta
> Uma consulta como 'onde parseConfig é definido' quer um tipo de busca diferente de 'como funciona a autenticação'. O Maguyva classifica a intenção, pondera quatro modalidades de retrieval de acordo, e funde os resultados com Reciprocal Rank Fusion ponderada.
Nota sobre o projeto histórico: este artigo de abril de 2026 descreve o projeto de roteamento daquela época, não o comportamento padrão atual. A correção de setembro abaixo trata apenas de uma afirmação sobre medição. Para conhecer o roteamento atual, as predefinições e os controles explícitos de modalidade, veja Como funciona.
Uma consulta de busca não é uma coisa só.
“onde parseConfig é definido” quer um símbolo exato — um local preciso, rápido. “como funciona a autenticação” quer significado — o conjunto espalhado de código relacionado que explica um conceito. “o que quebra se eu mudar esta função” quer o grafo de dependências. “encontre a string ECONNREFUSED” quer uma correspondência literal, nada sofisticado.
O grep é excelente para correspondências literais e útil para algumas caças a referências, mas não é um grafo de dependências e não entende significado. Embeddings cobrem o lado semântico, mas são a ferramenta errada para strings exatas e análise de impacto. A maioria das ferramentas de busca de código escolhe um único motor e faz toda consulta conviver com essa escolha. O Maguyva não escolhe. Ele descobre que tipo de pergunta você fez, e então combina quatro retrievers na proporção que essa pergunta merece.
Quatro modalidades
Internamente, existem quatro formas independentes de encontrar código:
- semantic — busca vetorial sobre embeddings binários da Voyage; encontra código por significado.
- text — correspondência de trigramas; encontra literais, strings de erro, identificadores exatos.
- structural — queries de AST; encontra definições, assinaturas e construções da linguagem.
- graph — o grafo de dependências; encontra chamadores, chamados e raio de impacto.
Cada um é forte em uma classe diferente de pergunta. O truque é decidir o quanto confiar em cada um para a consulta que está na sua frente.
Classificação de intenção
Antes de qualquer retrieval rodar, um classificador leve classifica a consulta em uma de seis intenções, com uma pontuação de confiança. É deliberadamente barato — heurísticas ordenadas, a primeira correspondência vence — porque roda no caminho crítico e adiciona apenas um ou dois milissegundos:
- começa com
def,class,func,import… → find_definition (confiança 0,95) - “who calls”, “usages of”, “references to” (quem chama, usos de, referências a) → find_references (0,90)
- “impact”, “blast radius”, “what depends on” (impacto, raio de impacto, o que depende de) → impact_analysis (0,90)
- uma
"string"entre aspas ou um token de erro comotraceback→ exact_match (0,85–0,90) - um identificador
CamelCaseousnake_case→ find_definition (0,60–0,80) - “how”, “why”, “explain”, “architecture” (como, por que, explique, arquitetura) → understand_code (0,75)
- nada corresponde → understand_code, baixa confiança (0,40)
Cada intenção carrega um perfil de peso entre as quatro modalidades. Estes são os números reais:
| Intenção | 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 |
Então “onde parseConfig é definido” se apoia fortemente em AST (0,6). “como funciona a autenticação” se apoia em vetores semânticos (0,5). “o que depende disso” é quase todo graph (0,7). “encontre ECONNREFUSED” é quase todo trigrama (0,9), com o modelo de embedding completamente desligado — porque similaridade semântica é exatamente a ferramenta errada para uma string exata.
O caminho rápido, e o caminho fundido
Quando o classificador está confiante — pontuação ≥ 0,85 — e a consulta é normal, o Maguyva pula a fusão por completo e roteia direto para a única modalidade dominante. “onde X é definido” não precisa de quatro retrievers; precisa do índice de AST, agora. Esse caminho direto é reportado de volta como fusion_strategy: "direct".
Tudo que é ambíguo passa pela fusão. As quatro (ou três, no preset padrão) modalidades rodam em paralelo, cada uma retornando sua própria lista ranqueada, e nós as combinamos.
Reciprocal Rank Fusion Ponderada
Fundir retrievers heterogêneos é mais difícil do que parece: uma similaridade de cosseno de 0,82, uma pontuação de trigrama de 137 e uma centralidade de grafo de 0,004 não estão na mesma escala, então você não pode simplesmente somá-las. O Reciprocal Rank Fusion contorna o problema descartando as pontuações brutas e mantendo apenas o rank que cada motor atribuiu. A contribuição de um resultado vinda de uma modalidade é:
contribution = weight × 1 / (k + rank + 1)
onde rank é a posição dele na lista daquela modalidade e k é uma constante de suavização. As contribuições são somadas entre modalidades para qualquer resultado que mais de um motor encontrou — a concordância entre retrievers naturalmente sobe ao topo. Usamos k = 40 no preset padrão e 60 em thorough (quick roda apenas com semantic, então a fusão nunca entra em ação ali). O trabalho original de RRF chegou a k = 60 para retrieval de propósito geral; nosso padrão enfatiza um pouco mais as primeiras posições, dando mais peso aos resultados que aparecem bem classificados em várias modalidades — e não recomendamos ajustá-lo manualmente.
Além disso, os resultados carregam um boost de importância de grafo. Um hub — uma função em que todo o codebase se apoia — deve superar em ranking um nó-folha pouco conhecido mesmo com a mesma relevância textual, então multiplicamos cada contribuição por:
boost = min(1 + 0.3 × ln(1 + centrality), 1.5)
A centralidade vem das métricas pré-computadas de PageRank/grau do pipeline, e o boost é limitado a 1,5× para que uma função popular não consiga sepultar completamente uma obscura mais relevante. Por fim, rebaixamos resultados de caminhos de dependências externas, artefatos de build e conteúdo arquivado, e removemos duplicatas mantendo o melhor chunk por arquivo.
O que ainda é imperfeito
O classificador de intenção é uma pilha de regexes, não um modelo aprendido. Ele trata os formatos comuns de consulta com heurísticas, e uma consulta ambígua recorre a understand_code e a uma combinação com maior peso semântico. Uma versão anterior deste artigo descreveu como alcançada uma redução da taxa de consultas sem resultados de aproximadamente 15% para menos de 5%. Na verdade, a proposta de dezembro de 2025 apresentava uma estimativa inicial e uma meta; não foi fornecido um conjunto de consultas nem um registro de medição que sustentasse essa redução. Retiramos essa afirmação de melhoria alcançada; veja as evidências de origem e a correção (em inglês). Não substituímos o classificador por um modelo treinado porque a versão de baixo custo é rápida e boa o suficiente para a função de roteamento pretendida, e porque um classificador errado, mas confiante, é pior do que uma alternativa honesta. Os pesos em si são valores a priori escolhidos manualmente, não aprendidos a partir de dados de cliques que não coletamos.
Faça a Pergunta, Não a Ferramenta
Um agente não deveria precisar saber se deve recorrer ao grep, a embeddings ou ao call graph — ele deveria fazer a sua pergunta em termos simples e obter a resposta certa. A fusão multimodal é o que permite que find_symbol, busca semântica e análise de dependências fiquem por trás de uma única interface de consulta: o sistema lê o formato da pergunta e silenciosamente monta o retriever certo para ela. O modelo que pontua os embeddings importa, mas também importa saber quando não usá-los. Escolher a ferramenta certa para cada consulta é um tipo de qualidade em si, e é algo que preferimos assumir a empurrar para quem chama.
// you bring the question. it brings the tools.
Leituras relacionadas
Mais do log de build do Maguyva
Por Que Atualizamos a Busca de Código para o voyage-4-large_
Migramos nossos embeddings de código para o voyage-4-large — atualmente no topo do ranking público RTEB de retrieval de código. A versão honesta: o trade-off que fazemos, o que realmente indexamos, e por que pagamos por embeddings premium.
Observabilidade de Agentes: Hooks, Alloy e Grafana_
Conectamos o Claude Code e o Codex a uma única stack do Grafana com OpenTelemetry e Alloy, e depois usamos traces e logs para encontrar e corrigir problemas de comportamento dos agentes na origem.
Minerando o Loop: Como Mudanças Se Tornam Memória Institucional_
Commits do Git se tornam entradas estruturadas de changelog e registros de decisões arquiteturais, que depois realimentam agentes de IA como memória institucional consultável.