Blog

Compressão de contexto: caber mais na janela sem perder sinal

Toda conversa longa com um modelo esbarra no mesmo teto: a janela de contexto é finita, e o histórico cresce a cada turno. Cedo ou tarde a conversa não cabe mais, e você tem que escolher o que entra no prompt e o que fica de fora. A saída ingênua é cortar as mensagens mais antigas: mantém as últimas N e joga o resto fora. Funciona até o modelo esquecer o nome do cliente, o número do pedido ou a decisão que a conversa tomou vinte turnos atrás, porque essa informação estava justamente no trecho que você descartou. O problema real não é caber, é caber sem perder sinal: enfiar mais conversa na mesma janela mantendo o que importa e descartando o que não importa. Compressão de contexto é o conjunto de técnicas que faz isso de forma consciente, distinguindo o turno de alto sinal que precisa sobreviver do turno de baixo sinal que pode virar uma linha de resumo. Este artigo mostra por que cortar por idade perde informação crucial, como orçar a janela em vez de estourar, o que nunca deve ser comprimido, o resumo em bloco dos turnos antigos, o cuidado com o resumo que inventa fato e como medir se a compressão está preservando o sinal ou destruindo ele.

2026-07-21 / IA Aplicada / 13 min

01

Por que cortar por idade perde o que importa

A janela deslizante, manter só os últimos N turnos, é a compressão mais barata e a mais enganosa. Ela assume que a mensagem mais recente é sempre a mais importante, e isso é falso numa conversa real. O cliente diz o número do pedido no segundo turno e volta a falar dele no vigésimo; a decisão de trocar o produto foi tomada no meio e precisa valer até o fim; a instrução de sistema que define o tom do bot está no começo de tudo. Cortar por idade descarta esses trechos justamente porque eles são antigos, sem olhar se ainda são necessários. O resultado é um bot que parece ter amnésia: ele responde bem os últimos turnos e comete erros grosseiros sobre fatos que a própria conversa já estabeleceu, porque o fato saiu da janela sem que ninguém verificasse se ele ainda era preciso.

A idade de um turno não mede o seu sinal. Um "ok, obrigado" recente ocupa espaço e não carrega quase nada; a definição do escopo do pedido, dita há muitos turnos, carrega o fio inteiro da conversa. Compressão por idade trata os dois igual, e é por isso que ela quebra. A compressão que preserva sinal separa duas perguntas que a janela deslizante confunde numa só: o que é recente e o que é importante. Nem todo recente é importante, e o mais grave, nem todo importante é recente. Uma vez que você aceita que essas duas perguntas são diferentes, fica claro que a régua do corte não pode ser o relógio, tem que ser o sinal.

EstratégiaRégua do corteO que quebra
Janela deslizante (últimos N)Idade do turnoPerde fato antigo ainda necessário, mantém turno recente vazio
Corte por token brutoEstourou o limite, trunca no meioCorta frase pela metade, separa pergunta da resposta
Compressão por sinalImportância do turno, não idadeCusta um resumo, exige classificar o que preservar

02

Orçar a janela antes de estourar

Comprimir não é reagir ao erro de limite excedido, é orçar a janela antes de chegar nele. Você define um teto de tokens para o contexto que é menor que o limite bruto do modelo, porque precisa deixar espaço para a resposta que o modelo vai gerar e para uma margem de segurança. Dentro desse teto, você aloca por prioridade: primeiro a instrução de sistema, depois as âncoras que não podem cair, depois os turnos recentes, e o que sobra de espaço é o orçamento para os turnos antigos, que serão comprimidos até caber nele. Orçar antes transforma a compressão de uma reação de pânico, disparada quando o prompt já estourou, num processo previsível que roda a cada turno e mantém o contexto sempre dentro de um tamanho conhecido.

A conta de tokens tem que ser feita com o tokenizador do modelo, não estimada por número de caracteres, porque a diferença entre estimar e contar é a diferença entre caber e estourar. Um texto com muitos números, código ou outro idioma consome tokens de forma diferente do que a regra de bolso de quatro caracteres por token sugere, e uma estimativa que erra para baixo faz o prompt estourar em produção exatamente quando a conversa fica interessante. Orçar de verdade é contar com o tokenizador certo, deixar margem para a resposta e tratar o teto como um contrato: o contexto nunca passa daqui, e a compressão é o mecanismo que garante isso a cada turno.

