Tool calling e saídas estruturadas: o contrato que não quebra
Vinte e quatro chamadas, zero JSON quebrado e duas datas úteis. Depois, o mesmo endpoint com uma descrição melhor — e o que um schema não corrige.
Nesta página
Dê a um modelo uma ferramenta de busca de voos e peça para ele encontrar um voo de Madri a Berlim. Aqui está o que volta:
<tool_call>
{"name": "search_flights",
"arguments": {"from": "Madrid", "to": "Berlin", "date": "3rd October 2026"}}
</tool_call>O JSON é válido. O nome da ferramenta está certo. Todos os campos obrigatórios estão presentes. E a chamada é inútil: nenhuma API de voos aceita "Madrid" onde espera um código de aeroporto, nem "3rd October 2026" onde espera uma data.
Essa lacuna — sintaticamente perfeita, semanticamente inutilizável — é o tema deste capítulo, e a primeira coisa a deixar clara é que isso não é um problema de JSON. Em vinte e quatro solicitações com essa ferramenta, o modelo produziu 24 chamadas de ferramenta válidas e zero JSON quebrado. Ele não falhou nenhuma vez na parte que todo mundo depura.
O modelo não executa nada
Link para a seção: O modelo não executa nadaAntes da mecânica, a frase que evita a maior parte da confusão: uma chamada de ferramenta é uma solicitação, não uma ação.
O modelo emite uma mensagem estruturada dizendo eu gostaria que search_flights fosse chamada com estes argumentos. Então ele para. Seu código recebe essa mensagem, decide se vai aceitá-la, chama o que tiver de chamar e envia o resultado de volta como outra mensagem. O modelo nunca tocou seu banco de dados, nunca fez uma solicitação HTTP, nunca teve credenciais.
Tudo sobre segurança de agent no Capítulo 30 decorre dessa divisão, e o mesmo vale para tudo sobre design de agent no Capítulo 23: o modelo propõe e seu código dispõe, e é no código que todas as garantias vivem.
Então uma ferramenta, sem o vocabulário, é duas coisas:
Um schema. Um JSON Schema que descreve uma função: seu nome, o que ela faz e quais argumentos recebe, com seus tipos e restrições. Isso entra no prompt, e é a única coisa que o modelo chega a ver.
Um endpoint. Uma função no seu código que recebe esses argumentos e retorna algo. O modelo nunca a vê, nunca sabe em que linguagem ela foi escrita e não consegue distinguir uma consulta ao banco de dados de uma string hardcoded.
Você envia os schemas com a solicitação
Link para a seção: Você envia os schemas com a solicitaçãoAs definições das ferramentas entram no prompt, serializadas no formato em que o modelo foi treinado. Elas custam tokens em cada chamada — um fato que volta com um número mais adiante neste capítulo.
O modelo responde com uma chamada em vez de texto
Link para a seção: O modelo responde com uma chamada em vez de textoEm vez de prosa, a resposta contém uma solicitação estruturada, e a API informa um motivo de encerramento dizendo isso. O motivo importa: é assim que seu código sabe que deve executar uma ferramenta em vez de mostrar uma resposta ao usuário.
Seu código executa — ou recusa
Link para a seção: Seu código executa — ou recusaEsta é a etapa em que não há modelo. Valide os argumentos contra o schema, decida se esse chamador tem permissão para fazer isso e execute.
Você envia o resultado de volta como uma mensagem
Link para a seção: Você envia o resultado de volta como uma mensagemO resultado vira outro turno na conversa, em um papel reservado para ele. O modelo o lê como qualquer outro contexto.
O modelo responde, ou pede outra ferramenta
Link para a seção: O modelo responde, ou pede outra ferramentaEsse é o loop do Capítulo 23, e o motivo pelo qual uma única solicitação pode virar uma dúzia de idas e voltas.
Nada disso é emergente. Como o Capítulo 11 estabeleceu, tool calling é um comportamento treinado:1 durante o pós-treinamento, o modelo viu milhares de conversas moldadas exatamente assim. É por isso que o formato é específico do modelo, por que a confiabilidade varia tanto entre modelos de tamanho semelhante e por que um modelo consegue chamar uma ferramenta que nunca viu — o formato foi treinado, a ferramenta específica vem do seu prompt.
O custo de um schema ruim, medido
Link para a seção: O custo de um schema ruim, medidoAqui está a ferramenta como a maioria das pessoas a escreve da primeira vez. Note que nada nela está errado; ela é apenas rasa:
{
name: "search_flights",
description: "Search for flights.",
parameters: {
type: "object",
properties: {
from: { type: "string", description: "Airport." },
to: { type: "string", description: "Airport." },
date: { type: "string", description: "The date." },
},
required: ["from", "to", "date"],
},
}Vinte e quatro solicitações, seis pares de cidades cruzados com quatro formas de expressar uma data ("dia 3 do mês que vem", "próxima sexta", "15 de dezembro", "amanhã"), greedy decoding para que os resultados sejam reproduzíveis:
| ferramenta chamada | JSON quebrado | data em ISO | aeroportos como IATA | tudo correto | |
|---|---|---|---|---|---|
| o schema acima | 24/24 | 0 | 2/24 | 4/24 | 1/24 |
Leia as duas primeiras colunas antes das três últimas. O modelo chama a ferramenta certa todas as vezes e produz JSON bem formado todas as vezes. A falha está inteiramente nos valores, e os valores são inutilizáveis: "Madrid" em vez de MAD, "3rd October 2026" em vez de 2026-10-03.
Vale insistir nisso porque é o que determina onde você procura quando algo quebra. O instinto é adicionar um parser de JSON com retry, ou pedir ao modelo com mais firmeza que produza JSON válido. Nenhuma dessas coisas trata nada do que aconteceu aqui.
Agora mude apenas a descrição
Link para a seção: Agora mude apenas a descriçãoMesmo endpoint. Mesmo código por trás. Mesmo modelo, mesmos prompts, mesma decodificação. A única coisa que muda é o texto no schema:
{
name: "search_flights",
description: "Search scheduled flights between two airports on a given day.",
parameters: {
type: "object",
properties: {
from: {
type: "string",
description: "Departure airport as a three-letter IATA code, e.g. MAD for Madrid. Never a city name.",
pattern: "^[A-Z]{3}$",
},
to: { /* same */ },
date: {
type: "string",
description: "Departure date as an ISO 8601 calendar date, YYYY-MM-DD. Resolve relative dates against today before calling.",
format: "date",
pattern: "^\\d{4}-\\d{2}-\\d{2}$",
},
},
required: ["from", "to", "date"],
},
}| FORMATO da data | VALOR da data | FORMATO do aeroporto | VALOR do aeroporto | |
|---|---|---|---|---|
| schema raso | 2/24 | 1/24 | 4/24 | 4/24 |
| schema descrito | 24/24 | 12/24 | 16/24 | 8/24 |
O formato da data vai de 2 em 24 para 24 em 24. Perfeito, a partir de uma mudança de texto, sem tocar no código e sem lógica de retry. Se você levar um hábito operacional deste capítulo, que seja este: quando uma ferramenta é chamada do jeito errado, a correção quase sempre está na descrição, e é a correção mais barata do sistema.
Agora leia a segunda coluna, que é a metade mais importante.
Um schema restringe o formato. Ele não consegue fornecer conhecimento.
Link para a seção: Um schema restringe o formato. Ele não consegue fornecer conhecimento.A data está em formato ISO 24 vezes em 24. Ela é o dia certo 12 vezes em 24.
Então metade das chamadas agora carrega uma data perfeitamente formatada que é a data errada. A descrição disse ao modelo qual formato produzir, e o modelo o produziu impecavelmente — mas transformar "próxima sexta" em 2026-09-11 exige saber a data de hoje e fazer aritmética de calendário, e nenhuma quantidade de descrição fornece isso. A mesma história vale para aeroportos: o formato foi de 4 para 16, mas o valor só foi de 4 para 8, porque escrever MAD exige saber que o aeroporto de Madri é MAD.
Essa distinção é a ideia que sustenta o capítulo:
Um schema é um contrato sobre forma. Ele pode tornar a saída do modelo parseável, tipada e consistente. Ele não consegue torná-la verdadeira, e todo modo de falha que sobrevive a um bom schema é uma falha de conhecimento, não uma falha de formato.
As duas coisas precisam de correções diferentes, e confundi-las desperdiça semanas. Falhas de formato são corrigidas na descrição ou com decodificação restrita, abaixo. Falhas de conhecimento são corrigidas colocando o conhecimento no prompt — a data atual na mensagem de sistema, uma consulta de aeroporto como uma segunda ferramenta que o modelo chama primeiro, um enum no schema quando o conjunto é pequeno o bastante para enumerar. Note o que as três têm em comum: elas tiram o problema da memória do modelo e o colocam na entrada dele, que é o tema inteiro do Capítulo 24.
Saídas estruturadas, e o que "decodificação restrita" realmente é
Link para a seção: Saídas estruturadas, e o que "decodificação restrita" realmente éTudo acima ainda depende de o modelo escolher produzir o formato certo. Existe uma garantia mais forte disponível, e ela é o melhor retorno do Capítulo 17.
Lembre como a geração funciona: a cada etapa, o modelo produz um logit para cada token do vocabulário, e o amostrador escolhe um. Decodificação restrita insere uma etapa no meio. Dada uma gramática — derivada do seu JSON Schema — ela calcula quais tokens poderiam vir legalmente em seguida, define os logits de todos os outros como infinito negativo e deixa o amostrador escolher entre o que resta.
Se o schema diz que a próxima coisa precisa ser um {, então todo token que não é { tem probabilidade zero. Não "improvável": zero. O modelo não consegue emitir JSON inválido porque os tokens inválidos foram removidos da distribuição antes da amostragem.
É isso que "saídas estruturadas", "modo JSON" e "geração guiada" são por baixo, e isso explica suas duas propriedades. A garantia é total para qualquer coisa que a gramática consiga expressar — tipos, campos obrigatórios, enums, aninhamento — porque ela é imposta mecanicamente, não solicitada com educação. E ela não diz nada sobre o conteúdo: uma gramática pode forçar "date" a ser uma string que corresponda a um padrão de data, mas não pode forçá-la a ser o dia certo. É o mesmo muro da seção anterior, alcançado pelo outro lado.
Duas notas práticas. Não é de graça: a máscara precisa ser calculada a cada etapa, e gramáticas complexas custam latência mensurável. E isso muda o que o modelo está fazendo — um modelo desviado de seu token preferido pode produzir conteúdo pior enquanto produz estrutura perfeita, razão pela qual "pedir com jeito e validar" ainda é um padrão razoável para formatos simples, e a decodificação restrita justifica seu custo quando o formato é complexo ou o consumidor é estrito.
Efeitos colaterais, e a única propriedade que importa
Link para a seção: Efeitos colaterais, e a única propriedade que importaO Capítulo 14 mediu um timeout seguido de um retry cobrando duas gerações por uma resposta. Com ferramentas, a mesma falha fica pior, porque uma ferramenta pode fazer algo.
Se seu código chama charge_card, dá timeout e tenta de novo, você tem duas cobranças. O modelo não faz ideia de que algo disso aconteceu; ele vê um resultado de ferramenta. A correção é a mesma de qualquer sistema distribuído e não é problema do modelo: torne a operação idempotente dando uma chave à chamada, para que a segunda execução reconheça a primeira e retorne seu resultado em vez de fazer o trabalho de novo.
A regra de design que decorre disso merece ser dita claramente. Separe leituras de escritas no seu catálogo de ferramentas. Uma leitura pode ser repetida livremente, executada em paralelo e armazenada em cache. Uma escrita não pode, e deve carregar uma chave, uma verificação de permissão e — para qualquer coisa que um usuário gostaria de saber antes que aconteça — uma etapa de aprovação que coloca uma pessoa entre a solicitação e a ação. Essa etapa de aprovação não é uma gentileza: é uma das poucas coisas entre um prompt injection e uma consequência real — e, como o Capítulo 30 mede, a mais fraca delas.
Quantas ferramentas antes de degradar?
Link para a seção: Quantas ferramentas antes de degradar?O folclore diz que carregar muitas ferramentas faz o modelo escolher mal. Vale medir em vez de repetir, então: as mesmas vinte e quatro solicitações, com a ferramenta de voos mais um conjunto crescente de outras — incluindo três deliberadamente confundíveis (horários de trem, travessias de balsa, rotas de ônibus).
| ferramentas carregadas | prompt tokens | escolheu search_flights | data em ISO |
|---|---|---|---|
| 1 | 353 | 24/24 | 24/24 |
| 5 | 730 | 24/24 | 24/24 |
| 10 | 1,193 | 21/24 | 21/24 |
| 20 | 2,119 | 24/24 | 24/24 |
A seleção não degradou. Com vinte ferramentas, três delas plausivelmente confundíveis, um modelo de meio bilhão de parâmetros escolheu a correta vinte e quatro vezes em vinte e quatro. A queda em dez são três chamadas que nomearam uma ferramenta diferente, e ela não se mantém quando se vai para vinte.
Esse é um resultado negativo e deve ser relatado como tal: nesta tarefa, com estas ferramentas, "ferramentas demais" não foi o problema. O que cresceu, monotonicamente e por um fator de seis, foi o prompt: de 353 tokens para 2,119, pagos em cada solicitação da conversa, para sempre, com ou sem uso de qualquer ferramenta.
Então a versão honesta do folclore é sobre custo e contexto, não precisão. Vinte ferramentas são um imposto permanente sobre cada mensagem, e o Capítulo 16 já mostrou o que um prefixo permanente faz com uma conta ao longo de quarenta turnos. Quando as pessoas relatam que muitas ferramentas prejudicam a qualidade, o mecanismo geralmente é que as definições expulsaram o contexto que importava — o que é um problema do Capítulo 24 fantasiado de Capítulo 18. Ferramentas que são genuinamente quase duplicatas também são um problema real, e a correção para elas não é menos ferramentas, mas descrições e namespaces melhores: prefixe-as por sistema (crm.search_customer, billing.search_customer) para que dois catálogos mesclados de duas equipes não colidam, e para que o modelo tenha algo pelo qual discriminar.
Três tipos de ferramenta, e a que abre a próxima parte
Link para a seção: Três tipos de ferramenta, e a que abre a próxima parteAjuda classificar ferramentas pelo que elas fazem com o mundo, porque a engenharia difere em cada caso.
Ferramentas de dados leem: pesquisar, buscar, consultar. Podem ser repetidas, paralelizadas e cacheadas. Elas falham retornando nada útil, e seu principal risco é trazer texto não confiável para o contexto — que é toda a superfície de ataque do Capítulo 30.
Ferramentas de ação escrevem: enviar, criar, cobrar, excluir. Não podem ser repetidas sem uma chave, não podem ser paralelizadas com segurança e são o motivo pelo qual fluxos de aprovação existem.
Ferramentas de orquestração chamam outros modelos. Uma ferramenta cuja implementação é outro agent, com seu próprio prompt, suas próprias ferramentas e seu próprio loop — e, para o modelo que a chama, ela se parece exatamente com as outras duas, porque um schema e um endpoint são tudo que ele chega a ver.
Esse terceiro tipo não é uma curiosidade. É o mecanismo por trás da metade agent-as-a-tool do Capítulo 25 — a outra topologia, a transferência, entrega a conversa e nunca a recebe de volta — e funciona precisamente porque a interface deste capítulo é estreita o suficiente para que um agent inteiro caiba atrás dela.
Para onde vamos agora
Link para a seção: Para onde vamos agoraAgora você tem um modelo que pode pedir coisas, e um contrato que torna o pedido parseável. O que você não tem é algo sobre o que ele possa perguntar além do que cabe em seu prompt.
A ferramenta mais comum em produção, por uma margem enorme, é uma busca em um corpo de texto que o modelo nunca viu durante o treinamento: sua documentação, seus tickets, seus contratos. Isso parece um problema resolvido — gerar embedding, encontrar os vizinhos mais próximos, colar no prompt — e as partes que não estão resolvidas são as que decidem se a resposta é confiável: como o texto é cortado antes de ser embedded, qual limiar de similaridade é baixo o bastante para significar eu não sei, e como uma citação é anexada a uma afirmação para que uma pessoa possa conferi-la.
O Capítulo 19 é sobre retrieval, e é o capítulo em que uma resposta errada deixa de ser curiosidade e passa a ser responsabilidade.
Fontes e método
Link para a seção: Fontes e métodoAs medições neste capítulo vêm de Qwen/Qwen2.5-0.5B-Instruct com greedy decoding, ao longo de 24 solicitações geradas cruzando seis pares de cidades com quatro formulações de data, usando o template de chat do próprio modelo para definições de ferramentas. Elas se reproduzem exatamente, e são de um modelo pequeno: leia a separação formato/valor como uma demonstração do mecanismo, não como um benchmark do que modelos atuais fazem. Um modelo de fronteira resolve "próxima sexta" corretamente com muito mais frequência — e ainda assim não pode ser forçado a isso por um schema, que é a parte que generaliza.
O vocabulário JSON Schema usado acima (type, properties, required, pattern, format, enum) é especificado no draft do JSON Schema que a documentação do seu provedor nomeia; o subconjunto útil é pequeno e igual entre provedores, e as diferenças que existem — quais keywords são impostas por decodificação restrita em vez de apenas passadas ao modelo — valem a leitura no guia de saídas estruturadas do provedor, em vez de serem presumidas.
Para decodificação restrita como técnica, as bibliotecas no estilo guidance e o projeto outlines documentam a construção de gramática para máscara de logit de um jeito que mapeia diretamente para o amostrador do Capítulo 17. E, para a ida e volta em si, a especificação mais clara não é um tutorial, mas um protocolo: o Capítulo 26 o lê linha por linha.
Referências
Link para a seção: Referências-
Ouyang, L. et al. Training language models to follow instructions with human feedback. arXiv:2203.02155 (2022). O artigo que tornou padrão a receita de pós-treinamento; o formato de uma chamada de ferramenta é aprendido ali, a partir de demonstrações, exatamente como o formato de uma resposta. ↩