Domínio 4 — Prompt Engineering & Structured Output · Lição 7 de 7

Prompt caching

Objetivos de aprendizagem

O invariante: prefix match

Prompt caching é um casamento de prefixo. A chave do cache deriva dos bytes exatos do prompt renderizado até cada breakpoint. A ordem de renderização é sempre:

flowchart LR
    T[1. tools - definições de ferramentas] --> S[2. system - prompt de sistema]
    S --> M[3. messages - histórico da conversa]
    M --> V[conteúdo variável da requisição]
    style T fill:#1a7f5a,color:#fff
    style S fill:#1a7f5a,color:#fff
    style M fill:#1a7f5a,color:#fff
  

Qualquer byte alterado em uma posição invalida o cache dali para a frente. Consequências diretas:

cache_control e breakpoints

O marcador é cache_control: {"type": "ephemeral"} em um bloco de conteúdo (TTL padrão de 5 minutos; opcional "ttl": "1h"). São permitidos no máximo 4 breakpoints por requisição, colocados nas fronteiras de estabilidade (ex.: fim do system compartilhado; fim do último turno em conversas multi-turn). Escrita em cache custa ~1,25× o preço de input (2× para TTL de 1h); leitura custa ~0,1× — economia de até 90% no trecho cacheado.

import anthropic

client = anthropic.Anthropic()

LARGE_SYSTEM = "Você é um analista de contratos. Políticas da empresa:\n" + ("- regra...\n" * 200)

def ask(question: str):
    response = client.messages.create(
        model="claude-opus-5",
        max_tokens=1024,
        system=[{
            "type": "text",
            "text": LARGE_SYSTEM,                      # estável, byte-idêntico
            "cache_control": {"type": "ephemeral"},    # breakpoint no fim do prefixo estável
        }],
        messages=[{"role": "user", "content": question}],  # variável: depois do breakpoint
    )
    u = response.usage
    print("write:", u.cache_creation_input_tokens,
          "| read:", u.cache_read_input_tokens,
          "| uncached:", u.input_tokens)
    return next(b.text for b in response.content if b.type == "text")

ask("O contrato 12 permite rescisão unilateral?")   # 1ª chamada: cache write
ask("E o contrato 15, qual é a multa?")             # 2ª chamada: cache read (~0.1x)

Invalidadores silenciosos

A falha de caching mais cara em produção é silenciosa: as requisições continuam funcionando, só que 10× mais caras. Os suspeitos clássicos ao auditar o código que monta o prompt:

PadrãoPor que quebra o cache
datetime.now() / timestamp interpolado no systemO prefixo muda a cada requisição — nada após o timestamp é reutilizável.
UUID / request ID no início do conteúdoCada requisição vira um prefixo único.
json.dumps(...) sem sort_keys=True, iteração de setSerialização não determinística: os bytes mudam mesmo com o mesmo conteúdo.
Tool set variável por usuário/modo (tools=build_tools(user))Tools renderizam na posição 0: qualquer variação invalida tudo.
Seções condicionais no system (if flag: system += ...)Cada combinação de flags é um prefixo distinto.

Correção geral: mover o trecho dinâmico para depois do último breakpoint (fim das messages), torná-lo determinístico, ou removê-lo.

Mínimo cacheável por modelo

Prefixos abaixo do mínimo do modelo não cacheiam, silenciosamente — sem erro, apenas cache_creation_input_tokens: 0. O mínimo varia de 512 a 4096 tokens conforme o modelo (ex.: 512 em claude-opus-5; 1024 em Opus 4.8/Sonnet 4.5; 4096 em Opus 4.6/Haiku 4.5 — e o valor não é monotônico entre gerações). Um prompt de 3K tokens cacheia em um modelo e não cacheia em outro.

Verificação: usage

CampoSignificado
usage.cache_creation_input_tokensTokens escritos no cache nesta requisição (~1,25×).
usage.cache_read_input_tokensTokens servidos do cache (~0,1×). É o campo que prova que o cache funciona.
usage.input_tokensApenas o restante não cacheado (preço cheio). Tamanho total do prompt = soma dos três.

Se cache_read_input_tokens permanece 0 em requisições repetidas com prefixo supostamente idêntico, há um invalidador silencioso: logue os payloads e faça diff dos bytes entre requisições adjacentes. Verifique de novo a cada mudança no código de montagem do prompt — regressões de caching não geram erro, só conta mais alta.

Escopo na prova: o exam guide marca "detalhes de implementação de prompt caching" como fora de escopo além de saber que existe — mas custo/arquitetura de prompt (system estável primeiro, conteúdo volátil no fim, verificação via usage) aparecem em questões de arquitetura. Domine o modelo mental do prefixo e os campos de usage.

Pegadinhas da prova

Resumo em 5 linhas

  1. Caching é prefix match na ordem tools → system → messages; qualquer byte alterado invalida dali em diante.
  2. cache_control: {"type": "ephemeral"} (TTL 5 min; opcional 1h), máximo de 4 breakpoints por requisição.
  3. Invalidadores silenciosos: timestamp/UUID no system, JSON sem sort_keys, tool set variável, seções condicionais.
  4. Mínimo cacheável por modelo: 512–4096 tokens; abaixo disso o marcador é ignorado sem erro.
  5. Prova de funcionamento: usage.cache_read_input_tokens > 0 — verifique após cada mudança na montagem do prompt.

Documentação oficial

Questões de fixação

1. Um agente adiciona cache_control ao system de 8K tokens, mas cache_read_input_tokens é sempre 0. O system começa com f"Hoje é {datetime.now()}. Você é...". Qual é o diagnóstico?

2. Uma aplicação monta tools dinamicamente por usuário (cada um vê 3 das 10 ferramentas) e cacheia o system de 5K tokens. O custo não caiu. Por quê?

3. Como confirmar, com evidência objetiva, que o prompt caching está funcionando em produção?

4. Um prompt compartilhado de 450 tokens recebe cache_control, mas cache_creation_input_tokens retorna 0 em todos os modelos testados, sem erro. Qual é a explicação mais provável?