Pular para o conteúdo
Eduardo Trindade
← Voltar para projetos

2026 · Open source · Java + React + IA

Codebase Copilot

Pergunte qualquer coisa sobre um repositório e receba uma resposta em que cada afirmação aponta para o arquivo e as linhas de onde saiu.

  • Java 25
  • Spring Boot 4.1
  • PostgreSQL 17 + pgvector
  • React 19
  • TypeScript
  • Vite
  • TanStack Query
  • Radix UI
  • OpenAI SDK

Contexto e problema

Entrar num projeto desconhecido significa descobrir onde estão as regras de negócio, como um fluxo atravessa as camadas e quais módulos fazem o quê. Perguntar a um modelo de linguagem sem contexto controlado produz respostas convincentes, mas sem como conferir.

O Codebase Copilot indexa o repositório na máquina de quem usa, recupera os trechos relevantes para cada pergunta e só aceita uma resposta cujas citações apontem para trechos que o modelo de fato recebeu. Toda afirmação pode ser conferida no código com um clique. Quando não há evidência suficiente, a ferramenta diz que não sabe.

Por que RAG

Um modelo de linguagem responde com o que aprendeu no treino. Ele nunca viu o seu repositório, então qualquer resposta sobre ele é um palpite bem escrito.

RAG (Retrieval-Augmented Generation, ou Geração Aumentada por Recuperação) muda a pergunta que o modelo responde. Em vez de "o que você sabe sobre este código?", passa a ser "com base nestes trechos, o que dá para afirmar?". A cada pergunta, o sistema:

  1. recupera os trechos do repositório mais relevantes;
  2. entrega esses trechos ao modelo, numerados como evidências;
  3. exige que cada afirmação cite a evidência de onde saiu.

É a diferença entre uma prova de memória e uma prova com consulta em que cada resposta precisa indicar a página do livro. O modelo continua escrevendo o texto, mas a base de verdade passa a ser o próprio código. Neste projeto, cada etapa (indexação, recuperação, montagem do contexto e validação das citações) é implementada de forma explícita, testada e medida.

Arquitetura

Pipeline do Codebase CopilotImportar → Pré-análise → Indexação → PostgreSQL + pgvector → Busca híbrida → Fusão RRF e contexto → LLM → Validação de citações → Resposta com arquivo e linhas01Importar02Pré-análise03Indexação04PostgreSQL + pgvector05Busca híbrida06Fusão RRF e contexto07LLM08Validação de citações09Resposta com arquivo elinhas

A primeira linha acontece uma vez por versão do repositório. A segunda, a cada pergunta.

  • Importar: a aplicação lê apenas pastas dentro das raízes autorizadas, ou extrai um ZIP com limites contra zip bombs e path traversal.
  • Pré-análise: mostra antes de indexar o que entra, as linguagens, o tamanho e o que foi excluído (dependências, artefatos gerados, .env, chaves e binários).
  • Indexação: os arquivos viram chunks com caminho e linhas, termos de identificador e vetores, guardados num snapshot imutável. Reindexar sem mudanças não reprocessa nada.
  • Busca híbrida: três canais. O lexical usa um tokenizador de identificadores (getUserById → get user by id). O vetorial usa pgvector. O de caminhos encontra arquivos citados pelo nome.
  • Fusão e contexto: os rankings são combinados por Reciprocal Rank Fusion (k = 60) e os trechos escolhidos entram num orçamento de tokens, com os rótulos E1…En.
  • Validação de citações: o modelo responde num formato estruturado estrito, e cada citação é conferida contra as evidências e as linhas que ele recebeu antes de chegar à tela.

O backend é um monólito modular em Java 25 e Spring Boot 4.1, com módulos de workspace, importação, indexação, recuperação, respostas, provedores e histórico. O frontend é React 19 com Vite e TypeScript, e os tipos da API são gerados do contrato OpenAPI.

Decisões técnicas e trade-offs

Monólito modular, não microsserviços

Menos complexidade operacional para um produto que roda na máquina de quem usa. A separação em módulos e portas internas permite extrair um worker no futuro, se as medições mostrarem necessidade. A indexação usa uma fila em tabela com SKIP LOCKED, heartbeat e limite de tentativas: um job interrompido é retomado e chega ao mesmo resultado de uma execução sem falha.

Provedores substituíveis

Embeddings e geração ficam atrás de portas internas (EmbeddingProvider, GenerationProvider). O SDK oficial openai-java só existe dentro do módulo de provedores, e um teste de arquitetura (ArchUnit) garante que os tipos dele não vazam. O Spring AI chegou a ser usado e saiu: o modelo de chat dele confundia tokens ausentes com zero e podia registrar o prompt em log.

