🏷️ Dois sentidos de "skill"
A palavra "skill" tem dois sentidos que custam coisas diferentes. (a) A descrição da skill — a linha curta que aparece na lista de skills — fica sempre no prefixo estável. (b) O conteúdo completo (o documentão) só entra quando a skill é invocada.
O que é
Uma skill se apresenta ao modelo por uma descrição curta (barata, sempre presente) e carrega seu corpo só sob demanda. São dois blocos com custos e momentos diferentes.
Por que aprender
Porque sem separar os dois você acha que "toda skill é cara". Na verdade, só o conteúdo pesa — e uma vez só, quando invocado.
Descrição (a)
Linha curta na lista de skills. Sempre no prefixo estável. Barata e constante.
Conteúdo (b)
O documento completo. Entra só quando a skill é invocada. Pesa uma vez, depois vira read barato.
Conceitos-chave
Descrição = sempre presente, barata.
Conteúdo = sob demanda, pesa 1×.
Invocar = trazer o conteúdo pra conversa.
Ter muitas skills listadas ≠ carregar todas.
➕ Skill no meio NÃO zera o cache
Quando uma skill é invocada no meio da conversa, o conteúdo dela entra anexado no FIM do histórico — não na posição 0. Tudo que veio antes continua válido e é lido barato; só o bloco novo da skill é gravado (um write). Nos turnos seguintes, ela já é read barato.
O que é
Anexar no fim preserva todo o prefixo anterior: o casamento de prefixo continua batendo até o ponto onde o conteúdo novo começa. Logo, não há reset — só uma gravação incremental.
Por que aprender
Porque tira o medo de invocar skills durante o trabalho. Aquele turno pode marcar uma % de cache mais baixa (o write da skill), mas é investimento único — vira barato já no próximo turno.
Mesma quantidade de conteúdo novo, resultados opostos: anexar no fim preserva o cache; alterar a posição 0 zera tudo.
Conceitos-chave
Anexa no fim = prefixo intacto.
Write único do bloco da skill.
Turnos seguintes = read barato.
Invocar skill ≠ resetar o cache.
💥 Quebrar a lista de tools = posição 0
As ferramentas são renderizadas na posição 0 — antes de system e do
histórico. Por isso mudar a lista tools
invalida TUDO: como o casamento de prefixo começa do primeiro byte,
qualquer alteração no bloco de ferramentas derruba o prefixo inteiro dali pra frente.
O que é
A lista tools ocupa a posição 0 na ordem de montagem tools → system → messages. Mudar qualquer byte ali é o pior caso possível para o cache.
Por que aprender
Porque é o silent invalidator mais caro. Saber que a posição 0 é sagrada muda como você conecta MCPs, edita descrições de tools e monta a lista.
Atenção
Não é só "adicionar tool". Remover, reordenar ou mudar um caractere numa descrição também conta. A posição 0 precisa ser byte-a-byte idêntica entre requests.
Conceitos-chave
tools = posição 0 do prompt.
Mudar tools = reset total.
Ordem: tools → system → messages.
Posição 0 = byte-a-byte idêntica.
🔨 Exemplos de quebra
Quatro formas clássicas de quebrar a posição 0 sem perceber. Todas mexem na lista
tools — de propósito ou por acidente.
Conectar/desconectar MCP no meio
Registra (ou remove) ferramentas novas na lista → a posição 0 muda → invalida tudo.
Mudar description ou input_schema
Basta uma palavra numa description ou um campo no schema — os bytes de tools mudam.
Ordem não-determinística
Montar tools de um set/dict sem ordenar → a ordem varia entre requests → miss silencioso. (É o assunto do exemplo copy-run abaixo.)
Ligar/desligar tools por modo/permissão
Trocar de modo que habilita/desabilita ferramentas altera a lista renderizada na posição 0.
O que é / Por que aprender
O que é: as quatro causas mais comuns de cache que "some" sem motivo aparente — todas alteram a posição 0.
Por que aprender: reconhecer o padrão faz você desconfiar da lista de tools primeiro quando cache_read_input_tokens zera.
Conceitos-chave
MCP conectado no meio = tools novas.
1 palavra na description já quebra.
set/dict sem sort = ordem variável.
Modo/permissão liga-desliga tools.
🔍 Tool Search anexa de propósito
O Tool Search carrega o schema de uma ferramenta
anexando — não trocando a lista tools.
Isso foi projetado justamente para NÃO quebrar o cache: descobrir uma
tool via Tool Search é seguro; conectar um MCP que reescreve a lista, não.
O que é
Tool Search traz o schema de uma ferramenta sob demanda, anexado no fim (como uma skill), preservando o prefixo. É o mesmo padrão "anexar no fim = seguro" aplicado a ferramentas.
Por que aprender
Porque distingue dois caminhos que parecem iguais: "trazer uma ferramenta nova pra conversa" pode ser seguro (Tool Search, anexa) ou destrutivo (reescrever tools, posição 0).
✓ Seguro
- ✓Descobrir tool via Tool Search (anexa no fim).
- ✓Invocar skill (anexa conteúdo no fim).
✗ Quebra
- ✗Conectar MCP que reescreve a lista de tools.
- ✗Editar description/schema de uma tool existente.
Conceitos-chave
Tool Search anexa o schema.
Feito para não quebrar o cache.
Descobrir tool ≠ reescrever tools.
Mesmo princípio das skills (fim = seguro).
👍 Regra de bolso
A frase que resolve 90% das decisões do dia a dia: conteúdo novo no FIM =
seguro. Mexer na lista de tools ou no
system prompt = reset da posição 0.
O que é
Um teste mental de uma linha: antes de qualquer mudança, pergunte "isso muda o começo do prompt ou só acrescenta no fim?". Fim = write barato; começo = reset.
Por que aprender
Porque você não precisa memorizar cada caso: a regra de bolso classifica qualquer ação nova em "seguro" ou "reset" na hora.
Dica prática
Pergunte sempre: "isso muda o começo ou só acrescenta no fim?" Se muda o começo (tools/system), espere um reset. Se acrescenta no fim (skill/Tool Search/nova mensagem), é write único e barato.
Exemplo copy-run — tornar a montagem de tools determinística
Objetivo: garantir que a lista tools (posição 0) saia byte-a-byte idêntica em todo request. Iterar um set/dict sem ordenar deixa a ordem variar → o cache nunca bate.
# ❌ QUEBRA — ordem varia entre requests (cache nunca bate)
tools = [tool_schema(name) for name in <meu_set_de_tools>]
# ^ set/dict: ordem não garantida
# ✅ ESTÁVEL — ordena por nome → mesma sequência de bytes sempre
tools = [tool_schema(name) for name in sorted(<meu_set_de_tools>)]
# (se as tools forem dicts, serialize com chaves ordenadas também)
# payload = json.dumps(tool, sort_keys=True)
Como verificar: rode o mesmo pedido duas vezes seguidas e olhe o usage. Com o sorted(...), o cache_read_input_tokens deixa de ser 0 entre requests iguais — sinal de que a posição 0 ficou idêntica e o prefixo bateu.
No arquivo <caminho que monta a lista de tools>, procure
onde `tools` é construída a partir de um set ou dict e:
1. ordene a serialização por nome (sorted / sort_keys=True);
2. confirme que a ordem fica estável entre dois requests;
3. me mostre o antes/depois do trecho.
Conceitos-chave
Fim = seguro (write único).
Começo (tools/system) = reset.
sorted(...) deixa a posição 0 estável.
cache_read > 0 entre requests iguais = ok.
Resumo do módulo
Próximo módulo:
2.3 — Invalidação a fundo