// context/budget.js
// Orca a janela por prioridade: primeiro o que nao pode cair,
// e o espaco que sobra vira o orcamento dos turnos antigos.

export function budgetContext(history, { windowLimit, reserveForReply }) {
  // O teto do contexto e o limite do modelo MENOS o espaco da resposta
  // e uma margem de seguranca. Nunca use o limite bruto.
  const budget = windowLimit - reserveForReply;

  // Sempre entram inteiros, na ordem de prioridade.
  const system = history.filter((t) => t.role === 'system');
  const anchors = history.filter((t) => t.pinned);       // fatos que nao podem cair
  const recent = takeRecentTurns(history, RECENT_KEEP);  // ultimos turnos, sempre crus

  const fixedCost = tokenCount([...system, ...anchors, ...recent]);

  // O que sobra do orcamento e para os turnos antigos, que serao comprimidos.
  const remaining = budget - fixedCost;
  const old = history.filter(
    (t) => !t.pinned && t.role !== 'system' && !recent.includes(t),
  );

  return { system, anchors, recent, old, remainingForOld: remaining };
}

03

O que nunca deve ser comprimido

A compressão só é segura quando existe uma lista clara do que ela não pode tocar. A instrução de sistema é a primeira: ela define o comportamento do bot e resumi-la é arriscar mudar o que o modelo faz. Depois vêm as âncoras, os fatos concretos que a conversa precisa carregar até o fim, número do pedido, nome do cliente, decisão tomada, valor combinado. Esses fatos não podem virar resumo porque um resumo é uma reformulação, e reformular um número ou um nome é a forma mais fácil de corrompê-lo. E os turnos recentes ficam crus porque são o contexto imediato do próximo turno, aquilo que o modelo precisa ler palavra por palavra para responder com coerência. Tudo o que não está nessas três categorias é candidato à compressão; tudo o que está nelas passa intacto.

  • Instrução de sistema: define o comportamento do bot, resumir muda o que ele faz. Passa sempre crua.
  • Âncoras de fato: número de pedido, nome, valor, decisão, prazo. Fatos concretos que a conversa carrega até o fim e que um resumo corromperia ao reformular.
  • Turnos recentes: o contexto imediato do próximo turno, precisa ser lido palavra por palavra para manter coerência. Ficam crus.
  • Candidatos à compressão: turnos antigos de baixo sinal, saudações, confirmações, digressões, tudo que já cumpriu seu papel e pode virar uma linha de resumo.

Identificar as âncoras é o passo que separa uma compressão que preserva sinal de uma que destrói. Elas podem ser marcadas de duas formas: explicitamente, quando o seu sistema sabe que um número de pedido ou um dado de cadastro entrou na conversa e o fixa, ou por extração, quando um passo dedicado lê o histórico e puxa os fatos concretos para uma lista estruturada antes de comprimir o resto. O importante é que a âncora saia do fluxo do texto comprimível e vire um dado protegido, porque enquanto ela estiver misturada com as saudações e as digressões, o compressor não tem como saber que aquele número não pode ser reformulado.

04

Resumir os turnos antigos em bloco

Com o que não pode cair já protegido, sobra o miolo comprimível: os turnos antigos de baixo sinal. A técnica é resumir esse bloco num texto curto que preserve o fio da conversa sem os detalhes que já não importam. Em vez de dez turnos de ida e volta sobre um agendamento que já foi resolvido, uma linha: "o cliente agendou para terça e confirmou o endereço". O resumo não substitui as âncoras, que seguem cruas e à parte, ele apenas costura o contexto narrativo que dá sentido à conversa. E a compressão pode ser hierárquica: os turnos ficam crus, depois viram resumos de bloco, e resumos antigos podem ser resumidos de novo num resumo mais alto, formando camadas em que o detalhe diminui conforme o turno envelhece, mas o fio nunca se rompe.

Compressao por sinal, nao por idade

  historico completo (nao cabe na janela)
     |
     v
  +-----------------------------------------------+
  | instrucao de sistema      -> CRU (nunca comprime)
  | ancoras (pedido, nome...)  -> CRU (dado protegido)
  | turnos recentes (ultimos N)-> CRU (contexto imediato)
  | turnos antigos baixo sinal -> RESUMO EM BLOCO
  +-----------------------------------------------+
     |
     v
  contexto comprimido cabe no orcamento
     |
     +-- turnos MUITO antigos: resumo do resumo (camada mais alta)
     |
     v
  prompt final: sistema + ancoras + resumo + recentes crus
     (o fio da conversa sobrevive, o detalhe morto sai)

