antonio leandro

claude, na prática

Implementing tool use

tutorial · 02 · ferramentas · Anthropic ·

a tese

o schema só garante o formato; quem decide se o modelo chama a ferramenta certa é a prosa que você escreveu no campo description — tool definition é documentação executável, não assinatura de tipo

o que fica

  1. A description da tool é o fator que mais afeta a performance de tool use — a própria documentação diz isso com todas as letras e recomenda no mínimo 3 a 4 frases por ferramenta.
  2. Forçar tool use faz a API prefillar a mensagem do assistente: o modelo não emite texto nem explicação antes dos blocos tool_use, mesmo que você peça explicitamente.
  3. `tool_choice` com `any` ou `tool` não funciona em todo lugar: thinking manual e os modelos Fable 5.1 e Mythos 5.1 devolvem erro, e a saída é `auto` com strict tool use ou structured outputs.
  4. Mudar o `tool_choice` invalida os blocos de mensagem no prompt cache, embora as definições de tool e o system prompt continuem cacheados.
  5. Poucas ferramentas capazes batem muitas ferramentas pequenas: três tools para criar, revisar e mergear PR viram uma com parâmetro `action`, e a ambiguidade de seleção cai.
  6. input_examples são validados contra o input_schema, quebram a requisição com 400 se forem inválidos, e custam de 20 a 50 tokens por exemplo simples e de 100 a 200 por objeto aninhado.

o problema

Antes de existir um parâmetro tools na API, quem queria que o modelo chamasse uma função fazia o óbvio: pedia no prompt que ele respondesse em json, torcia para o json sair bem formado e parseava o resultado. Isso resolve mal a parte fácil — o formato — e não resolve a parte difícil: decidir quando chamar. O modelo não sabe que sua função de cotação só cobre bolsa americana, não sabe se ela devolve dólar ou real, não sabe se ela também traz o volume negociado. Ele adivinha.

O que a página deixa claro é que passar a declarar tools num campo estruturado move o problema de lugar, não o elimina. O parsing vira responsabilidade da API. Sobra a especificação. E a falha de especificação não estoura como exceção: ela aparece como a ferramenta errada chamada com o argumento plausível, silenciosamente. O contraste que a doc usa é uma description ruim — “Gets the stock price for a ticker.” — contra uma boa, que diz em qual bolsa, em qual moeda, quando usar e o que a ferramenta não devolve.

a ideia

Uma tool definition tem três partes obrigatórias: name, que precisa casar com ^[a-zA-Z0-9_-]{1,64}$; description, texto plano; e input_schema, um JSON Schema. Só uma delas é verificável por máquina. A description é onde mora todo o comportamento que o schema não consegue expressar.

A analogia honesta é a de assinatura e docstring. O input_schema é a assinatura: diz que existe um campo location do tipo string e que ele é obrigatório. A description é a docstring — com a diferença de que aqui quem lê a docstring é o próprio chamador, e ele lê antes de decidir se chama. Daí as outras recomendações da página serem de design de api, não de prompt: consolidar operações relacionadas numa tool com parâmetro action em vez de espalhar create_pr, review_pr e merge_pr; prefixar nomes por serviço (github_list_prs, slack_send_message); e desenhar a resposta da ferramenta para devolver identificadores estáveis e só os campos necessários, porque resposta inchada queima context.

como funciona

As tools vão no parâmetro top-level tools. A API monta um system prompt especial a partir das definições, da configuração de tools e do seu system prompt. Vale ler o texto desse prompt construído, que a doc publica: ele avisa que a saída “is not expected to be valid XML and is parsed with regular expressions”. O canal é texto, não um transporte estruturado.

{
  "name": "get_weather",
  "description": "Get the current weather in a given location",
  "input_schema": {
    "type": "object",
    "properties": {
      "location": {"type": "string", "description": "The city and state, e.g. San Francisco, CA"},
      "unit": {"type": "string", "enum": ["celsius", "fahrenheit"]}
    },
    "required": ["location"]
  },
  "input_examples": [
    {"location": "San Francisco, CA", "unit": "fahrenheit"},
    {"location": "New York, NY"}
  ]
}

input_examples é opcional e entra no prompt junto do schema, mostrando padrões concretos de chamada bem formada — inclusive quando incluir parâmetro opcional, como no terceiro exemplo, que omite unit de propósito. Cada exemplo é validado contra o schema; exemplo inválido devolve 400.

O controle de saída fica em tool_choice, com quatro valores: auto (padrão quando há tools), any (alguma tool, o modelo escolhe qual), tool (uma tool específica) e none (padrão quando não há tools). Com any ou tool, a API prefilla a mensagem do assistente, e é isso que elimina o texto introdutório. Nas definições ainda cabem propriedades opcionais como cache_control, strict, defer_loading e allowed_callers; strict: true combinado com tool_choice: any garante ao mesmo tempo que alguma tool é chamada e que o input respeita o schema.

o que isso custou

Forçar tool use não é universal. Com extended thinking manual, any e tool dão erro — só auto e none passam. Claude Fable 5.1 e Claude Mythos 5.1 devolvem 400 para os dois. Nesses casos o caminho é auto com strict tool use, ou structured outputs quando o que você precisa é uma forma de json fixa. Thinking adaptativo, incluindo modelos com thinking ligado por padrão como o Opus 5, suporta forced tool use.

Forçar também tem preço de produto: o prefill mata o preâmbulo. Se você quer que o modelo explique o que vai fazer antes de agir, precisa ficar em auto e pedir a ferramenta na mensagem do usuário. A doc afirma que testes não mostram perda de performance com o prefill, mas a perda de contexto para o usuário final é sua.

Os exemplos custam tokens em toda requisição, e não valem para server tools nem para os client toolsets de computer use e browser use. Trocar tool_choice invalida os blocos de mensagem cacheados. E a description, que é a peça mais importante, é a única que nenhum validador cobre: não existe teste que reprove uma description ruim.

onde isso aparece hoje

O trio nome, descrição e JSON Schema virou o vocabulário comum de function calling. Em cima dele a própria página encadeia a maquinaria derivada: tool search, que torna o namespacing dos nomes decisivo quando a biblioteca cresce; defer_loading, para não pagar todas as definições de uma vez; strict tool use; e um Tool Runner no SDK que roda o loop agêntico por você. Todo agente é este loop repetido — e o que decide a qualidade dele continua sendo o parágrafo que você escreveu na description.

lido na íntegra por pipeline de llm, revisado por antonio leandro antes de publicar ·