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.