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

Tool Calling e outputs estruturados: o contrato que se mantém

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 pesquisa de voos e peça-lhe para encontrar um voo de Madrid para Berlim. Eis 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" quando espera um código de aeroporto, ou "3rd October 2026" quando espera uma data.

Essa lacuna — sintaticamente perfeita, semanticamente inutilizável — é o tema deste capítulo, e a primeira coisa a estabelecer é que não é um problema de JSON. Ao longo de vinte e quatro pedidos com esta ferramenta, o modelo produziu 24 chamadas de ferramenta válidas e zero JSON quebrado. Nem uma vez falhou na parte que toda a gente depura.

Antes da mecânica, a frase que evita a maior parte da confusão: uma chamada de ferramenta é um pedido, não uma ação.

O modelo emite uma mensagem estruturada que diz gostaria que search_flights fosse chamada com estes argumentos. Depois para. O seu código recebe essa mensagem, decide se a aceita, chama o que tiver de chamar e envia o resultado de volta como outra mensagem. O modelo nunca tocou na sua base de dados, nunca fez um pedido HTTP, nunca teve credenciais.

Tudo sobre segurança de agent no Capítulo 30 decorre dessa divisão, e o mesmo acontece com tudo sobre design de agent no Capítulo 23: o modelo propõe e o seu código dispõe, e o código é onde vivem todas as garantias.

Assim, uma ferramenta, despida de vocabulário, é duas coisas:

Um schema. Um JSON Schema que descreve uma função: o seu nome, o que faz e que argumentos recebe, com os respetivos tipos e restrições. Isto é o que entra no prompt, e é a única coisa que o modelo alguma vez vê.

Um endpoint. Uma função no seu código que recebe esses argumentos e devolve algo. O modelo nunca a vê, nunca sabe em que linguagem está escrita e não consegue distinguir uma query à base de dados de uma string codificada à mão.

As definições das ferramentas entram no prompt, serializadas no formato em que o modelo foi treinado. Custam tokens em todas as chamadas — um facto que volta mais à frente neste capítulo com um número.

Em vez de prosa, a resposta contém um pedido estruturado, e a API reporta um motivo de conclusão que o indica. Esse motivo importa: é assim que o seu código sabe que deve executar uma ferramenta em vez de mostrar uma resposta ao utilizador.

Este é o passo que não tem modelo nenhum. Valide os argumentos face ao schema, decida se quem chama tem permissão para fazer isto e execute.

O resultado torna-se mais um turno na conversa, num papel reservado para isso. O modelo lê-o como qualquer outro contexto.

Que é o loop do Capítulo 23, e a razão por que um único pedido pode transformar-se numa dúzia de idas e voltas.

Nada disto é emergente. Como o Capítulo 11 estabeleceu, tool calling é um comportamento treinado:1 durante o pós-treino, o modelo viu milhares de conversas com exatamente esta forma. É por isso que o formato é específico de cada modelo, que a fiabilidade varia tanto entre modelos de dimensão semelhante, e que um modelo consegue chamar uma ferramenta que nunca viu — a forma foi treinada, a ferramenta específica vem do seu prompt.

Aqui está a ferramenta como a maioria das pessoas a escreve primeiro. Note que nada nela está errado; é apenas fina:

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 pedidos, seis pares de cidades cruzados com quatro formas de expressar uma data ("the 3rd of next month", "next Friday", "15 December", "tomorrow"), decoding greedy para que os resultados se reproduzam:

ferramenta chamadaJSON quebradodata em ISOaeroportos como IATAtudo correto
o schema acima24/2402/244/241/24

Leia as duas primeiras colunas antes das últimas três. O modelo chama a ferramenta certa sempre e produz JSON bem formado sempre. 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 a pena insistir nisto porque determina onde procura quando algo falha. O instinto é acrescentar um parser de JSON com nova tentativa, ou pedir ao modelo com mais firmeza que produza JSON válido. Nenhuma dessas opções resolve algo que tenha acontecido aqui.

Mesmo endpoint. Mesmo código por trás. Mesmo modelo, mesmos prompts, mesmo decoding. 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 fino2/241/244/244/24
schema descrito24/2412/2416/248/24

O formato da data passa de 2 em 24 para 24 em 24. Perfeito, a partir de uma alteração de texto, sem tocar no código e sem lógica de nova tentativa. Se retirar um hábito operacional deste capítulo, é este: quando uma ferramenta é chamada incorretamente, a correção está quase sempre na descrição, e é a correção mais barata do sistema.

Agora leia a segunda coluna, que é a metade mais importante.

Um schema restringe a forma. Não consegue fornecer conhecimento.

Ligação para a secção: Um schema restringe a forma. Não consegue fornecer conhecimento.

