Saltar ao contido
18/30Capítulo 18 de 30

Tool Calling e saídas estruturadas: o contrato que aguanta

Vinte e catro chamadas, cero JSON roto e dúas datas útiles. Logo o mesmo endpoint cunha mellor descrición e o que un esquema non arranxa.

Nesta páxina

Dálle a un modelo unha ferramenta de busca de voos e pídelle que atope un voo de Madrid a Berlín. Isto é o que devolve:

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 é correcto. Todos os campos obrigatorios están presentes. E a chamada non serve para nada: ningunha API de voos acepta "Madrid" onde espera un código de aeroporto, nin "3rd October 2026" onde espera unha data.

Esa fenda — sintacticamente perfecta, semanticamente inútil — é do que trata este capítulo, e o primeiro que hai que deixar claro é que non é un problema de JSON. En vinte e catro peticións con esta ferramenta, o modelo produciu 24 chamadas válidas a ferramentas e cero JSON roto. Nin unha soa vez fallou na parte que todo o mundo depura.

Antes da mecánica, a frase que evita a maior parte da confusión: unha chamada a ferramenta é unha petición, non unha acción.

O modelo emite unha mensaxe estruturada que di quero que se chame a search_flights con estes argumentos. Despois detense. O teu código recibe esa mensaxe, decide se a atende, chama o que teña que chamar e devolve o resultado como outra mensaxe. O modelo nunca tocou a túa base de datos, nunca fixo unha petición HTTP, nunca tivo credenciais.

Todo sobre a seguridade de agent en Capítulo 30 vén desa división, e tamén todo sobre o deseño de agent en Capítulo 23: o modelo propón e o teu código dispón, e no código é onde viven todas as garantías.

Así que unha ferramenta, sen vocabulario de máis, son dúas cousas:

Un esquema. Un JSON Schema que describe unha función: o seu nome, que fai e que argumentos acepta, cos seus tipos e restricións. Isto é o que entra no prompt, e é o único que o modelo chega a ver.

Un endpoint. Unha función no teu código que recibe eses argumentos e devolve algo. O modelo nunca a ve, nunca sabe en que linguaxe está e non pode distinguir unha consulta á base de datos dunha cadea hardcoded.

As definicións das ferramentas van no prompt, serializadas no formato no que se adestrase o modelo. Custan tokens en cada chamada sen excepción — un feito que volve máis adiante neste capítulo cun número.

O modelo responde cunha chamada en vez de texto

Ligazón á sección: O modelo responde cunha chamada en vez de texto

En lugar de prosa, a resposta contén unha petición estruturada, e a API informa dun motivo de remate que o indica. O motivo importa: é como o teu código sabe que debe executar unha ferramenta en vez de mostrarlle unha resposta ao usuario.

Este é o paso no que non hai modelo. Valida os argumentos contra o esquema, decide se este chamador ten permiso para facelo e executa.

O resultado convértese noutra quenda da conversa, nun rol reservado para iso. O modelo léao coma calquera outro context.

Ese é o bucle do Capítulo 23, e a razón pola que unha soa petición pode converterse nunha ducia de idas e voltas.

Nada disto é emerxente. Como estableceu o Capítulo 11, tool calling é un comportamento adestrado:1 durante o post-training o modelo viu milleiros de conversas con exactamente esta forma. Por iso o formato é específico de cada modelo, por iso a fiabilidade varía tanto entre modelos de tamaño parecido e por iso un modelo pode chamar unha ferramenta que nunca vira: a forma estaba adestrada, a ferramenta concreta vén do teu prompt.

Aquí está a ferramenta tal como a escribe ao principio a maioría da xente. Fíxate en que non ten nada mal; simplemente é 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 catro peticións, seis pares de cidades cruzados con catro maneiras de expresar unha data ("o día 3 do mes que vén", "o vindeiro venres", "15 de decembro", "mañá"), greedy decoding para que os resultados se reproduzan:

ferramenta chamadaJSON rotodata en ISOaeroportos como IATAtodo correcto
o esquema anterior24/2402/244/241/24

