Pular para o conteúdo
MÓDULO 3.2

🏛️ Arquitetura pró-cache e auditoria

O melhor cache é o que nunca invalida sem querer. Aqui você desenha o prefixo — system congelado, tools determinísticas, um modelo por loop, forks que copiam o pai — e ganha um checklist e um prompt de auditoria pra rodar no Claude Code.

6
Tópicos
~45
Minutos
Avançado
Nível
Prática
Tipo
1

🧊 System prompt congelado

O system prompt (o texto de instruções antes da conversa) fica logo na frente do prefixo. Se você interpola data/hora, modo ou nome do usuário ali, cada requisição tem bytes diferentes no começo — e invalida tudo. A regra é: system congelado; contexto dinâmico vai depois, nas messages.

✗ system interpolado system + "hoje é {data}, user {nome}" muda a cada turno messages → cache invalida todo turno ✓ system congelado system fixo (sem variáveis) 🧊 idêntico byte a byte messages + "hoje é {data}, user {nome}" → prefixo estável, cache quente

Legenda: o dinâmico não some — ele muda de lugar. Sai da frente (system) e vai pro fim (messages), onde não quebra o prefixo.

Congela

System sem variáveis

Dinâmico

Vai pro fim das messages

Frente

System é quase posição 0

1 byte

Basta pra invalidar

2

🔤 Tools determinísticas

A lista de tools é renderizada na posição 0 — antes de tudo. Se você a monta de um set/dict sem ordenar, a ordem varia entre requests e o cache nunca bate. Serialize sempre na mesma ordem: ordene por nome.

✗ Ordem variável

tools = [t for t in registry]
# registry é um set/dict
# ordem muda entre requests → miss

✓ Ordem estável

tools = sorted(registry, key=lambda t: t["name"])
# mesma ordem sempre → prefixo idêntico
# cache_read para de ser 0
Prompt pronto — revisão pró-cache (system + tools)

Objetivo

Fazer o Claude Code apontar o que, no seu system prompt e na montagem das tools, está impedindo o cache — e reescrever pra maximizar o hit.

Prompt (cole no Claude Code)

Revise meu system prompt e a montagem de `tools` em <caminho/do/arquivo>.
Aponte o que está impedindo o prompt caching:
1) interpolação dinâmica no system (data/hora, nome do usuário, modo,
   uuid, qualquer coisa que muda entre requests);
2) ordem não-determinística das tools (montadas de set/dict sem sort).
Depois reescreva: (a) congele o system e mova o dinâmico pro fim das
messages; (b) serialize as tools em ordem estável (ordene por nome).
Mostre o diff e explique por que cada mudança preserva o prefixo.

Como verificar

Rode duas requisições iguais depois da mudança e confira o usage: cache_read_input_tokens deve deixar de ser 0 a partir da 2ª. Se ainda vier 0, sobrou um invalidator no prefixo (veja o tópico 5).

Posição 0

Tools vêm antes de tudo

sorted()

Ordene por nome

set/dict

Ordem varia = miss

Estável

Mesmos bytes = hit

3

🚫 Não trocar modelo no meio

Cache é por modelo. Trocar de modelo no meio invalida tudo — o prefixo cacheado de um modelo não serve pro outro. Precisa de um modelo mais barato pra uma subtarefa? Use um subagente, mantendo o loop principal num modelo só.

Loop principal — um modelo só (cache quente) modelo A modelo A modelo A delega subtarefa subagente · modelo barato cache próprio, não toca o do pai trocar p/ modelo B no loop → reset total

Legenda: o loop roxo fica num modelo só; o subagente ciano usa outro modelo sem tocar no cache do pai.

Por que subagente resolve

O subagente tem o próprio prefixo e o próprio cache. Ele roda a subtarefa no modelo barato e devolve o resultado como texto — o loop principal continua no mesmo modelo, com o cache intacto.

Por modelo

Cache não cruza modelos

Trocar = reset

Invalida tudo

Subagente

Modelo barato à parte

Um por loop

Mantém o cache quente

4