A data está em formato ISO 24 vezes em 24. É o dia certo 12 vezes em 24.

Portanto, metade das chamadas leva agora uma data perfeitamente formatada que é a data errada. A descrição disse ao modelo que forma produzir, e o modelo produziu-a sem falhas — mas transformar "next Friday" em 2026-09-11 exige saber a data de hoje e fazer aritmética de calendário, e nenhuma descrição fornece isso. A mesma história para aeroportos: o formato passou de 4 para 16, mas o valor apenas de 4 para 8, porque escrever MAD exige saber que o aeroporto de Madrid é MAD.

Essa distinção é a ideia que sustenta o capítulo:

Um schema é um contrato sobre forma. Pode tornar o output do modelo parseável, tipado e consistente. Não consegue torná-lo verdadeiro, e todos os modos de falha que sobrevivem a um bom schema são falhas de conhecimento, não falhas de formato.

As duas coisas precisam de correções diferentes, e confundi-las desperdiça semanas. Falhas de formato corrigem-se na descrição ou com decoding restringido, abaixo. Falhas de conhecimento corrigem-se pondo o conhecimento no prompt — a data atual na mensagem de sistema, uma pesquisa de aeroportos como segunda ferramenta que o modelo chama primeiro, um enum no schema quando o conjunto é suficientemente pequeno para enumerar. Repare no que as três têm em comum: tiram o problema da memória do modelo e colocam-no no seu input, que é todo o Capítulo 24.

Outputs estruturados, e o que é realmente "decoding restringido"

Ligação para a secção: Outputs estruturados, e o que é realmente "decoding restringido"

Tudo acima ainda depende de o modelo escolher produzir a forma certa. Há uma garantia mais forte disponível, e é a melhor recompensa do Capítulo 17.

