A mensagem 50 da sua sessão custa quase 6x mais tokens que a primeira. Mesmo texto, mesmo tamanho, e a janela de contexto explica o porquê.
Ninguém te avisa disso. A conversa cresce em silêncio enquanto você trabalha, e cada resposta do modelo vira peso morto no turno seguinte.
O efeito tem nome: snowball. Cada mensagem nova carrega a conversa inteira nas costas, e é essa bola de neve que vamos medir aqui, com fórmula e exemplo numérico.
No pacote: quanto o crescimento pesa no consumo de tokens e um HUD de statusline que torna o efeito visível. No final, o script completo pra instalar no seu Claude Code .

A API não tem memória
Um LLM não “lembra” da conversa. A API é stateless: cada request chega sozinho, sem estado do lado do servidor.
Pra manter o contexto, o cliente reenvia tudo a cada turno. System prompt, definições de ferramentas, CLAUDE.md, e cada mensagem trocada desde o início da sessão. Sua pergunta nova é só a última linha de um pacote que cresce a cada turno.
Não é exclusividade do Claude Code: agentes construídos sobre APIs de chat costumam funcionar assim. A sensação de memória é uma releitura do histórico, paga em tokens de input, a cada mensagem.
E tem um detalhe que engorda a bola: a resposta do modelo no turno anterior vira histórico. Ou seja, o output de ontem é o input de hoje. Resultados de ferramentas, diffs, logs de teste: tudo entra na conta do turno seguinte.
A fórmula
Vamos dar nomes. P é o custo fixo que abre toda mensagem: system prompt, ferramentas, CLAUDE.md. t é o tamanho médio de um turno completo: sua mensagem, mais a resposta, mais os resultados de ferramentas.
O input da mensagem n é o custo fixo mais todos os turnos anteriores:
input(n) = P + (n - 1) * t # grows linearly per message
total(N) = N * P + t * N * (N - 1) / 2 # grows quadratically per session
O custo por mensagem cresce linear. Mas o custo total da sessão soma essa escada inteira: 1 + 2 + … + (N – 1), a soma triangular. Por isso o total cresce quadrático.
Um exemplo ilustrativo, com P = 20k tokens e t = 2k tokens por turno:
| Mensagem | Input enviado | % da janela de contexto (200k) |
|---|---|---|
| 1 | 20k | 10% |
| 10 | 38k | 19% |
| 25 | 68k | 34% |
| 50 | 118k | 59% |
A mensagem 50 carrega quase 6x o peso da mensagem 1, com você digitando a mesma frase curta. E o total da sessão até ali passa de 3,4 milhões de tokens de input pra 50 mensagens.
É uma bola de neve descendo a montanha. Cada volta agrega o que encontrou no caminho, e nada sai por vontade própria. Só uma compactação enxuga a bola, e vamos chegar nela.
O cache paga a fatura, mas não esvazia a janela
Custo em dinheiro e custo em janela de contexto são coisas diferentes.
O prompt caching ataca o dinheiro. O prefixo repetido da conversa (system prompt, histórico antigo) é lido do cache por 10% do preço base de input na API da Anthropic. O Claude Code usa esse mecanismo: o payload do statusline expõe os campos cache_read_input_tokens e cache_creation_input_tokens de cada turno.
Mas o cache não muda a ocupação da janela de contexto. Token lido do cache é token dentro do contexto do mesmo jeito. A documentação do statusline define quais tokens contam pro percentual de janela usada:
used tokens = input_tokens
+ cache_creation_input_tokens
+ cache_read_input_tokens
# output_tokens do not count directly; they only enter
# the sum when they become history on the next turn
Cache barateia a releitura. A bola de neve continua crescendo, só desce a montanha com desconto. E quando a janela de contexto enche, nenhum desconto resolve: alguém precisa compactar o histórico.
O Claude Code faz isso sozinho perto do limite: limpa outputs antigos de ferramentas e, se preciso, resume a conversa. O resumo preserva seus pedidos, mas instruções do começo podem se perder. Compactar na sua hora, com /compact e um foco, preserva o que você escolher. Pra isso, você precisa ver a bola crescendo antes do limite.
O HUD: dando nome às fases da bola de neve
Percentual de contexto é um número abstrato. “62% usado” não diz o que fazer. Então criei um statusline que traduz o número em fases de uma bola de neve, cada uma com cor e recomendação:
| Fase | Faixa | Cor | Próxima mensagem |
|---|---|---|---|
flake |
0-14% | cinza | leve |
rolling |
15-39% | verde | média |
steady |
40-64% | amarelo | pesada |
heavy |
65-84% | laranja | cara |
avalanche |
85-94% | vermelho | rode /compact |
frozen |
95-100% | vermelho | fim da linha |
No terminal, a linha fica assim (bar proporcional de 10 posições):
[Opus] ##........ rolling -> next ~ medium
[Opus] #######... heavy -> next ~ expensive
[Opus] #########. avalanche -> next ~ /compact
A versão que uso no dia a dia adiciona uma seta de aceleração. O script guarda amostras do percentual num arquivo por sessão e olha uma janela deslizante de 120 segundos:
^^subiu 15 pontos ou mais na janela: turno pesado em andamento, provavelmente leitura de arquivos grandes^subiu 5 pontos ou mais: ritmo normal de trabalhovcaiu 10 pontos ou mais: um/compactacabou de acontecer
A aceleração importa porque a fase sozinha esconde o ritmo. Estar em steady subindo devagar é confortável. Estar em steady com ^^ significa que avalanche chega em poucos turnos.
Instalando no Claude Code
O Claude Code tem um mecanismo nativo de statusline: um script que recebe JSON da sessão via stdin e imprime uma linha. Ele roda dirigido por eventos da sessão, com debounce de 300ms.
Primeiro, salve o script em ~/.claude/snowball-statusline.sh. Esta é a versão mínima, com a fórmula das fases completa (depende só de jq):
#!/usr/bin/env bash
# Snowball statusline for Claude Code.
# Reads the /statusline JSON payload from stdin, prints one line.
set -euo pipefail
input="$(cat)"
model="$(jq -r '.model.display_name // "Claude"' <<<"$input")"
pct="$(jq -r '.context_window.used_percentage // 0' <<<"$input")"
pct="${pct%.*}"
# Map context usage to snowball phases.
if (( pct < 15 )); then phase="flake"; next="light"
elif (( pct < 40 )); then phase="rolling"; next="medium"
elif (( pct < 65 )); then phase="steady"; next="heavy"
elif (( pct < 85 )); then phase="heavy"; next="expensive"
elif (( pct < 95 )); then phase="avalanche"; next="/compact"
else phase="frozen"; next="DEAD"
fi
# Proportional 10-slot bar, half-up rounding.
filled=$(( (pct * 10 + 50) / 100 ))
(( filled > 10 )) && filled=10
bar=""
for (( i = 0; i < 10; i++ )); do
(( i < filled )) && bar+="#" || bar+="."
done
printf '[%s] %s %s -> next ~ %s\n' "$model" "$bar" "$phase" "$next"
Dê permissão de execução:
chmod +x ~/.claude/snowball-statusline.sh
Depois, registre no ~/.claude/settings.json:
{
"statusLine": {
"type": "command",
"command": "~/.claude/snowball-statusline.sh"
}
}
Pronto. A barra aparece no rodapé da sessão e atualiza sozinha.
Existe um atalho: o comando /statusline aceita instruções em linguagem natural, gera o script em ~/.claude/ e atualiza o settings pra você. Funciona bem pra começar. Escrever o script na mão dá controle fino sobre as fases e os limiares.
O payload de stdin traz mais do que percentual de contexto. Alguns campos úteis pra estender o HUD:
context_window.used_percentage: o combustível do snowballrate_limits.seven_day.used_percentage: cota semanal do plano (presente só pra assinantes Pro/Max, depois da primeira resposta da API)workspace.current_dir: diretório do projetocost.total_cost_usd: custo estimado da sessão, calculado no cliente (pode divergir da fatura)session_id: chave pra guardar estado por sessão, como o log de aceleração
Minha versão completa compõe cinco segmentos com esses campos: modelo, snowball com aceleração, cota semanal, diretório e branch git com estado. Tudo numa linha:
[Opus] | #####..... steady ^ -> heavy | 7d 41% | ~/code/app | main *2 !1
https://github.com/dklima/claude-snowball-statusline
O que fazer em cada fase
O HUD só vale a pena se mudar comportamento. O que cada fase pede:
flake e rolling: trabalhe normal. É a zona barata da sessão, boa pra tarefas exploratórias que leem muitos arquivos.
steady: hora de intenção. Termine a tarefa atual antes de abrir outra frente. Cada tangente daqui pra frente custa caro.
heavy: feche o ciclo. Peça o resumo, o diff final, o commit. Evite colar logs grandes ou pedir leituras extensas.
avalanche: rode /compact agora, antes que o próximo turno pesado estoure a janela de contexto. Ou melhor: se a tarefa acabou, abra sessão nova.
frozen: o auto-compact vai agir por você, nos termos dele. Salve o que importa e recomece limpo.
E uma consequência direta da fórmula: o custo fixo P é pago em toda mensagem, da primeira à última. CLAUDE.md inchado, servidores MCP que você não usa, ferramentas demais: tudo isso é peso morto multiplicado por N. Enxugar o P é a otimização mais barata que existe, porque é paga uma vez e economiza sempre.
O ponto central
Conversa longa é um contrato de custo crescente. Cada mensagem relê o histórico acumulado desde a última compactação: linear por mensagem, quadrático por sessão.
O cache barateia a releitura, mas a janela de contexto enche do mesmo jeito. E decisão de contexto sem visibilidade é chute. Um statusline de 30 linhas transforma o número abstrato em fase com nome, cor e ação.
Fontes




Deixe um comentário