Como o harness decide o que entra na janela de contexto?
O harness remonta a janela de contexto do zero a cada iteração, seguindo uma ordem de prioridade e um orçamento. Instruções de sistema, catálogo de ferramentas e configuração do projeto entram sempre. O pedido do usuário entra sempre. Histórico e saídas de ferramenta entram até um limiar de ocupação, e a partir dele o harness começa a cortar, sempre pelo mais barato de recuperar.
Nada disso é escolha do modelo. Ele recebe uma sequência de tokens já montada e não sabe o que ficou de fora. Como distribuir o espaço da janela é a decisão por trás dessa montagem, e num agente ela roda dezenas de vezes por tarefa em vez de uma vez por prompt.
A diferença entre um agente que termina a tarefa e um que se perde na iteração 15 costuma estar aqui. Duas alavancas fazem a maior parte do trabalho: trazer o conteúdo só quando ele for preciso e resumir o que já passou quando não houver mais espaço. A primeira evita o problema; a segunda administra o problema depois de ele existir.
O orçamento, repartido
Os números da figura vêm de uma tarefa de agente de código de porte médio numa janela de 200 mil tokens. Eles são plausíveis, não universais: meça os seus, que é uma linha de código.
Instruções de sistema (9k). O comportamento base do agente. Reenviado inteiro em toda chamada e quase sempre maior do que quem escreveu imagina.
Catálogo de ferramentas (18k). Nome, descrição e schema de cada ferramenta disponível. Também reenviado inteiro. É a parte que mais cresce quando alguém conecta servidores novos, e a que mais se beneficia de carregar sob demanda.
Configuração do projeto. Convenções, comandos de build, o que não mexer. Entra junto do sistema e vale o mesmo cuidado: o que fica no arquivo de projeto é custo fixo em toda iteração da vida do repositório.
Tarefa (2k). O que a pessoa pediu. É o menor segmento da figura e o único que existe por causa do usuário. Vale olhar para essa proporção uma vez.
Histórico (35k). Turnos anteriores, com os pedidos de ferramenta e o raciocínio entre eles. Cresce devagar e de forma previsível.
Saídas e arquivos (96k). O que voltou de cada comando e o que foi lido do
disco. Cresce em degraus, não em curva: um grep sem limite ou uma suíte de
teste falhando adicionam 30 mil tokens de uma vez.
Folga (40k). O espaço que precisa sobrar para a resposta. Um harness que enche a janela até 100% recebe erro da API em vez de resposta, e o modo de falha é pior do que parece: acontece exatamente quando a tarefa está mais longe do começo.
Somando: 27 mil tokens são custo fixo que se paga em toda chamada antes de qualquer trabalho acontecer. Numa tarefa de 30 iterações, são 810 mil tokens de prefixo. Prompt caching corta a maior parte disso enquanto o prefixo não muda, e é por isso que a ordem em que o harness monta o prompt tem consequência financeira: qualquer coisa variável colocada antes do catálogo invalida o cache inteiro.
A ordem de prioridade
Todo harness tem uma, mesmo quando ninguém a escreveu. Em ordem do que sai por último para o que sai primeiro:
- Instruções de sistema. Nunca saem. Sem elas o agente muda de comportamento no meio da tarefa.
- Pedido original do usuário. Nunca sai. É o único critério de sucesso que existe.
- Restrições dadas em qualquer momento. “Não mexa em
migrations/” precisa sobreviver a tudo. É o item que mais se perde na prática, porque chegou como uma mensagem de conversa e é tratado como histórico comum. - Decisões já tomadas. “Vamos com Postgres.” Perder isso faz o agente reabrir a discussão vinte iterações depois.
- Últimos turnos. Os dois ou três mais recentes, literais.
- Turnos antigos. Candidatos a resumo.
- Saídas de ferramenta antigas. Primeiros a sair, porque quase sempre dá para rodar o comando de novo.
Os itens 3 e 4 são os que separam um harness bom de um ruim. Eles têm a forma de conversa e o peso de instrução, e quase todo harness ingênuo os trata pela forma. Uma restrição escrita na segunda mensagem é, estruturalmente, mais um turno de usuário entre trinta. Nada no histórico a marca como obrigatória, então ela envelhece no mesmo ritmo de uma pergunta sobre onde fica um arquivo. A correção é sem graça: um campo separado que o montador sempre escreve, preenchido quando a restrição aparece em vez de reconstruído depois a partir da conversa.
Os limiares
O corte não acontece quando a janela enche. Acontece quando cruza um limiar, e o limiar existe porque cortar no limite significa cortar com pressa.
Até uns 70% de ocupação não vale mexer em nada: qualquer economia custa mais do
que rende. Passando disso, o harness começa a soltar o que é recuperável.
Saídas de ferramenta antigas viram uma linha do tipo “saída de 1.240 linhas
descartada, rode pytest -q de novo se precisar”. O agente perde o conteúdo e
mantém o caminho de volta.
Perto de 90% não sobra o que descartar de graça, e aí entra a compactação: uma chamada de modelo que transforma vinte turnos num resumo de alguns milhares de tokens. É a operação mais cara e a mais arriscada do conjunto, porque é a única que apaga informação sem deixar um caminho de volta.
A escolha do limiar é um trade-off que ninguém documenta. Limiar baixo compacta cedo e demais, e a sessão perde detalhe que ainda seria útil. Limiar alto adia o corte até o ponto em que a compactação precisa engolir muita coisa de uma vez, e resumo de muita coisa perde mais.
Existe um terceiro caso, que é o que quebra na prática: o salto único que pula os dois limiares de uma vez. O agente está em 62% e roda um comando que devolve 80 mil tokens. Ele vai de 62% a 102% sem passar por nenhum ponto de decisão, e o harness precisa lidar com uma janela que já não cabe. As saídas boas aqui são truncar a saída antes de contá-la ou gravá-la em arquivo e devolver o caminho. A saída ruim, e comum, é compactar de emergência com a saída gigante ainda dentro do que vai ser resumido.
A janela anunciada não é a janela útil
Vale separar o número comercial do número operacional.
Um benchmark de 2024 avaliou 17 modelos de contexto longo em 13 tarefas sintéticas, indo além do teste de agulha no palheiro para incluir rastreamento com múltiplos saltos e agregação. Quase todos mostraram queda grande de desempenho conforme o comprimento aumenta, e entre os que declaravam suportar 32 mil tokens ou mais, cerca de metade sustentava desempenho aceitável nos 32 mil1. O teste é sintético e não é a sua tarefa, mas a implicação de engenharia é direta: orçar contra o número anunciado é orçar contra um teto que o modelo não usa inteiro.
A posição também conta. Um estudo de 2023 mediu o desempenho variando o lugar da informação relevante dentro de um contexto longo e encontrou desempenho mais alto quando ela está no começo ou no fim, com queda no meio2. Num agente, quem escolhe a posição de cada saída de ferramenta é o harness. A regra prática que sobra é modesta: o que precisa ser obedecido fica nas pontas, e o meio é onde se coloca o que é bom ter.
As quatro formas de liberar espaço
Descartar. Apagar o conteúdo e deixar uma marca de que existiu. Custo zero, reversível quando existe um caminho para reler.
Comprimir. Reescrever o mesmo conteúdo em menos tokens sem passar por resumo semântico. Um método de 2023 fez compressão em nível de token com um controlador de orçamento e relatou até 20 vezes de compressão com pouca perda em quatro conjuntos de dados, incluindo GSM8K e ShareGPT3. É um resultado sobre prompt de tarefa, não sobre histórico de agente, e a transferência para o segundo caso não está demonstrada.
Resumir. Trocar vinte turnos por um parágrafo. É o que a compactação faz, e o custo é uma chamada de modelo mais a perda do que o resumo não considerou importante.
Mover para fora. Gravar em arquivo, num banco ou na anotação de um subagente, e trazer de volta só o trecho relevante. A formulação canônica dessa ideia trata a janela como memória principal e o resto como memória secundária, com o próprio modelo movendo dados entre os níveis por meio de chamadas de função4. É a única das quatro que aumenta o total de informação disponível em vez de reduzi-lo.
Descartar o mais antigo primeiro parece a política óbvia e não é neutra. Um trabalho de 2023 sobre cache de atenção encontrou que os primeiríssimos tokens de uma sequência recebem atenção desproporcional independentemente do conteúdo, e que preservar o estado deles recupera boa parte do desempenho que uma janela deslizante ingênua perde5. O mecanismo é interno ao modelo e não descreve o que o harness faz com o histórico, então não transfere direto. O que ele sugere é que “descarta o mais velho” é uma decisão com custo, e não uma operação inócua.
Onde isso falha
A perda é silenciosa. O harness não avisa que descartou. O agente não sabe que esqueceu. O sintoma chega como uma pergunta repetida ou uma decisão revertida, três iterações depois do corte.
Contar token é caro o suficiente para ser feito errado. Tokenizar o contexto inteiro a cada iteração custa tempo, então muito harness estima por caracteres. A estimativa erra para mais em código e para menos em texto com acento, e o erro aparece como um estouro que não deveria acontecer.
Compactar não é composto. Resumir um resumo perde de novo, e a segunda perda não é proporcional à primeira: o que sobreviveu à primeira compactação já era o mais genérico.
Sob demanda tem latência. Trazer conteúdo só quando precisa troca tokens por idas ao disco ou à rede. Numa tarefa de 40 iterações, cinco buscas extras por iteração viram uma diferença perceptível de tempo de parede.
Prompt caching briga com poda agressiva. Toda vez que você reescreve a parte inicial do prompt para economizar espaço, invalida o cache e paga preço cheio na chamada seguinte. Economizar 8 mil tokens de janela pode custar mais do que mantê-los.
A janela pode encher de conteúdo que não é seu. Saída de ferramenta é texto
de origem externa entrando na mesma sequência que as instruções. Uma página web
lida, uma issue, um README de dependência: tudo isso ocupa orçamento e, ao
mesmo tempo, entra no lugar de onde o modelo tira instrução. Gerir contexto e
gerir confiança são a mesma decisão em pontos diferentes do código, e tratá-las
separadamente é como o conteúdo lido acaba com o mesmo peso do que você
escreveu.
O que fazer
- Meça as partes fixas hoje. Sistema, catálogo e configuração de projeto, em tokens. É o número que você vai pagar em toda chamada pelo resto do projeto.
- Reserve a folga da resposta antes de qualquer outra coisa. Estourar a janela na iteração 25 desperdiça tudo que foi gasto até ali.
- Trunque na origem, não na montagem. Toda ferramenta que pode devolver muito precisa de um limite próprio e de um jeito de pedir mais.
- Marque o que nunca sai. Restrição e decisão precisam de um lugar estruturado, fora do histórico comum. Se elas moram no meio da conversa, elas vão embora com a conversa.
- Prefira descartar com endereço a resumir. “Arquivo
pagamentos.pylinhas 40 a 90, releia se precisar” vale mais que um parágrafo sobre o que o arquivo fazia. - Registre a ocupação por iteração. Uma linha de log com os cinco números da Figura 1 mostra em minutos onde a janela está indo.
O passo 6 é o que transforma essa discussão em engenharia. Sem ele, toda decisão sobre contexto é palpite sobre um número que ninguém olhou.
Footnotes
-
Hsieh et al. (2024) construíram um benchmark sintético com comprimento e complexidade configuráveis, avaliaram 17 modelos de contexto longo em 13 tarefas e encontraram queda acentuada de desempenho com o aumento do comprimento, apesar de acurácia quase perfeita no teste simples de recuperação. ↩
-
Liu et al. (2023) variaram a posição da informação relevante dentro de um contexto longo e mediram desempenho mais alto nas pontas e queda no meio, em perguntas sobre múltiplos documentos e em recuperação de chave-valor. ↩
-
Jiang et al. (2023) fizeram compressão de prompt em nível de token com um controlador de orçamento e relataram até 20 vezes de compressão com pouca perda de desempenho em quatro conjuntos de dados. ↩
-
Packer et al. (2023) propuseram tratar a janela de contexto como memória principal e o armazenamento externo como memória secundária, com o modelo movendo dados entre os níveis por meio de chamadas de função. ↩
-
Xiao et al. (2023) observaram que os primeiros tokens de uma sequência concentram atenção independentemente do conteúdo, e que preservar o estado deles recupera boa parte do desempenho perdido por uma janela deslizante. ↩
Perguntas frequentes
- O que o agente carrega no contexto a cada chamada?
- Instruções de sistema, catálogo de ferramentas, configuração do projeto, o pedido do usuário, o histórico da conversa e as saídas de ferramenta acumuladas. Os dois primeiros são custo fixo reenviado toda vez. Os dois últimos crescem a cada iteração e são os que forçam algum corte antes do fim da tarefa.
- Como calcular o orçamento de contexto de um agente?
- Meça as partes fixas uma vez: instruções de sistema mais catálogo de ferramentas mais configuração do projeto. Subtraia da janela útil, não da anunciada. Reserve o espaço da resposta mais longa que você espera. O que sobrar é o orçamento de histórico e saídas, e é dele que o harness precisa tirar espaço.
- Por que o agente estoura o contexto?
- Quase sempre por saída de ferramenta, não por conversa. Um `grep` sem limite, um teste que falha imprimindo mil linhas, um arquivo lido inteiro quando três funções bastavam. Conversa cresce devagar e de forma previsível; saída de ferramenta cresce em degraus de dezenas de milhares de tokens sem aviso.
- Qual é a diferença entre a janela anunciada e a janela útil?
- A anunciada é o máximo que a API aceita. A útil é a faixa em que o modelo ainda encontra o que você colocou lá. Um benchmark de 2024 avaliou 17 modelos de contexto longo e encontrou que, entre os que declaravam 32 mil tokens ou mais, cerca de metade sustentava desempenho nesse comprimento.
- É melhor resumir ou descartar o que não cabe?
- Descarte o que dá para reler: saída de ferramenta, conteúdo de arquivo, resultado de busca. Resuma o que não tem origem recuperável, como o raciocínio da conversa. Resumir custa uma chamada de modelo e apaga detalhe literal; descartar é grátis e reversível quando existe o caminho do arquivo para reabrir.
- Colocar tudo numa janela de um milhão resolve?
- Adia o problema e cobra por ele. O custo por chamada cresce com o que você mantém, a latência acompanha, e a posição do que importa dentro da janela passa a afetar o resultado. Uma janela grande é espaço para errar mais devagar, não substituto para decidir o que entra.
Referências
- Hsieh, C.-P. et al.. RULER: What's the Real Context Size of Your Long-Context Language Models? (2024)arXiv:2404.06654
- Liu, N. F. et al.. Lost in the Middle: How Language Models Use Long Contexts (2023)arXiv:2307.03172
- Packer, C. et al.. MemGPT: Towards LLMs as Operating Systems (2023)arXiv:2310.08560
- Jiang, H. et al.. LLMLingua: Compressing Prompts for Accelerated Inference of Large Language Models (2023)arXiv:2310.05736
- Xiao, G. et al.. Efficient Streaming Language Models with Attention Sinks (2023)arXiv:2309.17453