Recorde como a geração funciona: a cada passo, o modelo produz um logit para cada token no vocabulário, e o amostrador escolhe um. Decoding restringido insere um passo pelo meio. Dada uma gramática — derivada do seu JSON Schema — calcula que tokens poderiam vir a seguir legalmente, 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 tem de ser um {, então todo token que não seja { 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.

É isto que está por baixo de "outputs estruturados", "modo JSON" e "geração guiada", e é isto que explica as suas duas propriedades. A garantia é total para tudo o que a gramática consegue expressar — tipos, campos obrigatórios, enums, aninhamento — porque é aplicada mecanicamente em vez de pedida com educação. E não diz nada sobre o conteúdo: uma gramática consegue forçar "date" a ser uma string que corresponde a um padrão de data, e não consegue forçá-la a ser o dia certo. Que é a mesma parede da secção anterior, vista do outro lado.

Duas notas práticas. Não é grátis: a máscara tem de ser calculada a cada passo, e gramáticas complexas custam latência mensurável. E muda o que o modelo está a fazer — um modelo desviado do seu token preferido pode produzir pior conteúdo enquanto produz uma estrutura perfeita, razão pela qual "pedir com jeitinho e validar" continua a ser um padrão razoável para formas simples, e o decoding restringido justifica o custo quando a forma é complexa ou o consumidor é estrito.

O Capítulo 14 mediu um timeout seguido de uma nova tentativa a cobrar duas gerações por uma resposta. Com ferramentas, a mesma falha piora, porque uma ferramenta pode fazer algo.

Se o seu código chama charge_card, atinge um timeout e tenta de novo, tem duas cobranças. O modelo não faz ideia de que isto aconteceu; vê um único 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 devolva o seu resultado em vez de fazer o trabalho outra vez.

A regra de design que se segue merece ser dita sem rodeios. Separe leituras de escritas no seu catálogo de ferramentas. Uma leitura pode ser repetida livremente, executada em paralelo e guardada em cache. Uma escrita não pode, e deve levar uma chave, uma verificação de permissões e — para qualquer coisa sobre a qual um utilizador gostaria de saber antes de acontecer — um passo de aprovação que coloca um humano entre o pedido e a ação. Esse passo de aprovação não é uma cortesia: é uma das poucas coisas entre uma prompt injection e uma consequência real — e, como mede o Capítulo 30, a mais fraca delas.

O folclore diz que carregar muitas ferramentas faz o modelo escolher mal. Vale a pena medir em vez de repetir, portanto: os mesmos vinte e quatro pedidos, com a ferramenta de voos mais um conjunto crescente de outras — incluindo três deliberadamente confundíveis (horários de comboios, travessias de ferry, rotas de autocarros).

ferramentas carregadastokens de promptescolheu 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 bilião de parâmetros escolheu a certa vinte e quatro vezes em vinte e quatro. A quebra aos dez são três chamadas que nomearam uma ferramenta diferente, e não sobrevive à passagem para vinte.

Esse é um resultado negativo e deve ser reportado como tal: nesta tarefa, com estas ferramentas, "demasiadas ferramentas" não foi o problema. O que cresceu, monotonamente e por um fator de seis, foi o prompt: de 353 tokens para 2,119, pagos em todos os pedidos da conversa, para sempre, quer alguma ferramenta seja usada ou não.

Portanto, 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 a uma fatura ao longo de quarenta turnos. Quando as pessoas reportam que muitas ferramentas prejudicam a qualidade, o mecanismo costuma ser que as definições expulsaram o contexto que importava — o que é um problema do Capítulo 24 vestido de Capítulo 18. Ferramentas que são verdadeiramente quase duplicadas umas das outras também são um problema real, e a correção para essas não é menos ferramentas, mas melhores descrições e namespaces: prefixe-as por sistema (crm.search_customer, billing.search_customer) para que dois catálogos fundidos de duas equipas não colidam, e para que o modelo tenha algo com que discriminar.

Três tipos de ferramenta, e a que abre a próxima parte

Ligação para a secção: Três tipos de ferramenta, e a que abre a próxima parte

Ajuda ordenar as ferramentas pelo que fazem ao mundo, porque a engenharia difere em cada caso.

Ferramentas de dados leem: pesquisam, obtêm, consultam. Repetíveis, paralelizáveis, armazenáveis em cache. Falham ao devolver nada útil, e o principal risco é trazerem texto não fiável para o contexto — que é toda a superfície de ataque do Capítulo 30.

Ferramentas de ação escrevem: enviam, criam, cobram, apagam. Não repetíveis sem uma chave, não paralelizáveis com segurança, e a razão pela qual existem fluxos de aprovação.

Ferramentas de orquestração chamam outros modelos. Uma ferramenta cuja implementação é outro agent, com o seu próprio prompt, as suas próprias ferramentas e o seu próprio loop — e, para o modelo que chama, parece exatamente igual às outras duas, porque um schema e um endpoint são tudo o que alguma vez vê.

Esse terceiro tipo não é uma curiosidade. É o mecanismo por trás da metade agent-como-ferramenta 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 caber um agent inteiro por trás.

Agora tem um modelo que consegue pedir coisas, e um contrato que torna o pedido parseável. O que não tem é algo sobre o qual ele possa perguntar para além do que cabe no seu prompt.

A ferramenta mais comum em produção, com larga margem, é uma pesquisa sobre um corpo de texto que o modelo nunca viu durante o treino: a sua documentação, os seus tickets, os seus contratos. Isto soa a problema resolvido — fazer embed, encontrar os vizinhos mais próximos, colá-los — e as partes que não estão resolvidas são as que decidem se a resposta é fiável: como o texto é cortado antes de ser embedded, que limiar de similaridade é suficientemente baixo para significar não sei, e como uma citação fica ligada a uma afirmação para que um leitor a possa verificar.

O Capítulo 19 é sobre retrieval, e é o capítulo em que uma resposta errada deixa de ser uma curiosidade e passa a ser uma responsabilidade.


As medições neste capítulo vêm de Qwen/Qwen2.5-0.5B-Instruct com decoding greedy, sobre 24 pedidos gerados que cruzam seis pares de cidades com quatro formulações de data, usando o chat template próprio do modelo para definições de ferramentas. Reproduzem-se exatamente, e são um modelo pequeno: leia a divisão formato/valor como uma demonstração do mecanismo, não como um benchmark do que os modelos atuais fazem. Um modelo de fronteira resolve "next Friday" corretamente com muito mais frequência — e ainda assim não pode ser obrigado a fazê-lo por um schema, que é a parte que generaliza.

O vocabulário de JSON Schema usado acima (type, properties, required, pattern, format, enum) é especificado no draft do JSON Schema que a documentação do seu fornecedor indicar; o subconjunto útil é pequeno e igual entre fornecedores, e as diferenças que existem — que palavras-chave são impostas por decoding restringido em vez de meramente passadas ao modelo — merecem ser lidas no guia de structured-output do fornecedor, não assumidas.

Para decoding restringido como técnica, as bibliotecas ao estilo guidance e o projeto outlines documentam a construção gramática-para-máscara-de-logit de uma forma 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 lê-o linha a linha.

  1. Ouyang, L. et al. Training language models to follow instructions with human feedback. arXiv:2203.02155 (2022). O artigo que tornou standard a receita de pós-treino; a forma de uma chamada de ferramenta é aprendida aí, a partir de demonstrações, exatamente como a forma de uma resposta.

Pronto para deixar a LIA escolher?

Construa com todos os modelos de IA num só sítio — comece grátis hoje.