Phase 19 - Lesson 02

Capstone 02 — RAG over Codebase (Cross-Repo Semantic Search)

Em 2026, qualquer organização de engenharia madura executa uma busca de código interna que compreende o significado, e não apenas correspondências de strings. Sourcegraph Amp, busca em bases de código do Cursor, grafo empresarial da Augment, repomap do Aider, MCP interno do Pinterest — todos seguem o mesmo padrão. Ingerir múltiplos repositórios, analisar com tree-sitter, gerar embeddings de trechos baseados em funções e classes, realizar busca híbrida, reordenar (re-rank), e responder com citações. Este projeto prático (capstone) desafia você a construir uma ferramenta que suporte 2 milhões de linhas de código em 10 repositórios e sobreviva a reindexações incrementais a cada git push.

Type: Capstone Languages: Python (ingestion), TypeScript (API + UI) Prerequisites: Phase 5 (NLP foundations), Phase 7 (transformers), Phase 11 (LLM engineering), Phase 13 (tools), Phase 17 (infrastructure) Phases exercised: P5 · P7 · P11 · P13 · P17 Time: 30 hours

Problem

Em 2026, todo agente de programação de fronteira é distribuído com uma camada de recuperação de base de código, pois apenas as janelas de contexto não resolvem dúvidas que envolvem múltiplos repositórios. O contexto de 1 milhão de tokens do Claude ajuda, mas não elimina a necessidade de uma recuperação ordenada e relevante. A busca por cosseno ingênua sobre blocos de texto puros envenena os resultados em códigos gerados automaticamente, em duplicidades de monorepositórios e na cauda longa de símbolos raramente importados. A solução em produção é a busca híbrida (densa + BM25) sobre blocos estruturados de forma ciente da AST (árvore de sintaxe abstrata) com um reordenador (re-ranker), apoiada por um grafo de referências de símbolos.

Você aprenderá isso ao indexar uma frota real de repositórios — e não um repositório fictício de tutorial — e medir MRR@10, fidelidade das citações e o frescor da indexação incremental. Os modos de falha são de nível de infraestrutura: um monorepositório de 100 mil arquivos, um push que altera metade dos arquivos, uma consulta que precisa cruzar quatro repositórios para responder corretamente.

Concept

Uma pipeline de ingestão ciente da AST analisa cada arquivo com o tree-sitter, extrai nós de funções e classes e realiza a divisão em blocos (chunking) nos limites dos nós, em vez de usar janelas fixas de tokens. Cada bloco recebe três representações: um embedding denso (Voyage-code-3 ou nomic-embed-code), termos esparsos BM25 e um pequeno resumo em linguagem natural. O resumo adiciona uma terceira modalidade recuperável — os usuários perguntam "como X é autorizado" e o resumo menciona "authz", mesmo que o código utilize apenas check_permission.

A recuperação é híbrida. Uma consulta dispara buscas densas e esparsas (BM25) em paralelo, mescla os melhores resultados e envia essa união para um reordenador de codificação cruzada (cross-encoder re-ranker, como Cohere rerank-3 ou bge-reranker-v2-gemma-2b). A lista reordenada é fornecida para um sintetizador de contexto longo (Claude Sonnet 4.7 com cache de prompt ou Llama 3.3 70B hospedado localmente) com instruções para citar cada afirmação por arquivo e intervalo de linhas. Respostas sem citações são rejeitadas por um pós-filtro.

O frescor incremental é o grande desafio de infraestrutura. Um git push dispara um webhook de diff indicando quais arquivos e símbolos mudaram. Apenas os blocos afetados passam pelo processo de geração de novos embeddings. As arestas de símbolos compartilhados entre arquivos (importações, chamadas de métodos) são recalculadas. O índice permanece consistente sem a necessidade de reprocessar as 2 milhões de linhas de código a cada commit.

Architecture