Le as dúas primeiras columnas antes das tres últimas. O modelo chama a ferramenta correcta sempre e produce JSON ben formado sempre. O fallo está enteiramente nos valores, e os valores non se poden usar: "Madrid" no canto de MAD, "3rd October 2026" no canto de 2026-10-03.

Paga a pena insistir nisto porque determina onde miras cando algo rompe. O instinto é engadir un parser de JSON cun reintento, ou pedirlle ao modelo con máis firmeza que produza JSON válido. Ningunha das dúas cousas aborda nada do que ocorreu aquí.

O mesmo endpoint. O mesmo código detrás. O mesmo modelo, os mesmos prompts, a mesma decodificación. O único que cambia é o texto no esquema:

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
esquema fino2/241/244/244/24
esquema descrito24/2412/2416/248/24

O formato da data pasa de 2 de 24 a 24 de 24. Perfecto, cun cambio de texto, sen tocar código e sen lóxica de reintentos. Se levas un hábito operativo deste capítulo, que sexa este: cando unha ferramenta se chama mal, a corrección case sempre está na descrición, e é a corrección máis barata do sistema.

Agora le a segunda columna, que é a metade máis importante.

Un esquema restrinxe a forma. Non pode achegar coñecemento.

Ligazón á sección: Un esquema restrinxe a forma. Non pode achegar coñecemento.

A data está en formato ISO 24 veces de 24. É o día correcto 12 veces de 24.

Así que agora a metade das chamadas levan unha data perfectamente formatada que é a data incorrecta. A descrición díxolle ao modelo que forma debía producir, e o modelo produciuna de maneira impecable — pero converter "o vindeiro venres" en 2026-09-11 require saber a data de hoxe e facer aritmética de calendario, e ningunha descrición achega iso. O mesmo cos aeroportos: o formato pasou de 4 a 16, pero o valor só de 4 a 8, porque escribir MAD require saber que o aeroporto de Madrid é MAD.

Esa distinción é a idea que sostén o capítulo:

Un esquema é un contrato sobre a forma. Pode facer que a saída do modelo sexa parseable, tipada e consistente. Non pode facela certa, e todo modo de fallo que sobrevive a un bo esquema é un fallo de coñecemento, non un fallo de formato.

As dúas cousas necesitan arranxos distintos, e confundilas fai perder semanas. Os fallos de formato arránxanse na descrición ou con decodificación restrinxida, máis abaixo. Os fallos de coñecemento arránxanse poñendo o coñecemento no prompt — a data actual na mensaxe de sistema, unha busca de aeroportos como segunda ferramenta que o modelo chama primeiro, un enum no esquema cando o conxunto é pequeno dabondo para enumeralo. Fíxate no que teñen en común as tres opcións: sacan o problema da memoria do modelo e méteno na súa entrada, que é todo o Capítulo 24.

Saídas estruturadas, e que é realmente a "decodificación restrinxida"

Ligazón á sección: Saídas estruturadas, e que é realmente a "decodificación restrinxida"

Todo o anterior segue dependendo de que o modelo escolla producir a forma correcta. Hai unha garantía máis forte dispoñible, e é a mellor recompensa do Capítulo 17.

Lembra como funciona a xeración: en cada paso o modelo produce un logit para cada token do vocabulario, e o sampler escolle un. A decodificación restrinxida insire un paso polo medio. Dada unha gramática — derivada do teu JSON Schema — calcula que tokens poderían vir legalmente a continuación, pon os logits de todos os demais en infinito negativo e deixa que o sampler escolla entre o que queda.

