Pular para o conteúdo
18/30Capítulo 18 de 30

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:

TEXT
<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.

Antes 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.

As 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 texto

Em 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.

Esta é 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 mensagem

O resultado vira outro turno na conversa, em um papel reservado para ele. O modelo o lê como qualquer outro contexto.

Esse é 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.

Aqui está a ferramenta como a maioria das pessoas a escreve da primeira vez. Note que nada nela está errado; ela é apenas rasa:

tools/badFlights.tsTS
{
  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 chamadaJSON quebradodata em ISOaeroportos como IATAtudo correto
o schema acima24/2402/244/241/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.

Mesmo endpoint. Mesmo código por trás. Mesmo modelo, mesmos prompts, mesma decodificação. A única coisa que muda é o texto no schema:

tools/goodFlights.tsTS
{
  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 dataVALOR da dataFORMATO do aeroportoVALOR do aeroporto
schema raso2/241/244/244/24
schema descrito24/2412/2416/248/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 importa

O 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.

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 carregadasprompt tokensescolheu search_flightsdata em ISO
135324/2424/24
573024/2424/24
101,19321/2421/24
202,11924/2424/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 parte

Ajuda 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.

Agora 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.


As 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.

  1. 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.

Pronto para deixar a LIA escolher por você?

Crie com todos os modelos de IA em um só lugar — comece grátis hoje.