🌿 Fork reusa o prefixo do pai

Uma operação lateral — um resumo, um subagente — que remonta system/tools/model diferentes perde o cache do pai. Para reaproveitar o prefixo quente, o fork precisa copiar system/tools/model verbatim e só anexar o específico no fim.

PAI — prefixo quente tools → system → model já cacheado ✓ copia verbatim + anexa no fim tools → system → model (idênticos) + tarefa reusa o cache do pai · só grava o bloco novo ✗ remonta system/tools/model diferentes paga o prefixo do zero

Legenda: o fork ciano herda o prefixo do pai (só grava o sufixo); o fork vermelho remonta e paga tudo de novo.

✓ Fork que reusa

  • Copia system byte a byte
  • Mantém a mesma lista de tools e o mesmo model
  • Anexa a instrução específica só no fim

✗ Fork que perde

  • Escreve um system novo "enxuto" pro resumo
  • Passa outro conjunto de tools ou outro modelo
  • Resultado: prefixo diferente, cache do zero
Verbatim

Copie system/tools/model

No fim

Anexe o específico

Herda

Reusa o cache do pai

Remontar

= pagar do zero

5

🔎 Checklist de silent invalidators

Um silent invalidator não dá erro — só faz cache_read_input_tokens ficar sempre 0. Antes de culpar o modelo, varra tudo que alimenta o prefixo e cace estes suspeitos.

Varra o que entra no prefixo tools system messages procurando variação relógio · uuid · sort · set

Legenda: qualquer fonte de variação no que vira tools/system muda o prefixo — e derruba o cache em silêncio.

Os 6 suspeitos de sempre

  • datetime.now() / Date.now() no system
  • uuid ou id aleatório no prefixo
  • json.dumps sem sort_keys=True
  • Iterar um set (ordem não garantida)
  • tools montadas por usuário/modo
  • Seção condicional no system (if flag: system += …)

Sintoma → causa

cache_read_input_tokens sempre 0 em requests repetidos = tem um invalidator. Faça o diff dos bytes do prefixo entre dois requests iguais e o byte que muda é o culpado (o tópico 4 do módulo 3.3 tem o prompt pronto pra isso).

Silencioso

Não dá erro

read = 0

O sintoma único

6 suspeitos

Varra o prefixo

Diff

Acha o byte culpado

6

🔥 Pré-aquecimento e TTL 1h

Uma requisição com max_tokens: 0 ao subir o app grava o cache do prefixo e retorna na hora — tira a latência do 1º request real. Só vale se três coisas forem verdade; e, pra tráfego com pausas longas, existe o TTL de 1h (write ~2×).

tempo app sobe max_tokens: 0 grava o cache · retorna na hora 1º usuário prefixo já quente → resposta rápida

Legenda: o pré-aquecimento paga o write antes do tráfego, pra o primeiro usuário não sentir a latência da gravação.

✓ Pré-aquecer vale quando

  • A 1ª latência é visível pro usuário
  • O prefixo é grande (vale a gravação)
  • Há um momento antes do tráfego (boot/deploy)

TTL de 1h

  • Write mais caro (~2× em vez de ~1,25×)
  • Vale pra tráfego com gaps > 5 min
  • Segura o cache quente entre pausas longas
max_tokens: 0

Grava sem gerar resposta

Antes do tráfego

Boot/deploy

TTL 1h

Gaps > 5 min

~2×

Write de 1h

Resumo do módulo

System congelado — nada de data/hora/nome; dinâmico vai pro fim das messages.
Tools determinísticas — ordene por nome; set/dict sem sort = miss.
Um modelo por loop — trocar modelo reseta; use subagente pra subtarefa barata.
Fork copia o pai — system/tools/model verbatim + específico no fim.
Checklist de invalidators — relógio, uuid, sort, set, tools por usuário, condicional no system.
Pré-aquecimento e TTL 1hmax_tokens: 0 no boot; 1h pra gaps longos.

Próximo módulo

3.3 — Prompts prontos (copy-run): o kit de 6 prompts pra auditar e corrigir cache no Claude Code.