A compressão hierárquica é o que permite conversas realmente longas caberem numa janela fixa sem esquecer o começo. Sem ela, você tem duas opções ruins: manter tudo cru e estourar, ou cortar por idade e perder o começo. Com ela, o começo da conversa não desaparece, ele encolhe: vira uma linha de resumo que ainda diz o que foi combinado, e essa linha sobrevive mesmo depois de centenas de turnos, porque ocupa quase nada. O detalhe morto sai, o fio vivo fica. É a diferença entre um bot que "lembra que a conversa começou tratando de um reembolso" e um bot que só sabe os últimos três turnos e age como se a conversa tivesse começado agora.

05

O resumo que inventa fato

Comprimir com um modelo tem um risco que o corte por idade não tem: o resumo pode inventar. Quando você pede a um modelo para resumir dez turnos, ele pode introduzir um detalhe que não estava lá, trocar um número, afirmar uma decisão que a conversa não tomou. E o perigo é que esse fato inventado entra no contexto como se fosse verdade e contamina todos os turnos seguintes, porque agora o próprio histórico afirma algo falso, e o modelo passa a raciocinar em cima disso. Uma alucinação num resumo de contexto é pior do que uma alucinação numa resposta: a resposta o usuário vê e corrige, o resumo fica silencioso no histórico envenenando o resto da conversa. Por isso a compressão que usa modelo precisa de um freio explícito.

O freio tem três partes. A primeira é instruir o resumo a ser extrativo, não criativo: ele deve condensar o que foi dito, não interpretar nem completar, e nunca introduzir número, nome ou valor que não apareça no texto original. A segunda é manter as âncoras fora do resumo: como os fatos concretos já foram extraídos e protegidos crus, o resumo não precisa carregar número nenhum, e um resumo que não tem números não tem como errar número. A terceira é a verificação: quando o custo justifica, um passo checa que o resumo não introduziu fato ausente do original, comparando as âncoras do resumo com as âncoras extraídas. A regra que ancora tudo é que a compressão nunca pode adicionar informação, só remover; se o resumo diz algo que o original não dizia, ele falhou, por mais fluente que soe.

// context/summarize.js
// Resume os turnos antigos de forma extrativa e verifica que
// nenhum fato novo entrou. Compressao so REMOVE, nunca ADICIONA.

const COMPRESS_INSTRUCTION = `
Condense os turnos abaixo preservando o fio da conversa.
Regras: seja extrativo, nao interprete nem complete.
NUNCA introduza numero, nome, valor ou decisao que nao esteja no texto.
Fatos concretos ja foram extraidos a parte: nao os repita nem os invente.
`;

export async function compressOldTurns(oldTurns, anchors, model) {
  const summary = await model.summarize(COMPRESS_INSTRUCTION, oldTurns);

  // Verifica que o resumo nao inventou fato: nenhuma ancora nova
  // pode aparecer no resumo que nao estivesse no original.
  const introduced = extractFacts(summary).filter(
    (fact) => !appearsIn(fact, oldTurns) && !anchors.includes(fact),
  );

  if (introduced.length > 0) {
    // O resumo alucinou um fato: descarta e cai para um corte conservador
    // em vez de envenenar o contexto com algo que a conversa nao disse.
    return conservativeTrim(oldTurns);
  }

  return summary;
}

06

Medir se a compressão preserva o sinal

Compressão é uma troca: você ganha espaço na janela e paga em fidelidade ao histórico. Sem medir, você não sabe de que lado a troca está pendendo, e uma compressão agressiva demais degrada o bot de um jeito que não aparece em nenhum erro imediato, só numa piora difusa da qualidade das respostas. A métrica central é a retenção de âncoras: depois de comprimir, os fatos que a conversa estabeleceu ainda estão acessíveis no contexto? Você monta um conjunto de conversas longas com fatos plantados no começo, aplica a compressão e faz perguntas cuja resposta depende desses fatos. Se o bot acerta, a compressão preservou o sinal; se erra, ela cortou algo que precisava sobreviver, e você aperta o que fica cru ou o que vira âncora.

  1. Oráculo de âncoras: monte conversas longas com fatos plantados e pergunte no fim algo que dependa deles; meça quantos o bot ainda acerta depois da compressão.
  2. Taxa de fidelidade do resumo: verifique que o resumo não introduziu fato ausente do original, com o passo de verificação como métrica contínua, não só como freio.
  3. Ocupação da janela: acompanhe quanto do orçamento cada categoria consome; se as âncoras crescem sem limite, a extração precisa priorizar ou a conversa precisa de checkpoint.
  4. Sinal por token: compare a qualidade da resposta com e sem compressão no mesmo caso; a compressão só vale se a queda de qualidade for menor que o ganho de caber.
  5. Alerta de perda: registre quando um turno é cortado ou resumido e o que ele continha, para reconstruir o que foi perdido quando uma conversa der resposta errada.