git push --> webhook --> ingest worker (LlamaIndex Workflow)
                           |
                           v
             tree-sitter parse + AST chunk
                           |
            +--------------+----------------+
            v              v                v
          dense        BM25 index       summary (LLM)
        (Voyage / bge)  (Tantivy)        (Haiku 4.5)
            |              |                |
            +------> Qdrant / pgvector <----+
                            |
                            v
                      symbol graph (Neo4j / kuzu)
                            |
   query --> LangGraph agent (retrieve -> rerank -> synth)
                            |
                            v
                 Claude Sonnet 4.7 1M context
                            |
                            v
                 answer + file:line citations

Stack

  • Análise sintática: tree-sitter com gramáticas para 17 linguagens (Python, TS, Rust, Go, Java, C++, etc.)
  • Embeddings densos: Voyage-code-3 (hospedado) ou nomic-embed-code-v1.5 (hospedagem local), fallback para bge-code-v1
  • Índice esparso: Tantivy (escrito em Rust) com BM25F, com pesos diferenciados por campos (nome do símbolo vs corpo)
  • Banco de dados vetorial: Qdrant 1.12 com busca híbrida, ou pgvector + pgvectorscale para equipes com menos de 50 milhões de vetores
  • Modelo de resumo de blocos: Claude Haiku 4.5 ou Gemini 2.5 Flash, com cache de prompt
  • Reordenador (re-ranker): Cohere rerank-3 or bge-reranker-v2-gemma-2b hospedado localmente
  • Orquestração: LlamaIndex Workflows para ingestão, LangGraph para o agente de consulta
  • Sintetizador: Claude Sonnet 4.7 (contexto de 1M) com cache de prompt
  • Grafo de símbolos: Neo4j (gerenciado) ou kuzu (embarcado) para arestas de importação e chamada
  • Observabilidade: spans do Langfuse por etapa de recuperação + síntese

Build It

  1. Varredor de ingestão. Percorra o histórico do git a cada webhook de push. Colete os arquivos alterados. Para cada arquivo, analise com o tree-sitter, extraindo nós de funções e classes com seus respectivos intervalos de linhas de origem. Emita registros de blocos contendo {repo, path, start_line, end_line, symbol, body}.

  2. Resumidor de blocos. Agrupe os blocos em chamadas para o Haiku 4.5 com cache de prompt no preâmbulo do sistema. Prompt: "Resuma esta função em uma frase, indicando seu contrato público e efeitos colaterais." Armazene o resumo junto ao bloco de código correspondente.

  3. Pool de embeddings. Duas filas paralelas: densa (lotes de 128 com Voyage-code-3) e resumo (mesmo modelo, porém sobre a string do resumo). Grave os vetores no Qdrant com os metadados {repo, path, start_line, end_line, symbol, kind}.

  4. Índice BM25. Índice Tantivy com pesos por campo: peso 4 para nome do símbolo, peso 1 para o corpo do símbolo, peso 2 para o resumo. Isso permite consultas do tipo "encontre a função chamada X" junto com "encontre a função que faz X".

  5. Grafo de símbolos. Para cada bloco, registre as arestas correspondentes: importações (este arquivo usa o símbolo Y do repositório Z), chamadas (esta função chama o método M na classe C), herança. Armazene no kuzu. Utilizado no momento da consulta para expandir a busca além das fronteiras de um único repositório.

  6. Agente de consulta. Estrutura LangGraph com três nós. retrieve dispara buscas densas e BM25 em paralelo, deduplicando por (repo, path, symbol). rerank executa a ordenação cruzada (cross-encoder) nos top-50 resultados e mantém os top-10. synth chama o Claude Sonnet 4.7 com os blocos reordenados no contexto, utiliza cache de prompt e exige citações no formato arquivo:linha.

  7. Validação de citações. Analise a saída do modelo; qualquer alegação sem uma âncora no formato (repo/path:start-end) é sinalizada para reprocessamento ou descartada. Retorne apenas respostas devidamente citadas ao usuário.

  8. Reindexação incremental. Em cada webhook, calcule a diferença (diff) no nível do símbolo. Gere novos embeddings apenas para blocos cujo texto foi alterado. Recalcule as arestas de símbolos para blocos cujas importações foram modificadas. Métrica: um push de 50 arquivos deve ser reindexado em menos de 60 segundos para uma frota de 2M de linhas de código.

  9. Avaliação. Rotule 100 perguntas multirrepositório com suas respectivas respostas padrão no formato arquivo:linha. Meça MRR@10, nDCG@10, fidelidade das citações (proporção de alegações com âncoras verificáveis) e latência p50/p99.

