Reduza contexto LLM em 95% para código com AST + BM25

Reduza contexto LLM em 95% para código com AST + BM25

O problema que todo dev que usa LLM conhece

Você já tentou usar um assistente de código numa base grande? O modelo pede contexto, você joga arquivos inteiros, a janela de contexto enche, a latência dispara e a resposta ainda vem errada porque faltou uma dependência num arquivo lá no fundo. Isso é o que chamamos de "perda de sinal estrutural": similaridade semântica entre chunks de texto não captura que a função A chama o tipo B definido em C. O resultado é contexto inchado e resposta rasa.

O que foi proposto

Um desenvolvedor compartilhou no Reddit uma abordagem que ataca exatamente esse gargalo. Em vez de chunking e embeddings, ele constrói um grafo tipado a partir da AST (Abstract Syntax Tree) de cada arquivo usando Tree-sitter. Nós do grafo são funções, classes, interfaces, tipos, módulos. Arestas são imports, exports, chamadas, herança, composição. Esse grafo é persistido em SQLite – custo único de parse por projeto.

Na hora da consulta, ao invés de embedded similarity, ele roda BM25 sobre metadados dos nós: nomes, assinaturas, docstrings, caminhos. Os nós mais pontuados alimentam o LLM. E como o grafo guarda as arestas, uma função recuperada puxa automaticamente suas dependências diretas via travessia. Resultado empírico: ~5K tokens por consulta em codebases médias-grandes que antes exigiam ~100K com abordagem ingênua.

Como funciona na visão de operador

A arquitetura tem três etapas. Primeiro: parse de cada arquivo com Tree-sitter – ferramenta madura, suporta várias linguagens, custo O(n). Segundo: grafo tipado armazenado em SQLite – barato, portável, sem dependência de vector DB. Terceiro: BM25 nos metadados dos nós – algoritmo fast, sem custo de inferência de embedding. A mágica está em que BM25 é excelente para matching de tokens exatos (nomes, tipos), justamente o que código usa. Embeddings são bons para semântica difusa, mas em código o nome da função é o principal sinal.

Para queries complexas de múltiplos arquivos, o autor sugere um fallback hierárquico: (1) diagrama Mermaid do grafo completo sempre em contexto como mapa arquitetural; (2) BM25 para lookup direcionado; (3) quando a capacidade de contexto chega a 70%, um modelo rápido comprime nós menos relevantes antes de passar ao modelo principal. Isso lembra uma técnica de compressão seletiva de contexto, mas com vantagem estrutural.

O que isso muda na prática

Quem ganha? Qualquer time que mantém codebase grande e quer usar LLM para tarefas como refatoração, code review automatizado, documentação. A redução de tokens implica menos custo de API e menor latência. Quem perde? Abordagens puramente baseadas em embedding – se o código tem nomes significativos, BM25 pode ser mais preciso. Também perde quem depende de vector DB caro – aqui um SQLite resolve.

Ação prática: Se você usa RAG para código, teste substituir a etapa de retrieval de chunks por um grafo AST + BM25. O Tree-sitter tem bindings para Python, JS, Rust. O custo de implementação não é trivial, mas o ganho de performance pode ser enorme.

Tensão: escalabilidade real?

A abordagem é inteligente, mas levanta dúvidas: O parse único de AST escala para codebases de milhões de linhas? Sim, mas o grafo pode ficar grande – o autor usou SQLite, mas consultas com travessia de arestas podem ficar lentas sem índices adequados. Além disso, BM25 não captura sinônimos ou variações de nomenclatura – se o time não tem padrão de nomes, a recall pode cair. E o fallback com compressão de nós em 70% da janela? Depende de um modelo rápido de compressão, que pode perder detalhes. No fim, a abordagem resolve o gargalo de contexto, mas move o problema para a qualidade do parsing e a manutenção do grafo.

Conclusão

Reduzir o contexto de 100K para 5K tokens não é só economia – é viabilizar consultas que antes eram impossíveis por estouro de janela. A técnica de AST + BM25 é um bom trade-off entre custo e precisão para código. Mas fica a pergunta: será que a comunidade vai adotar isso ou continuará insistindo em embeddings genéricos?

Fonte original: Reddit r/MachineLearning

Compartilhe este artigo

Comentários (0)

Nenhum comentário ainda. Seja o primeiro a comentar!

Deixe seu comentário