Local-first

Com os provedores desligados (none, o padrão), nada sai do computador. Com um provedor remoto, só vão os trechos selecionados como evidência e o texto das perguntas, nunca arquivos excluídos.

Orçamento de tokens

O contexto é montado na ordem da fusão: até 8 evidências por padrão, até 3 trechos por conteúdo, sem sobrepor linhas já escolhidas e dentro de 6.000 tokens estimados no prompt final. Um trecho que não cabe é pulado e os seguintes ainda são tentados. Não há reranking: ele só entra se a avaliação mostrar que compensa o custo e a latência.

SQL explícito na recuperação

Spring Data JDBC para agregados pequenos e JdbcClient com SQL explícito para indexação e busca, sem JPA. O ranking híbrido precisa ser observável e testável, e abstrações genéricas esconderiam esse comportamento.

Segurança e privacidade

O repositório importado é tratado como conteúdo não confiável: pode ter segredos, código malicioso, instruções de prompt injection, arquivos gigantes ou caminhos malformados.

  • Nenhum código importado é executado, em nenhuma etapa. O importador lê texto e não roda scripts, ferramentas de build nem hooks.
  • Leitura restrita: caminhos são canonicalizados e validados dentro da raiz, links simbólicos não são seguidos e há limites de quantidade, profundidade e tamanho. ZIPs são verificados contra path traversal, caminhos absolutos e expansão excessiva, contando os bytes realmente gravados.
  • Segredos: arquivos sensíveis são excluídos pelo nome, em regras que não podem ser desativadas, e por conteúdo (blocos de chave privada e chaves de formatos publicados), antes de qualquer chunk, embedding ou envio.
  • Prompt injection: cada evidência vai num bloco delimitado por um nonce aleatório de 128 bits por pedido, então um arquivo não consegue fechar o próprio bloco. A geração não tem ferramentas, a saída é JSON estrito e o backend decide o status final.
  • API restrita à própria máquina: o backend escuta em 127.0.0.1, recusa outros hosts e origens e envia Content Security Policy estrita.
  • Logs sem conteúdo: chaves de API, código e prompts não aparecem em logs nem em mensagens de erro.

Como medi a qualidade

A avaliação existe antes de qualquer otimização de prompt ou troca de modelo. O corpus tem 80 perguntas sobre quatro repositórios (uma fixture sintética, o próprio Codebase Copilot, pallets/click e honojs/hono), metade em português e metade em inglês. Catorze exigem abstenção, e outras catorze testam injeção de instruções ou vazamento de segredos.

Harnesses próprios medem:

  • Recuperação: Recall@k, MRR@10, Precision@10 e cobertura de arquivos em perguntas que atravessam módulos.
  • Respostas: validade das citações, respostas sustentadas por ao menos uma citação válida, abstenção correta e indevida, e vazamento de texto proibido.
  • Operação: tokens, custo estimado e latência média e p95.

O primeiro baseline publicado é o do canal lexical, em 66 perguntas com intervalo esperado. Ele virou piso de não regressão: nenhuma mudança é promovida se o resultado cair abaixo dele.

CanalPerguntasRecall@10MRR@10
Lexical (todos os corpora)660,4920,291

As metas de desempenho, medidas com provedores simulados, foram atendidas:

MetaMedido
Busca híbrida p95 ≤ 1 s com 50 mil chunks676 ms com cerca de 46 mil chunks
Pergunta sem o tempo do provedor ≤ 1 s com 50 mil chunks620 ms
Reindexação sem mudanças < 10% da completa3,1 s contra 41–45 s (7–8%)

A comparação dos canais vetorial e híbrido e a avaliação das respostas com o modelo real ainda não rodaram. Elas saem do mesmo harness, sem código novo. Até lá, duas garantias valem por construção e por teste: nenhuma citação inválida é aceita em silêncio, e nenhum conteúdo excluído é enviado ao provedor.

Telas

  • Resposta com evidências — Citações validadas, ranks da busca e o trecho no código.
  • Visão geral do projeto — Snapshot ativo, linguagens, indexações e perguntas.
  • Arquivos — A árvore do snapshot exatamente como foi indexada.
  • Histórico — Conversas guardadas para retomar e auditar.

Próximos passos

  • Rodar a avaliação dos canais vetorial e híbrido e das respostas com o modelo real, seguida de auditoria humana, e definir as metas a partir dessa medição.
  • Adaptadores de análise estrutural (AST) para as linguagens prioritárias.
  • Comparação entre snapshots.
  • Exportação de um mapa arquitetural.
  • Provedor local de modelos.