Use It

$ code-rag ask "how is S3 multipart abort wired into our retry budget?"
[retrieve]  12 chunks dense + 7 chunks bm25, 16 unique after dedup
[rerank]    top-5 kept (cohere rerank-3)
[synth]     claude-sonnet-4.7, cache hit rate 68%, 2.1s
answer:
  Multipart aborts are triggered by `AbortMultipartOnFail` in
  services/uploader/retry.go:122-148, which decrements the per-bucket
  retry budget defined in config/budgets.yaml:34-51 ...
  citations: [services/uploader/retry.go:122-148, config/budgets.yaml:34-51,
              libs/s3client/multipart.ts:44-61]

Ship It

A habilidade entregável é detalhada em outputs/skill-codebase-rag.md. Dado um conjunto de repositórios, ela monta a pipeline de ingestão, o índice híbrido e o agente de consulta, e retorna respostas citadas para qualquer dúvida que cruze os repositórios. Critérios de avaliação:

Weight Criterion How it is measured
25 Retrieval quality MRR@10 e nDCG@10 em um conjunto de teste de 100 perguntas
20 Citation faithfulness Proporção de alegações nas respostas contendo âncoras arquivo:linha válidas
20 Latency and scale Latência de consulta p95 em 10k QPS no tamanho do corpus indexado
20 Incremental indexing correctness Tempo desde o git push até que as alterações sejam localizáveis em um commit de 50 arquivos
15 UX and answer formatting Citações clicáveis, pré-visualização de trechos de código e facilidade de perguntas de acompanhamento

Exercises

  1. Substitua o Voyage-code-3 pelo nomic-embed-code hospedado localmente. Meça a diferença no MRR@10. Relate se a diferença diminui quando a reordenação (re-ranking) está ativada.

  2. Injete 20% de código gerado por IA (código repetitivo gerado por LLMs) no corpus de código e refaça a avaliação. Observe o envenenamento da recuperação de dados. Adicione uma tag "gerado" nos metadados e reduza o peso desses resultados na busca.

  3. Faça um benchmark da busca híbrida do Qdrant versus pgvector + pgvectorscale no tamanho real de sua base de código. Relate a latência p99 para um tamanho de lote igual a 1.

  4. Adicione uma verificação semanal contra desvios de desempenho: reexecute semanalmente a avaliação das 100 perguntas. Dispare alertas caso ocorra queda de MRR@10 > 5%.

  5. Estenda o sistema para resolução de símbolos multilinguagem: uma função em Python que chama um serviço em Go via gRPC. Utilize o grafo de símbolos para mapear essa conexão.

Key Terms

Term What people say What it actually means
AST-aware chunking "Divisão em nível de função" Divisão do código nos limites dos nós estruturais do tree-sitter em vez de limites de janelas de tokens fixas
Hybrid search "Densa + esparsa" Execução paralela de buscas vetoriais e BM25, mesclando e reordenando os melhores resultados
Cross-encoder rerank "Classificação de segunda etapa" Modelo que avalia conjuntamente o par (consulta, candidato), oferecendo maior precisão do que a similaridade por cosseno simples
Prompt caching "Cache de prompt do sistema" Funcionalidade do Claude / OpenAI que desconta até 90% do custo de tokens repetidos no início do prompt
Symbol graph "Grafo de código" Mapeamento de conexões de importações, chamadas de funções e heranças entre arquivos e repositórios
Citation faithfulness "Taxa de aterramento da resposta" Proporção de afirmações que o usuário pode verificar diretamente clicando no link e lendo o trecho referenciado
Incremental re-index "Tempo de disponibilização na busca" Tempo decorrido desde o git push até que as alterações nos símbolos estejam disponíveis para consulta

Further Reading

0 lifetime access. Curriculum based on AI Engineering from Scratch by Rohit Ghumare (MIT, used under attribution).