A recuperação ainda vence uma janela de 1M tokens para sua base de código
Algumas semanas atrás eu estava depurando um bug de autenticação em um projeto paralelo — um backend TypeScript, uns 140 arquivos, confortavelmente abaixo de um milhão de tokens. Eu tinha um modelo de 1M tokens na minha frente, então fiz a coisa preguiçosa: joguei toda a árvore src/ em um único prompt e pedi ao agente para encontrar onde o token de sessão era atualizado e por que às vezes não era.
Ele leu tudo. Me custou uma pequena fortuna em tokens de entrada para aquela única rodada. E apontou com confiança para um helper refreshToken que parecia certo mas era o errado — havia dois, em módulos diferentes, e o que ele escolheu não estava no caminho que disparou. Eu tinha dado todo o palheiro e ele pegou uma agulha plausível do meio.
Então fechei aquela sessão, abri uma nova com o mesmo modelo e deixei ele trabalhar do jeito normal: listar a árvore, fazer grep de refreshToken, ler os três arquivos que corresponderam, seguir as importações. Ele encontrou o bug real em cerca de um quarto dos tokens e um terço do tempo de relógio. Mesmo modelo. Mesmo repositório. A diferença estava completamente em como eu tinha alimentado o código.
A versão curta: para uma base de código real, despejar tudo em uma janela de contexto enorme geralmente é a pior jogada — não porque a janela seja pequena demais, mas porque os modelos ficam mensuravelmente menos confiáveis conforme você a preenche, e código é o pior input para esse tipo de falha. Recuperação direcionada — grep, indexação de símbolos, leitura consciente de dependências — vence em custo, latência e precisão para a maioria das tarefas de código. O contexto longo vence em alguns casos específicos, e vou te dar a regra no final.
O número da janela é real; a parte utilizável não é
Primeiro, a parte que é verdade: os números grandes existem. Em junho de 2026, os três principais modelos de codificação — Claude Opus 4.8, GPT-5.5 e Gemini 3.1 Pro — todos vêm com janela de 1M tokens, e mais de treze modelos frontier hospedados anunciam 1M ou mais (comparação de janelas de contexto da Morph). Anthropic e OpenAI até eliminaram a taxa adicional por contexto longo, então agora a cobrança é pela mesma taxa por token em toda a janela.
O problema é que anunciado e utilizável são números diferentes. Os testes independentes de recall em 2026 continuam mostrando que a capacidade efetiva fica bem abaixo do rótulo — uma revisão coloca o contexto efetivo em cerca de 50–65% do nominal, e reporta pontuações MRCR multi-agulha que despencam na segunda metade da janela: Gemini 3.1 Pro mantendo ~84,9% de recall na faixa de 128K–256K e colapsando para 26,3% na faixa de 512K–1M (análise "context window lie" da CodingFleet). Trate os percentuais exatos como indicativos — são números de benchmark de fornecedores, não um estudo controlado — mas o formato é consistente em todo lugar: o recall cai antes de a janela estar cheia.
Essa queda tem nome e histórico de pesquisa, e importa mais para código do que para quase qualquer outra coisa.
Por que código é o pior input para uma janela cheia
Dois efeitos bem replicados trabalham contra você quando você enfia um repositório em um único prompt.
Perdido no meio. O artigo de Liu et al. (publicado na TACL) mostrou que a precisão do modelo é uma função em U de onde a informação relevante está: maior quando está no início ou no final do contexto, e degradando mais de 30% quando está enterrada no meio — e isso se manteve em seis famílias de modelos em QA multi-documento e recuperação de pares chave-valor. Uma base de código não é nada mais que "informação relevante no meio". A função que você precisa raramente é o primeiro ou último arquivo que você colou.
Apodrecimento de contexto. O estudo de 2025 da Chroma testou 18 modelos frontier — Claude Opus 4 / Sonnet 4 / 3.7 / 3.5 / Haiku 3.5, OpenAI o3 e a família GPT-4.1, Gemini 2.5 Pro/Flash, e três tamanhos de Qwen3 — e descobriu que a performance cai conforme o comprimento do input cresce mesmo em tarefas triviais como encontrar uma string ou replicar palavras repetidas (relatório "Context Rot" da Chroma). A descoberta que deveria deixar todo desenvolvedor de agentes de código desconfortável: na tarefa LongMemEval, todo modelo se saiu pior com o histórico completo de ~113k tokens do que com um trecho focado de ~300 tokens do mesmo conteúdo relevante. Mais tokens do material certo, apresentados em bloco, pontuaram mais baixo do que uma pequena fatia curada.
Tem um detalhe naquele relatório que é quase engraçado se você escreve código: os modelos pontuaram pior quando o palheiro "preservava um fluxo lógico de ideias" do que quando estava embaralhado. Um repositório é maximamente estruturado logicamente — importações, grafos de chamadas, definições de tipos, módulos ordenados. Você está dando ao modelo exatamente a forma de input que ele lida pior.
E código pune a falha mais duramente do que prosa. Se um chatbot perde uma frase de um longo documento, dá uma resposta um pouco mais vaga. Se um agente de código perde a definição real de refreshToken, escreve código que chama um método que não existe ou corrige a função errada — e faz isso com confiança. Como apontou uma análise de recuperação de agentes: "o modo de falha do RAG em contextos de codificação é silencioso e cumulativo" (MindStudio). Uma recuperação errada não gera erro. Ela vai para produção.
O que "recuperação" significa para código (na maioria das vezes não são embeddings)
Quando as pessoas ouvem "recuperação" imaginam um banco de dados vetorial. Para código, os agentes que realmente vão para produção — Claude Code, Cursor, Devin — na maioria das vezes não recorrem a ele primeiro. Eles navegam do jeito que um desenvolvedor experiente faria: olhar para a árvore de arquivos, fazer grep de um símbolo, ler os arquivos específicos que corresponderam, seguir a cadeia de importações (MindStudio). A ferramenta de busca principal do Claude Code é ripgrep — correspondência de texto exata, rápida, com número de linha, que funciona em qualquer repositório sem pré-processamento.
Há uma boa razão para os vetores não serem o padrão para código, e não é nostalgia. Um enquadramento útil é de três camadas, escalando apenas quando necessário (busca de código para agentes de IA):
- Lexical (ripgrep): correspondência exata, milissegundos, gitignore-aware. Seu padrão.
- Estrutural (ast-grep): correspondência de padrões AST — "encontre toda função async," "todo catch-and-rethrow" — coisas que regex não consegue expressar de forma limpa. Escale aqui quando a consulta é sobre forma, não sobre strings.
- Semântico (embeddings / repo-map): consultas conceituais em linguagem natural. Último recurso.
Por que semântico é o último recurso e não o primeiro? Porque as consultas que um agente de código realmente gera são curtas e têm formato de símbolo — auth flow, user service, refreshToken — e esse é exatamente o formato que quebra a busca por embeddings. O benchmark CoREB descobriu que consultas curtas por palavras-chave "colapsam quase todo modelo semântico testado para próximo de nDCG@10 zero". Embeddings capturam similaridade de significado; código é um grafo de dependências, importações e definições de tipos, e similaridade não é a relação que você está consultando. Duas funções podem ser semanticamente quase idênticas e estruturalmente não relacionadas; a que você precisa é a que está no caminho de chamada, não a que lê de forma semelhante.
Isso não torna a indexação semântica inútil — a torna uma especialista. Quando ela merece seu lugar, as boas implementações não fazem embedding de janelas de texto arbitrárias. Elas fazem parse com tree-sitter em unidades reais — funções, classes, interfaces com seus docstrings intactos — mantêm esses pedaços inteiros, e combinam similaridade vetorial com correspondência de palavras-chave BM25 mais ferramentas de grafo de chamadas para rastreamento de dependências e análise de raio de impacto (opencode-codebase-index). Esse é o jogo completo para recuperação de código: chunking consciente de dependências nos limites de símbolo com fallback de palavras-chave — não "divida o arquivo a cada 512 tokens e torça".
A matemática de custo e latência que ninguém faz
Deixe a precisão de lado por um segundo e apenas calcule a conta. Digamos que um repositório médio tem 600K tokens. Jogá-lo em uma única rodada significa pagar 600K tokens de entrada em cada rodada da conversa a menos que você esteja fazendo cache cuidadosamente — e mesmo com cache, você carrega a latência do modelo prestando atenção em tudo isso. Um passe de recuperação em vez disso envia a árvore (alguns KB), um resultado de grep (algumas centenas de tokens) e talvez quatro arquivos que você realmente lê (~15K tokens). Essa não é uma diferença de arredondamento; é uma a duas ordens de magnitude por rodada, multiplicada por cada rodada em uma sessão de depuração.
Latência segue a mesma curva. Um modelo raciocinando sobre 600K tokens é mais lento no primeiro token e no geral do que o mesmo modelo raciocinando sobre 18K. Em um loop interativo onde você está fazendo vinte rodadas para perseguir um bug, isso se acumula em minutos de espera perdida. Recuperação mantém cada rodada pequena, então cada rodada é rápida, então o loop fica apertado.
E há um custo mais silencioso: orçamento de atenção. Todo arquivo irrelevante na janela é um distrator, e Chroma mostrou que distratores arrastam a precisão para baixo de forma mensurável — um distrator prejudicou, quatro o amplificaram. Um repositório completo em contexto são milhares de distratores para qualquer pergunta dada. Você não está apenas pagando mais para ser mais lento; você está pagando mais para ser mais lento e mais impreciso.
Quando o contexto longo realmente vence
Isso não é "recuperação sempre". Há um intervalo real onde jogar tudo é a decisão certa, e é definido pelo tamanho do repositório e formato da tarefa, não pelo tamanho da janela pela qual você está pagando.
O contexto longo vence quando:
- O repositório inteiro genuinamente cabe com folga — digamos abaixo de ~150K tokens, bem dentro da frente de alto recall da janela — e a tarefa precisa de raciocínio entre arquivos que é difícil de recuperar em pedaços. Um repositório pequeno onde tudo importa é exatamente o caso que a recuperação complica demais.
- Você precisa de raciocínio de arquivo inteiro ou módulo inteiro em uma única passada — um único arquivo grande que você quer refatorar completamente, uma leitura de segurança do início ao fim de um módulo, uma revisão arquitetural onde o modelo precisa manter as relações em vista de uma vez. Recuperação fragmenta isso; contexto preserva.
- É uma passada única e somente leitura, não um loop iterativo. Uma única chamada "revise este PR" ou "resuma como esse serviço funciona" amortiza o custo em uma rodada. A economia que mata o contexto longo — pagar a janela inteira em cada rodada — não morde quando há apenas uma rodada.
Recuperação vence em todo o resto, que na prática é a maior parte do trabalho: qualquer repositório grande demais para caber confortavelmente, qualquer coisa iterativa, qualquer coisa onde você precisa de uma definição específica em vez de uma compreensão geral, e qualquer coisa onde uma recuperação silenciosamente errada enviaria código quebrado para produção.
Uma regra aproximada que eu realmente uso: se a fatia relevante da tarefa é menor que o repositório, recupere. Se a tarefa é o repositório inteiro e o repositório cabe nos primeiros ~15% da janela, carregue. Na maioria dos dias, a fatia é menor que o repositório.
A conclusão
A janela de 1M tokens é uma capacidade real, e vale a pena usar os preços sem sobretaxa para os trabalhos nos quais ela é boa — revisões de uma única passada de código que cabe, raciocínio de módulo inteiro, o repositório pequeno ocasional onde tudo é relevante. Mas não é um substituto para saber onde seu código vive. Para um agente de código que funciona, a pilha entediante — listar a árvore, fazer grep do símbolo, ler os quatro arquivos que corresponderam, seguir as importações, escalar para AST ou embeddings apenas quando a busca por palavras-chave retornar vazia — é mais barata, mais rápida e mais precisa do que a impressionante. O número grande na folha de especificações é uma opção, não uma instrução. Na maioria das vezes, não a preencha.
