RAG sobre o seu próprio código: o encanamento que separa o protótipo do produto A developer built ArchLens, a boilerplate for production-grade RAG systems over codebases, addressing common pitfalls like embedding dimension mismatches, LLM provider coupling, and multitenancy. The project includes Java 21/Quarkus and Python/FastAPI backends with static analysis of artifacts and configurable LLM providers. "Vamos colocar uma IA pra responder perguntas sobre o nosso código." A frase parece simples, e um protótipo sai numa tarde: joga uns arquivos num embedding, guarda num índice, faz a busca. Funciona na demo. O problema aparece quando isso vira produto: respostas inconsistentes, vetores que não batem com o banco, cada cliente vendo dados do outro, e um acoplamento total a um provedor de LLM. O que separa o protótipo do produto é o encanamento — e ele é mais chato do que parece. O erro de RAG mais silencioso: você indexa com um modelo de embeddings de 1536 dimensões, depois troca para um de 768, e o banco continua esperando vector 1536 . Nada explode na cara — as buscas só ficam sutilmente erradas . A coluna do banco e o modelo são um contrato . Trate como tal: valide no arranque. Probe de arranque: materializa um embedding e falha cedo se a dimensão divergir { probe-de-arranque-materializa-um-embedding-e-falha-cedo-se-a-dimensão-divergir data-source-line="528"} vetor = embedding provider.embed "amostra" assert len vetor == EMBEDDING DIMENSION, "Dimensão do modelo = vector N no banco" {data-source-line="531"} Falhar no boot é infinitamente melhor que descobrir semanas depois que a busca semântica está degradada. No protótipo, a chamada à OpenAI fica no meio da regra. Aí você quer rodar local para testar sem custo, ou trocar para um modelo self-hosted, e percebe que o provedor está espalhado por toda parte. A saída é uma porta hexagonal : o domínio fala com uma interface; os adaptadores implementam cada provedor. public interface LlmGateway { String complete String prompt ; } // local determinístico, sem custo | openai | ollama — escolhidos por configuração {data-source-line="546"} Com isso, provider=local roda em dev sem chave nem rede, e produção troca para openai ou ollama sem tocar no núcleo . Testes ficam determinísticos; custo de dev vai a zero. Se a ferramenta vai servir mais de um time ou cliente, o isolamento precisa existir desde a ingestão — não dá para "adicionar depois". Cada chunk, cada vetor, cada consulta carrega o tenant id . Uma busca vetorial sem filtro de tenant é um vazamento de dados esperando para acontecer. Embeddings respondem "o que o código diz". Mas um diagnóstico de arquitetura útil também precisa de fatos determinísticos : este endpoint do OpenAPI não tem schema de erro, esta migration não tem rollback, este Dockerfile roda como root, este pipeline não trava em vulnerabilidade. Isso é análise estática de artefatos — código, OpenAPI, migrations, Docker, CI — e ela dá ao relatório evidências rastreáveis , não só texto plausível de LLM. A combinação RAG contextual + análise factual é o que torna o diagnóstico confiável. Chunking, embeddings, pgvector, probe de dimensão, porta de LLM, multitenancy, analisadores de artefatos, geração de ADR com evidência — nada disso é a "feature de IA" que aparece na demo, mas é tudo que faz ela virar produto. São semanas de fundação antes da primeira resposta confiável. Empacotei essa fundação inteira como um boilerplate: backend Java 21/Quarkus + worker Python/FastAPI , com pipeline de RAG chunking, embeddings, pgvector com probe de dimensão , análise estática de artefatos, geração de ADRs com evidências, troca de LLM por configuração local/openai/ollama e multitenancy — rodando via Docker Compose. É o ArchLens . Se você vai construir uma ferramenta de IA sobre código, ele te entrega o encanamento pronto e uma arquitetura limpa para estender. Já apanhou de algum desses pontos montando RAG? Conta nos comentários.