A compressão de contexto bem feita é invisível: o bot mantém conversas longas coerentes, lembra o que foi combinado no começo e nunca estoura a janela, e você não percebe que por baixo há um mecanismo constante decidindo o que preservar cru e o que condensar. A compressão mal feita também é quase invisível, e é aí que mora o risco: ela degrada a qualidade aos poucos, sem um erro claro que aponte a causa, até alguém notar que o bot "está mais burro" sem saber por quê. A diferença entre as duas não está no código da compressão, está na medição: só medir a retenção de sinal separa a compressão que economiza janela da que silenciosamente joga fora a memória da conversa.

FAQ

Perguntas frequentes

Por que não basta manter só as últimas mensagens da conversa?

Porque a idade de um turno não mede a importância dele. A janela deslizante, manter os últimos N turnos, assume que o mais recente é o mais relevante, e isso é falso numa conversa real: o número do pedido dito no começo, a decisão tomada no meio e a instrução de sistema no topo são antigos e continuam essenciais. Cortar por idade descarta esses trechos justamente por serem antigos, sem checar se ainda são necessários, e o resultado é um bot com amnésia, que responde bem os últimos turnos e erra grosseiramente sobre fatos que a própria conversa já estabeleceu. A compressão que preserva sinal separa duas perguntas que a janela deslizante confunde numa só, o que é recente e o que é importante, e usa a importância, não o relógio, como régua do corte.

Comprimir contexto com um modelo não corre o risco de alucinar?

Corre, e é o risco mais perigoso da compressão, porque um fato inventado num resumo entra no histórico como verdade e contamina todos os turnos seguintes, silenciosamente, sem que o usuário veja e corrija como faria numa resposta. O freio tem três partes: instruir o resumo a ser extrativo, condensar o que foi dito sem interpretar nem introduzir número, nome ou valor novo; manter as âncoras fora do resumo, já que os fatos concretos são extraídos e protegidos crus a parte, então um resumo sem números não tem como errar número; e verificar que o resumo não introduziu fato ausente do original, caindo para um corte conservador quando introduziu. A regra que ancora tudo é que a compressão só pode remover informação, nunca adicionar.

Como saber se a compressão está preservando o que importa?

Medindo a retenção de âncoras, que é o sinal que a conversa não pode perder. Você monta um conjunto de conversas longas com fatos plantados no começo, aplica a compressão e faz perguntas cuja resposta depende desses fatos; se o bot acerta, a compressão preservou o sinal, se erra, ela cortou algo que precisava sobreviver. Junto disso, acompanhe a taxa de fidelidade do resumo com o passo de verificação, a ocupação da janela por categoria e o sinal por token, comparando a qualidade da resposta com e sem compressão no mesmo caso. Sem medir, uma compressão agressiva demais degrada o bot de forma difusa, sem erro imediato que aponte a causa, até alguém notar que ele "está mais burro" sem saber por quê.

Comprimir contexto é escolher o sinal, não o relógio

Caber na janela sem perder o que importa não é cortar as mensagens mais antigas, é distinguir o turno de alto sinal que precisa sobreviver do turno de baixo sinal que pode virar uma linha de resumo. Orçar a janela em tokens, proteger crua a instrução de sistema, as âncoras de fato e os turnos recentes, resumir o resto de forma extrativa e verificada e medir a retenção de sinal transforma a janela de contexto de um teto que trunca a conversa num orçamento que a conversa respeita. Posso desenhar essa camada de compressão no seu sistema de IA, escolhendo o que fica cru e o que condensa, blindando o resumo contra alucinação e medindo que o fio da conversa sobrevive, para que o seu bot mantenha conversas longas sem amnésia nem estouro de janela.