Ir al contenido principal
Tool calling en producción: protege tus APIs del LLM

Tool calling en producción: protege tus APIs del LLM

AI Integration
5 min readPor Daily Miranda Pardo

Tu agente funciona en el entorno de desarrollo. Los tests pasan. Haces el deploy. Y tres días después recibes un error de producción que no deberías ver nunca: una API lanzó una excepción porque llegó un campo numérico en formato string. O faltaba un campo obligatorio. O el valor estaba fuera de rango.

El modelo generó un tool call incorrecto. Y no había nada que lo parara antes de llegar a tu sistema.

El error que nadie testea en desarrollo

Cuando construyes un agente con tool calling — el mecanismo por el que un LLM decide llamar a una función externa — tus pruebas casi siempre cubren el caso feliz: el modelo genera el JSON correcto, la herramienta se ejecuta, el resultado vuelve al agente.

Lo que rara vez se testea:

  • El modelo genera "amount": "150" en lugar de "amount": 150 — número como string
  • Omite customerId porque en el prompt anterior ese campo no era relevante
  • Inventa una fecha en el pasado: "dueDate": "2024-03-01"
  • Pasa un UUID sintácticamente válido que no existe en tu base de datos

Estos no son bugs del modelo. Son características estadísticas de los LLMs: en muestras suficientemente grandes, ocurrirán. En producción, con miles de llamadas reales, las muestras son siempre suficientemente grandes.

El error arquitectónico más habitual es no tener ninguna capa entre el output del modelo y la ejecución de la herramienta.

Tres capas de validación para agentes en producción

La defensa se construye en tres niveles. Cada uno atrapa un tipo distinto de error.

Capa 1 — Validación de esquema con Zod

La primera línea de defensa verifica que el JSON generado por el modelo cumple el contrato de la herramienta. Nunca ejecutes el tool call con los argumentos crudos del modelo:

import { z } from 'zod';

const SendInvoiceSchema = z.object({
  customerId: z.string().uuid(),
  amount: z.number().positive(),
  currency: z.enum(['EUR', 'USD', 'GBP']),
  dueDate: z.string().regex(/^\d{4}-\d{2}-\d{2}$/),
});

type SendInvoiceArgs = z.infer<typeof SendInvoiceSchema>;

function validateToolCall(name: string, args: unknown): SendInvoiceArgs {
  const result = SendInvoiceSchema.safeParse(args);
  if (!result.success) {
    throw new ToolValidationError(name, result.error.format());
  }
  return result.data;
}

Si la validación falla, no llegas a la API. El error se gestiona internamente, no como una excepción de producción.

Capa 2 — Retry con feedback de error al modelo

La validación sola no es suficiente si simplemente terminas el proceso cuando falla. Lo que distingue a un agente robusto es que cuando falla la validación, devuelve el error al modelo y le da la oportunidad de corregirlo:

async function executeWithRetry<T>(
  toolName: string,
  rawArgs: unknown,
  validator: (args: unknown) => T,
  executor: (args: T) => Promise<unknown>,
  llm: LLMClient,
  maxAttempts = 3
): Promise<unknown> {
  let currentArgs = rawArgs;
  let lastError: string | null = null;

  for (let attempt = 0; attempt < maxAttempts; attempt++) {
    try {
      const validArgs = validator(currentArgs);
      return await executor(validArgs);
    } catch (err) {
      lastError = err instanceof Error ? err.message : String(err);
      if (attempt + 1 >= maxAttempts) break;

      // Devolver el error al modelo para que corrija el tool call
      currentArgs = await llm.correctToolCall(toolName, currentArgs, lastError);
    }
  }

  throw new Error(
    `Tool ${toolName} failed after ${maxAttempts} attempts. Last error: ${lastError}`
  );
}

El método correctToolCall envía al modelo un mensaje del tipo: "El tool call que generaste para send_invoice falló con este error: amount debe ser un número positivo, recibiste una cadena. Corrige los argumentos." El modelo genera la versión corregida. Este ciclo ocurre en milliseconds y es invisible para el usuario final.

Capa 3 — Validación semántica de negocio

Hay errores que un schema nunca puede atrapar porque son válidos sintácticamente pero incorrectos desde el punto de vista del negocio:

  • amount: 0.001 — número positivo válido, pero una factura de 0,1 céntimos es un error
  • dueDate: "2020-01-01" — formato correcto, pero en el pasado
  • customerId: "uuid-válido-pero-de-cliente-dado-de-baja" — UUID correcto, cliente inactivo

Estas validaciones van dentro de la herramienta misma, antes de ejecutar la operación real:

async function sendInvoice(args: SendInvoiceArgs): Promise<void> {
  if (args.amount < 1) {
    throw new BusinessValidationError('El importe mínimo de factura es 1€');
  }

  const dueDate = new Date(args.dueDate);
  if (dueDate < new Date()) {
    throw new BusinessValidationError('La fecha de vencimiento no puede ser pasada');
  }

  const customer = await db.customer.findUnique({ where: { id: args.customerId } });
  if (!customer?.active) {
    throw new BusinessValidationError(
      `Cliente ${args.customerId} no encontrado o dado de baja`
    );
  }

  await invoiceService.create(args);
}

Estos errores de negocio se retroalimentan al modelo con el mismo patrón de retry de la capa 2.

Lo que separa un prototipo de un agente en producción real

Un agente de desarrollo funciona cuando el modelo está en modo cooperativo y el input es el esperado. Un agente de producción funciona bajo condiciones adversas: inputs inesperados, modelos con alucinaciones puntuales, reinicios de proceso.

Las tres capas — esquema, retry con feedback, validación de negocio — no son optimizaciones opcionales. Son la diferencia entre un prototipo y un sistema que puede tocar dinero real, datos de clientes o flujos operativos sin supervisión constante.

El artículo anterior sobre agentes IA stateful cubre el problema del estado persistente. Este es el problema hermano: qué pasa cuando el LLM genera una llamada incorrecta. Son dos capas distintas de la misma arquitectura robusta.

En los proyectos de integración IA y agentes de automatización que construimos en DAILYMP, estas tres capas van siempre en el diseño inicial. Añadirlas como parche después del primer fallo visible en producción es más caro, más arriesgado y más difícil de razonar.

Si tienes un agente que ya está en producción y no sabe qué hace cuando el modelo genera un tool call incorrecto, merece revisarlo antes de que ocurra el primer fallo real:

Revisamos tu agente juntos →

Compartir artículo

LinkedInXWhatsApp

¿Procesos repetitivos en tu empresa?

Descarga gratis el Mapa de Automatización IA — los 5 procesos que más tiempo roban y cómo resolverlos.

Sin spam. Solo el PDF. Puedes darte de baja cuando quieras.

Escrito por Daily Miranda Pardo

Ayudo a empresas a automatizar procesos, crear agentes IA y conectar sistemas inteligentes.