🔧 Técnico — o cache em ação
Agora que os termos estão claros, vamos ver o cache funcionando de verdade: como ele se comporta numa conversa que cresce, o que anexa sem quebrar e o que reseta a posição 0, e a invalidação a fundo — com exemplos copy-run pra rodar no seu Claude Code.
O cache não é bateria que descarrega: a fatia lida do cache cresce a cada turno. Um turno com algo novo (ex.: uma skill grande) marca menos; o resto vira leitura barata no próximo.
Tudo nesta trilha sai de um único invariante: prompt caching é um
casamento de prefixo. A chave do cache são os bytes exatos do prompt
até o ponto de corte, e a ordem de montagem é sempre
tools → system → messages. Guarde essa frase — ela
explica por que anexar no fim é seguro e por que mexer na lista de ferramentas quebra tudo.
Os três campos que você vai auditar o tempo todo: cache_read_input_tokens
(lido do cache, ~0,1×), cache_creation_input_tokens (gravado, ~1,25×)
e input_tokens (só o resto não-cacheado, preço cheio). A
soma dos três é o tamanho do prompt. Se o campo de leitura fica
zero repetindo o mesmo prefixo, tem um silent invalidator.
Mapa da trilha
Conteúdo detalhado
💬 O cache numa conversa
O prefixo estável cresce, cada turno relê barato, e a % de cache SOBE — não descarrega. Onde entra o write frio e por que a pausa longa custa.
system + tools + todo o histórico (inclusive a última resposta) formam o prefixo estável, que cresce a cada turno.
É o que torna a conversa barata: quanto maior o prefixo estável, mais tokens saem a ~0,1×.
Prefixo = começo idêntico que se repete; só o final novo escapa do cache.
A cada mensagem nova, só o final muda; a API relê todo o prefixo do cache (~0,1×) e grava só o bloco novo.
Explica por que o segundo turno em diante é muito mais barato que o primeiro.
Hit = leitura do cache; write = gravação do bloco novo daquele turno.
Enquanto você responde dentro de ~5 min, a janela renova e o cache fica quente.
Trabalhar em ritmo contínuo mantém tudo barato sem esforço.
TTL = tempo de vida da entrada; cada uso reinicia o relógio dos 5 min.
Um turno pode marcar 40% (teve coisa nova, ex.: uma skill grande). Esse bloco vira read barato no próximo turno, e a % sobe.
Desfaz a intuição errada de que o cache "acaba"; ele só melhora com a conversa.
Turno agora: 40k lido / 60k novo = 40%; próximo turno: ~100k lido / ~1k novo ≈ 99%.
A 1ª mensagem grava o cache (paga um pouco mais); não há prefixo anterior para ler.
Evita o susto de ver o 1º turno "caro" — é o único que grava tudo.
Write ~1,25× (TTL 5 min); a partir do 2º turno predomina read ~0,1×.
Passou dos ~5 min parado, o TTL expira e a próxima mensagem paga a gravação de novo.
Explica por que retomar depois do almoço "custa": foi um write frio, não um bug.
Para tráfego com gaps > 5 min existe o TTL de 1 h (write ~2×).
🧩 Skills e ferramentas: anexar vs quebrar
Conteúdo novo no FIM é seguro; mexer na lista de tools reseta a posição 0. Por que Tool Search anexa de propósito.
(a) A DESCRIÇÃO da skill (linha curta na lista) fica sempre no prefixo estável. (b) O CONTEÚDO completo só entra quando a skill é invocada.
Sem separar os dois, você acha que "toda skill é cara" — só o conteúdo pesa, e uma vez.
Descrição = sempre presente e barata; conteúdo = sob demanda.
O conteúdo da skill entra ANEXADO no FIM do histórico (não na posição 0). Tudo antes continua válido e é lido barato; só o bloco novo é gravado.
Tira o medo de invocar skills no meio da conversa: é um write único, não um reset.
Anexar no fim = seguro; nos turnos seguintes a skill já é read barato.
As ferramentas são renderizadas na posição 0 (antes de system e histórico). Mudar a lista tools invalida tudo dali pra frente.
É o silent invalidator mais caro: um byte na posição 0 derruba o prefixo inteiro.
Ordem de montagem: tools → system → messages.
(1) Conectar/desconectar MCP no meio; (2) mudar description/input_schema de uma tool; (3) montar tools de um set/dict sem ordenar; (4) ligar/desligar tools por modo/permissão.
São as 4 causas mais comuns de cache que "some" sem motivo aparente.
Basta uma palavra numa description mudar para invalidar a posição 0.
O Tool Search carrega o schema de uma ferramenta ANEXANDO (não trocando a lista) — feito justamente pra NÃO quebrar o cache.
Distingue "descobrir tool via Tool Search" (seguro) de "conectar MCP que reescreve a lista" (quebra).
Anexar schema no fim = write único; reescrever tools = reset.
Conteúdo novo no FIM = seguro. Mexer na lista de tools ou no system prompt = reset da posição 0.
Uma frase que resolve 90% das decisões do dia a dia sobre o cache.
Pergunte sempre: "isso muda o começo ou só acrescenta no fim?"
🔎 Invalidação a fundo
Hierarquia de 3 tiers, o 20-block lookback, os silent invalidators e o timing concorrente no fan-out. Como auditar com usage.
Mudança só invalida o tier dela e os de baixo: mudar tools OU trocar modelo = reset total; mudar system = invalida system+messages; mudar mensagem = só messages.
Você calcula o "raio de dano" de qualquer mudança antes de fazê-la.
Tiers na ordem tools → system → messages; o modelo é raiz de tudo.
Trocar tool_choice por request ou ligar/desligar thinking mantém o cache de tools+system.
Evita paranoia: nem todo parâmetro que muda por request quebra o cache.
Esses dois não entram no prefixo cacheável — não precisa se preocupar.
Cada breakpoint procura entrada anterior olhando no máx. 20 blocos pra trás. Turno que adiciona >20 blocos (loops com muitos tool_use/tool_result) pode fazer o próximo turno errar o cache silenciosamente.
É a causa "invisível" de miss em agentes que fazem muitas chamadas de ferramenta por turno.
Fix: breakpoint intermediário a cada ~15 blocos.
datetime.now()/Date.now()/uuid no system; json.dumps sem sort_keys=True; iterar set; tools montadas por usuário; seção condicional no system.
São mudanças que você não "vê" mas alteram os bytes do prefixo a cada request.
Sintoma comum: cache_read_input_tokens sempre 0.
A entrada só fica legível DEPOIS que a 1ª resposta começa a streamar. N requests paralelos com o mesmo prefixo pagam todos preço cheio.
Disparar tudo de uma vez parece rápido, mas joga fora o cache compartilhado.
Fix: mandar 1, esperar o 1º token, disparar os N-1.
cache_read_input_tokens / cache_creation_input_tokens na resposta. Zero em requests repetidos = tem invalidator.
É o instrumento de medição — sem ele você chuta; com ele você prova.
Faça diff dos bytes do prompt entre dois requests iguais para achar o byte que muda.