Con una cuenta desbloqueas el cuestionario, el foro de dudas y el tutor con IA de esta lección, además del registro de progreso y el certificado verificable al terminar «IA Agéntica: Agentes que Operan Sistemas Reales».
Una herramienta, desde el punto de vista del modelo, no es código: es una ficha de texto con tres campos. Un nombre, una descripción y un esquema de parámetros. Nada más. El modelo nunca ve tu implementación, tu base de datos ni tu manejo de errores; decide únicamente con esa ficha. De ahí se sigue la regla que gobierna toda esta lección: si el modelo se equivoca al llamar una herramienta, la primera sospechosa es la ficha, no el modelo.
La forma canónica de una definición de herramienta, común a las APIs (application programming interface, interfaz de programación de aplicaciones) de function calling y al protocolo MCP que veremos en la lección 4, es:
{
"name": "programar_pase",
"description": "Reserva una ventana de seguimiento y grabación para un satélite en una estación. Úsala cuando el operador pida grabar un pase concreto y ya conozcas su hora de inicio en UTC. Si no conoces la hora, llama antes a proximos_pases.",
"inputSchema": {
"type": "object",
"properties": {
"norad_id": { "type": "integer", "description": "Identificador de catálogo NORAD del satélite" },
"estacion": { "type": "string", "enum": ["orbiteye-mx", "orbiteye-cl"] },
"inicio_utc": { "type": "string", "description": "Instante de inicio en ISO 8601 UTC, p. ej. 2026-08-02T03:14:00Z" },
"elevacion_min_deg": { "type": "number", "minimum": 0, "maximum": 90, "default": 10 },
"idempotency_key": { "type": "string", "description": "Clave única de la reserva; repetir la misma clave no crea un segundo pase" }
},
"required": ["norad_id", "estacion", "inicio_utc", "idempotency_key"]
}
}
La descripción dice CUÁNDO, no solo QUÉ
El error más común al escribir tools es documentar como si el lector fuera un programador que ya sabe qué quiere llamar. El modelo no sabe qué quiere llamar: está eligiendo. Compara estas dos descripciones de la misma función:
Mala: "Programa un pase." Correcta y completamente inútil para decidir.
Buena: "Reserva una ventana de seguimiento... Úsala cuando el operador pida grabar un pase concreto y ya conozcas su hora de inicio en UTC. Si no conoces la hora, llama antes a proximos_pases."
La segunda incluye las tres cosas que hacen a una descripción útil: el efecto, la condición de uso y la relación con otras herramientas del catálogo. Ese último punto es el que elimina la mayoría de las llamadas incorrectas en producción, porque el fallo típico no es inventar argumentos sino elegir la herramienta equivocada de dos que se parecen.
El resto del contrato lo lleva JSON Schema, del que en la práctica basta un subconjunto pequeño: type (string, integer, number, boolean, array, object), properties, required, enum para catálogos cerrados, minimum/maximum para rangos y description por campo. El enum merece atención especial: convierte un espacio infinito de cadenas en dos opciones válidas, y con ello elimina de raíz toda una familia de errores.
Granularidad: ni un martillo ni cuatrocientos destornilladores
Existe la tentación de exponer un endpoint REST (representational state transfer) por tool y terminar con cuarenta herramientas, o de exponer una sola ejecutar_operacion(operacion, params) que lo haga todo. Ambas son malas por razones distintas: la primera satura el contexto y la capacidad de discriminación del modelo; la segunda destruye el contrato, porque params se vuelve un objeto libre que ningún esquema puede validar — y validar es justo lo que estamos tratando de conseguir.
Los nombres siguen la forma verbo_objeto (programar_pase, leer_telemetria, cancelar_reserva): el verbo anticipa si la acción lee o escribe, lo que además permite aplicar políticas de permisos por prefijo.
Ejemplo resuelto: el coste en tokens de la granularidad
Supón que cada definición de herramienta ocupa unos 150 tokens en el contexto. Un catálogo plano de 38 endpoints REST expuestos uno a uno cuesta:
TA=38×150=5700 tokens
Reagrupado en 9 herramientas orientadas a tareas del operador (cada una absorbiendo varios endpoints tras un enum o un parámetro tipado), el mismo catálogo cuesta:
TB=9×150=1350 tokens
El ahorro es 5700−1350=4350 tokens, es decir 4350/5700≈0.763, un 76.3 % menos de contexto fijo. Y ese contexto fijo se paga en cada iteración del lazo: con el presupuesto de la lección 1 (Δ=1850 tokens por iteración), los 4350 tokens liberados equivalen a poco más de dos iteraciones extra de trabajo útil, en cada llamada, para siempre. La granularidad no es estética: es presupuesto.
Validación en el servidor, siempre
El esquema que ve el modelo es una guía, no una garantía. El modelo puede emitir un entero donde esperabas una cadena, omitir un campo requerido o inventar un valor fuera del enum. Por eso el runtime valida otra vez, del lado del servidor, antes de tocar nada. La razón profunda es de seguridad, y la desarrollaremos en la lección 7: el contenido que llega al contexto del modelo puede haber sido influido por datos no confiables, así que tratar su salida como confiable equivale a tratar como confiable la entrada de un atacante.
Veamos tres llamadas reales a programar_pase:
Válida: {"norad_id": 25544, "estacion": "orbiteye-mx", "inicio_utc": "2026-08-02T03:14:00Z", "elevacion_min_deg": 12, "idempotency_key": "pase-25544-20260802"}. Pasa: tipos correctos, estacion dentro del enum, elevación en rango.
Inválida: la misma con "elevacion_min_deg": 120. Rechazada: 120>90 viola maximum. Sin validación server-side habrías reservado un pase imposible que nunca dispararía.
Ambigua: {"norad_id": 25544, "estacion": "orbiteye-mx"} porque el operador dijo "graba el próximo pase de la ISS". Rechazada por campos requeridos faltantes — y la descripción ya decía qué hacer: llamar antes a proximos_pases.
El error es información, no un fracaso
Cuando una tool falla, la peor respuesta posible es una excepción genérica del tipo Error 400. La mejor es un mensaje que el modelo pueda usar para corregirse: qué campo falló, qué se esperaba, qué valor llegó y cuál es el siguiente paso razonable. Compara {"error": "bad request"} con {"error": "elevacion_min_deg=120 fuera de rango [0, 90]; usa un valor en grados sobre el horizonte"}. La segunda cierra el lazo en una iteración; la primera provoca reintentos idénticos hasta agotar el presupuesto.
La contrapartida es la idempotencia. Si una tool que escribe se reintenta y no es idempotente, tres reintentos son tres reservas. Por eso programar_pase exige idempotency_key: repetir la misma clave devuelve la reserva existente en lugar de crear una nueva. Regla práctica: toda herramienta que modifica estado necesita una clave de idempotencia o una operación de comprobación previa.
Implementa tú mismo el validador que el runtime ejecuta antes de tocar el sistema:
Playground · javascript
Comprueba el punto más contraintuitivo de la lección:
Tu tool cancelar_reserva falla por timeout y el runtime la reintenta tres veces. ¿Qué propiedad evita que se cancelen tres reservas distintas?
En la próxima lección salimos del contrato de una herramienta individual para ocuparnos del recurso que todas comparten y que ninguna puede desperdiciar: el contexto.