Se o esquema di que o seguinte debe ser un {, entón todo token que non sexa { ten probabilidade cero. Non "improbable": cero. O modelo non pode emitir JSON inválido porque os tokens inválidos foron retirados da distribución antes da mostraxe.

Iso é o que hai por baixo de "structured outputs", "JSON mode" e "guided generation", e explica as súas dúas propiedades. A garantía é total para calquera cousa que a gramática poida expresar — tipos, campos obrigatorios, enums, aniñamento — porque se aplica mecanicamente en vez de pedirse con educación. E non di nada sobre o contido: unha gramática pode forzar que "date" sexa unha cadea que encaixa cun patrón de data, e non pode forzar que sexa o día correcto. É a mesma parede da sección anterior, alcanzada desde o outro lado.

Dúas notas prácticas. Non é gratis: a máscara ten que calcularse en cada paso, e as gramáticas complexas custan unha latencia medible. E cambia o que fai o modelo: un modelo apartado do seu token preferido pode producir peor contido mentres produce unha estrutura perfecta, por iso "pedir ben e validar" segue sendo un valor por defecto razoable para formas simples, e a decodificación restrinxida xustifica o seu custo cando a forma é complexa ou o consumidor é estrito.

Efectos secundarios, e a única propiedade que importa

Ligazón á sección: Efectos secundarios, e a única propiedade que importa

O Capítulo 14 mediu un timeout seguido dun reintento que facturaba dúas xeracións por unha soa resposta. Con ferramentas o mesmo fallo empeora, porque unha ferramenta pode facer algo.

Se o teu código chama charge_card, dá timeout e reintenta, tes dous cargos. O modelo non ten nin idea de que pasou nada diso; ve un único resultado de ferramenta. A corrección é a mesma ca en calquera sistema distribuído e non é problema do modelo: fai que a operación sexa idempotente dándolle unha clave á chamada, para que a segunda execución recoñeza a primeira e devolva o seu resultado en vez de facer o traballo outra vez.

A regra de deseño que segue paga a pena dicila claramente. Separa as lecturas das escrituras no teu catálogo de ferramentas. Unha lectura pódese reintentar libremente, executar en paralelo e gardar en cache. Unha escritura non, e debe levar unha clave, unha comprobación de permisos e — para calquera cousa que un usuario quixese coñecer antes de que ocorra — un paso de aprobación que poña unha persoa entre a petición e a acción. Ese paso de aprobación non é unha cortesía: é unha das poucas cousas que se interpoñen entre unha prompt injection e unha consecuencia real — e, como mide o Capítulo 30, a máis débil delas.

O folclore di que cargar moitas ferramentas fai que o modelo escolla mal. Paga a pena medilo en vez de repetilo, así que: as mesmas vinte e catro peticións, coa ferramenta de voos máis un conxunto crecente doutras — incluídas tres deliberadamente confundibles (horarios de tren, travesías en ferry, rutas de autobús).

ferramentas cargadasprompt tokensescolleu search_flightsdata en ISO
135324/2424/24
573024/2424/24
101,19321/2421/24
202,11924/2424/24

A selección non empeorou. Con vinte ferramentas, tres delas plausiblemente confundibles, un modelo de medio billón de parámetros escolleu a correcta vinte e catro veces de vinte e catro. A caída en dez son tres chamadas que nomearon unha ferramenta diferente, e non se mantén ao pasar a vinte.

É un resultado negativo e debe comunicarse como tal: nesta tarefa, con estas ferramentas, "demasiadas ferramentas" non foi o problema. O que si medrou, de maneira monótona e por un factor de seis, é o prompt: de 353 tokens a 2,119, pagados en cada petición da conversa, para sempre, se se usa algunha ferramenta ou non.

Así que a versión honesta do folclore fala de custo e context, non de precisión. Vinte ferramentas son un imposto permanente en cada mensaxe, e o Capítulo 16 xa mostrou que fai un prefixo permanente nunha factura ao longo de corenta quendas. Cando a xente conta que moitas ferramentas prexudican a calidade, o mecanismo adoita ser que as definicións expulsaron o context que importaba — que é un problema do Capítulo 24 disfrazado de Capítulo 18. As ferramentas que son realmente case duplicados tamén son un problema real, e a corrección para esas non é ter menos ferramentas senón mellores descricións e espazos de nomes: prefixalas por sistema (crm.search_customer, billing.search_customer) para que dous catálogos fusionados de dous equipos non colidan, e para que o modelo teña algo co que discriminar.

Tres tipos de ferramenta, e a que abre a seguinte parte

Ligazón á sección: Tres tipos de ferramenta, e a que abre a seguinte parte

Axuda ordenar as ferramentas polo que lle fan ao mundo, porque a enxeñaría é distinta para cada unha.

Ferramentas de datos len: buscan, traen, consultan. Reintentables, paralelizables, cacheables. Fallan devolvendo nada útil, e o seu risco principal é que traen texto non fiable ao context — que é toda a superficie de ataque do Capítulo 30.

Ferramentas de acción escriben: envían, crean, cobran, eliminan. Non son reintentables sen unha clave, non son paralelizables con seguridade, e son a razón pola que existen os fluxos de aprobación.

Ferramentas de orquestración chaman outros modelos. Unha ferramenta cuxa implementación é outro agent, co seu propio prompt, as súas propias ferramentas e o seu propio bucle — e para o modelo que chama ten exactamente o mesmo aspecto ca as outras dúas, porque un esquema e un endpoint é todo o que chega a ver.

Ese terceiro tipo non é unha curiosidade. É o mecanismo detrás da metade agent-as-a-tool do Capítulo 25 — a outra topoloxía, o handoff, entrega a conversa e nunca a recupera — e funciona precisamente porque a interface deste capítulo é estreita dabondo para que un agent enteiro caiba detrás dela.

Agora tes un modelo que pode pedir cousas, e un contrato que fai que o pedido sexa parseable. O que non tes é nada sobre o que poida pedir máis alá do que cabe no seu prompt.

A ferramenta máis común en produción, con moita diferenza, é unha busca nun corpo de texto que o modelo nunca viu durante o adestramento: a túa documentación, os teus tickets, os teus contratos. Iso soa a problema resolto — facerlle embedding, atopar os veciños máis próximos, pegalos — e as partes que non están resoltas son as que deciden se a resposta é fiable: como se corta o texto antes de facerlle embedding, que limiar de similitude é baixo dabondo para significar non o sei, e como se liga unha cita a unha afirmación para que unha persoa lectora poida comprobala.

O Capítulo 19 é recuperación, e é o capítulo no que unha resposta incorrecta deixa de ser unha curiosidade e empeza a ser unha responsabilidade.


As medicións deste capítulo veñen de Qwen/Qwen2.5-0.5B-Instruct con greedy decoding, sobre 24 peticións xeradas que cruzan seis pares de cidades con catro formulacións de data, usando o propio modelo de chat do modelo para as definicións de ferramentas. Reprodúcense exactamente, e son un modelo pequeno: le a división formato/valor como unha demostración do mecanismo máis que como un benchmark do que fan os modelos actuais. Un modelo de fronteira resolve "o vindeiro venres" correctamente moito máis a miúdo — e aínda así un esquema non pode obrigalo a facelo, que é a parte que xeneraliza.

O vocabulario de JSON Schema usado arriba (type, properties, required, pattern, format, enum) está especificado no draft de JSON Schema que nomea a documentación do teu provedor; o subconxunto útil é pequeno e o mesmo entre provedores, e as diferenzas que existen — que palabras clave se aplican mediante decodificación restrinxida en vez de simplemente pasárense ao modelo — paga a pena lelas na guía de structured-output do provedor en vez de dalas por supostas.

Para a decodificación restrinxida como técnica, as bibliotecas de estilo guidance e o proxecto outlines documentan a construción de gramática a máscara de logit dun xeito que encaixa directamente co sampler do Capítulo 17. E para a ida e volta en si, a especificación máis clara non é un tutorial senón un protocolo: o Capítulo 26 leo liña por liña.

  1. Ouyang, L. et al. Training language models to follow instructions with human feedback. arXiv:2203.02155 (2022). O artigo que fixo estándar a receita de post-training; a forma dunha chamada a ferramenta apréndese aí, a partir de demostracións, exactamente igual que a forma dunha resposta.

Listo para deixar que LIA escolla por ti?

Crea con todos os modelos de IA nun só sitio: empeza gratis hoxe mesmo.