saltar al contenido
volver al mapa
técnica
etapa: act
4 min de lectura

Tool calling

El tool calling es el contrato entre el LLM y el mundo: el modelo devuelve JSON estructurado (nombre + argumentos), el runtime valida contra un schema y ejecuta. Es lo que convierte el texto en acción.

OpenAI Function CallingAnthropic Tool UseJSON SchemaPydantic

Qué es

El tool calling (también llamado ) es la interfaz por la cual un LLM devuelve, en lugar de (o además de) texto, una llamada de función estructurada: nombre de la herramienta + argumentos en JSON. El runtime la recibe, valida los argumentos contra un schema, ejecuta y devuelve el resultado al contexto.

El modelo nunca ejecuta nada por su cuenta. Solo propone: 'yo llamaría get_weather con { city: 'São Paulo' }'. La ejecución, la autorización, el reintento y la auditoría quedan a cargo del runtime. Olvidar quién ejecuta es el error más caro que aparece en producción.

El schema JSON: el contrato

Cada herramienta se declara al modelo mediante un schema JSON con nombre, descripción y parámetros tipados. El LLM usa ese schema como gramática: conoce el nombre, conoce el formato de los argumentos y usa la descripción para decidir cuándo tiene sentido llamar.

La descripción es la parte más descuidada. El LLM no lee código: lee el string. 'Obtiene el clima de una ciudad' es mejor que 'get_weather', porque el modelo decide cuándo llamar con base en prosa. Los schemas con malas descripciones generan llamadas correctas o erróneas al azar.

schema de la herramienta get_weatherjson
{
  "name": "get_weather",
  "description": "Obtiene la temperatura actual y el pronóstico de los próximos 3 días en una ciudad.",
  "parameters": {
    "type": "object",
    "properties": {
      "city": {
        "type": "string",
        "description": "Nombre de la ciudad, en portugués o inglés (ej: 'São Paulo', 'Lisbon')."
      },
      "unit": {
        "type": "string",
        "enum": ["celsius", "fahrenheit"],
        "default": "celsius"
      }
    },
    "required": ["city"]
  }
}

Validación de argumentos: tu responsabilidad

Cuando el modelo devuelve una llamada de función, el runtime recibe un JSON que puede estar mal: tipo equivocado, campo faltante, valor fuera del enum, string donde debía ir un número. Validar antes de ejecutar es innegociable: es la frontera entre el modelo, que propone, y tu código, que ejecuta con permisos reales.

En Python, Pydantic es el camino estándar: declaras un modelo, llamas a model_validate(model_dump), y Pydantic lanza ValidationError con la lista exacta de campos y el motivo. En TypeScript, el par natural es Zod, con safeParse devolviendo { success, data, error }.

validacao.pypython
from pydantic import BaseModel, Field, ValidationError


class GetWeatherArgs(BaseModel):
    city: str = Field(..., min_length=1)
    unit: str = Field("celsius", pattern=r"^(celsius|fahrenheit)$")


def call_tool(name: str, raw_args: dict):
    if name != "get_weather":
        raise ValueError(f"herramienta desconocida: {name}")
    try:
        args = GetWeatherArgs.model_validate(raw_args)
    except ValidationError as e:
        return {"error": "invalid_args", "details": e.errors()}
    return fetch_weather(args.city, args.unit)

El flujo completo, de punta a punta

La llamada a una herramienta atraviesa cinco etapas, cada una con una respuesta posible y un camino de error distinto. Sin ver las cinco, parece magia, y los bugs no están donde esperas.

1. declaración: la lista de schemas se entrega al LLM junto con el prompt.

2. propuesta: el LLM devuelve tool_calls con name + JSON arguments.

3. validación: el runtime parsea y valida contra el schema; un fallo aquí significa 'el modelo alucinó'; trátalo como error y devuelve el motivo al LLM.

4. ejecución: el runtime llama a la función real (HTTP, DB, comando) con permisos y timeout.

5. observación: el resultado se convierte en un mensaje tool en el contexto; el LLM lo usa para la siguiente decisión.

runtime.tsxtypescript
import Anthropic from '@anthropic-ai/sdk';
import { z } from 'zod';

const GetWeather = z.object({
  city: z.string().min(1),
  unit: z.enum(['celsius', 'fahrenheit']).default('celsius'),
});

async function run(anthropic: Anthropic, userMessage: string) {
  const tools = [{
    name: 'get_weather',
    description: 'Obtiene el clima actual de una ciudad.',
    input_schema: /* shape JSON del schema */,
  }];

  let msg = await anthropic.messages.create({
    model: 'claude-opus-4', max_tokens: 1024, tools,
    messages: [{ role: 'user', content: userMessage }],
  });

  if (msg.stop_reason === 'tool_use') {
    const call = msg.content.find((b) => b.type === 'tool_use');
    const parsed = GetWeather.safeParse(call.input);
    if (!parsed.success) return { error: 'invalid_args', issues: parsed.error.issues };
    const result = await fetchWeather(parsed.data.city, parsed.data.unit);
    msg = await anthropic.messages.create({
      model: 'claude-opus-4', max_tokens: 1024, tools,
      messages: [
        { role: 'user',     content: userMessage },
        { role: 'assistant',content: msg.content },
        { role: 'user',     content: [{ type: 'tool_result', tool_use_id: call.id, content: JSON.stringify(result) }] },
      ],
    });
  }
  return msg;
}

Por qué no se puede omitir la validación

  • el modelo puede inventar campos: vio 1000 schemas distintos y no garantiza consistencia
  • los tipos están garantizados, los valores no: sabe que 'unit' es un string, pero puede mandar 'celcius' (typo) de vez en cuando
  • los enums son la frontera de error más común: el modelo rara vez respeta el enum sin un prompt explícito
  • la validación es barata: cuesta microsegundos; una ejecución sin validar puede permitir un orden de ejecución crítico

Puntos fuertes y limitaciones

  • fuerte: el schema fuerza estructura; el modelo no necesita 'intentar' producir JSON válido; está produciendo tokens dentro de una gramática
  • fuerte: los errores de validación devuelven un mensaje rico al modelo, que casi siempre se corrige en el siguiente turno
  • débil: una mala descripción lleva a llamadas correctas o erróneas al azar; escribir un buen schema es trabajo de diseño
  • débil: la llamada es opaca para el usuario; si no lee el trace, no sabe qué herramienta se ejecutó
  • débil: el paralelismo depende del proveedor; algunos permiten varias tool_calls en una respuesta, otros solo una
Function callingtool useMecanismo por el cual el LLM pide, en vez de solo texto, ejecutar una función con parámetros estructurados: el modelo emite JSON, tu código ejecuta la función y el resultado vuelve a la conversación. Es lo que convierte un modelo puro en un agente capaz de actuar en el mundo, y lo que necesita guardrails para evitar la llamada equivocada con el argumento equivocado.ver en el glosario →

conexiones en el mapa

actualizado el 14/10/2026 · fuentes para profundizar · reportar un error

en otros dominios

Estudia antes

Desbloquea

pon a prueba lo que entendiste

Cargando preguntas…