Tool use: cómo un LLM accede a herramientas externas

Los LLMs no pueden ver la hora ni tu base de datos. Tool use les da ese acceso. Explico el loop, la definición de tools y los errores más comunes.

Colaboradores: Manu Rubio

Pregúntale a un LLM qué tiempo hace ahora en Madrid. La respuesta saldrá de su entrenamiento, no del tiempo real. No puede hacer ninguna consulta externa. Tool use es el mecanismo que rompe ese aislamiento: le da al modelo la posibilidad de pedirte que ejecutes funciones reales en su nombre.

Para seguir este post necesitas saber crear funciones en TypeScript y haber hecho alguna llamada a una API. No necesitas experiencia previa con LLMs ni con agentes.

Por qué el LLM no puede buscar en Google

Piensa en alguien que lleva años desconectado del mundo. Sabe mucho de lo que aprendió antes, pero si le preguntas el precio actual de algo, solo puede darte una estimación basada en lo que recuerda.

Un LLM funciona igual. Se entrenó con datos hasta una fecha concreta y desde entonces no “ve” nada nuevo. Cuando genera texto, solo tiene acceso a lo que está en la conversación activa. Nada más.

El modelo procesa tokens (la unidad mínima de texto que maneja internamente, más o menos una palabra o fragmento de palabra) y predice el siguiente basándose en el contexto que tiene. Sin herramientas externas, si le preguntas el valor del euro hoy, el modelo solo puede intentar adivinar o admitir que no sabe. Ninguna de las dos opciones sirve en un sistema real.

Tool use no cambia cómo funciona el modelo por dentro. Lo que hace es añadir un protocolo para que el modelo pueda pedir que tu código ejecute cosas y luego recibir los resultados.

El tool loop: así fluye la información

Antes de ver código, la mecánica. Un tool call (la acción de invocar una herramienta) sigue siempre estos pasos:

  1. Tu código envía la pregunta del usuario junto con una lista de herramientas disponibles. Cada herramienta tiene nombre, descripción y un esquema que define qué argumentos acepta.
  2. El modelo lee esas descripciones y decide si necesita alguna. Si puede responder sin ellas, lo hace directamente.
  3. Si necesita una tool, devuelve un bloque tool_use con el nombre y los argumentos. No ejecuta nada: solo formula la petición.
  4. Tu código ejecuta la herramienta con esos argumentos y obtiene el resultado.
  5. Envías ese resultado de vuelta al modelo en un bloque tool_result. El modelo lo incorpora y genera la respuesta final.

El modelo nunca ejecuta nada por su cuenta. Esta separación es intencional: tu código mantiene el control de qué se ejecuta, con qué permisos y en qué entorno.

1.00

Este patrón tiene nombre en arquitectura de software: Agent Adapter, una capa que traduce la intención del LLM al formato que el sistema externo requiere [2]. El bloque tool_use actúa como un objeto Command del catálogo GoF: encapsula toda la información para ejecutar una acción, y el handler (tu código) decide si proceder. Puedes ver el mapeo completo en el post sobre patrones GoF en agentes IA.

Tu primera tool en TypeScript

La definición de una tool tiene tres campos obligatorios: name, description, e input_schema [1].

// La definición le dice al modelo QUÉ puede pedirte que hagas
const tools = [{
  name: "get_weather",
  // La description es lo único que el modelo lee para decidir si usar esta tool
  description: "Devuelve el tiempo actual de una ciudad. Úsala cuando el usuario " +
    "pregunte por el clima o la temperatura. No sirve para previsiones futuras.",
  input_schema: {
    type: "object",
    properties: {
      city: {
        type: "string",
        description: "Nombre de la ciudad, por ejemplo: Madrid, Barcelona"
      }
    },
    required: ["city"]  // campos obligatorios para que la tool funcione
  }
}];

Ahora el loop. El SDK de Anthropic devuelve stop_reason === "tool_use" cuando el modelo quiere invocar una herramienta:

// Enviamos la pregunta con las tools disponibles
const messages = [{ role: "user", content: "¿Qué tiempo hace en Madrid?" }];

const response = await client.messages.create({
  model: "claude-opus-4-7",
  max_tokens: 1024,
  tools,      // lista de herramientas disponibles
  messages
});

if (response.stop_reason === "tool_use") {
  // El modelo quiere usar una tool: extraemos nombre y argumentos
  const toolCall = response.content.find(b => b.type === "tool_use");

  // TU código ejecuta la función real con los argumentos que pidió el modelo
  const result = await getWeather(toolCall.input.city);

  // Enviamos el resultado de vuelta para que el modelo complete la respuesta
  messages.push({ role: "assistant", content: response.content });
  messages.push({
    role: "user",
    content: [{ type: "tool_result", tool_use_id: toolCall.id, content: result }]
  });
  // Aquí harías una segunda llamada al API para obtener la respuesta final
}

El campo tool_use_id vincula el resultado con la petición original. Sin él, el modelo no sabe a qué llamada corresponde el resultado.

Si quieres ver este patrón completo con múltiples herramientas y manejo de errores, el post de Programmatic Tool Calling entra en más detalle.

Function calling vs MCP: ¿cuál necesitas?

Hay dos formas de conectar herramientas a un LLM. La primera es function calling (también llamado client tools): tú defines el esquema, tú ejecutas la función, tú devuelves el resultado. Es exactamente lo que acabas de ver en el código de arriba.

La segunda es MCP (Model Context Protocol, un estándar abierto lanzado por Anthropic y donado a la Linux Foundation — qué es MCP). MCP define cómo un agente se conecta a herramientas externas de forma estandarizada, sin escribir un adaptador a medida para cada integración.

1.00


Function callingMCP
Quién ejecuta la herramientaTu códigoUn servidor MCP externo
Cuándo usarloHerramientas propias, lógica internaIntegrar herramientas de terceros con servidor MCP ya disponible
Configuración inicialDefines el esquema en el promptConfiguras la conexión a un servidor MCP
FlexibilidadMáxima, controlas todoDepende de lo que exponga el servidor MCP

Para empezar, function calling es suficiente. MCP tiene más sentido cuando quieres conectar herramientas que ya tienen servidores compatibles (bases de datos, gestores de código, APIs populares) y no quieres escribir el adaptador desde cero.

La description es el contrato

El modelo no puede ver tu código. No sabe qué hace get_weather por dentro. Lo único que tiene es la description que le das en el esquema.

Si escribes description: "obtiene el tiempo", el modelo tiene que adivinar cuándo usarla, qué parámetros tiene sentido pasar, y qué devuelve. Si tienes dos tools con descripciones igual de vagas, el modelo elegirá una sin criterio real. La documentación oficial de Anthropic recomienda al menos tres o cuatro frases para herramientas que no sean triviales, respondiendo: qué hace, cuándo debe usarse, qué devuelve, y cuándo no debe usarse [1].

Una description es útil o es una trampa. No hay término medio.

Errores comunes al empezar

Description demasiado corta

“Busca información” no es una descripción funcional. Si tu herramienta busca en una base de datos de productos pero la describes así, el modelo la usará para cualquier búsqueda. El resultado son llamadas erróneas que producen respuestas incorrectas sin ningún error técnico visible. El bug no está en el código: está en el texto.

Demasiadas tools al mismo tiempo

Si inyectas quince herramientas con nombres parecidos, el modelo tiene que elegir entre todas cada vez. La selección se degrada sin que haya errores evidentes: el modelo simplemente usa la herramienta menos precisa con más frecuencia. Lo que funciona mejor: agrupar operaciones relacionadas en una sola tool con un parámetro action (por ejemplo, una tool catalog_query con action: "search" | "get_detail" en lugar de dos tools separadas).

Side effects sin confirmación humana

Borrar un registro, enviar un email, hacer una transferencia. Estas acciones tienen consecuencias fuera del sistema. Si el modelo puede invocarlas directamente, cualquier alucinación (cuando el modelo genera información incorrecta con aparente confianza) o malinterpretación puede causar daño real. Para cualquier acción irreversible, tu código debe pedir confirmación antes de ejecutar, sin importar lo que el modelo haya decidido.

No devolver el error al modelo

Cuando tu función falla (timeout, parámetro inválido, API caída), si simplemente no devuelves nada o lanzas una excepción silenciosa, el modelo intenta continuar con información incompleta. Lo correcto es enviar el mensaje de error como tool_result para que el modelo pueda decidir si reintentar con otros parámetros, o explicarle al usuario que algo falló.

Checklist de implementación

  • Cada tool tiene name, description e input_schema definidos correctamente

  • La description responde qué hace, cuándo usarla, qué devuelve y cuándo NO usarla

  • El loop maneja stop_reason === "tool_use" y envía tool_result de vuelta al modelo

  • Los errores de ejecución se devuelven al modelo como tool_result, no se silencian

  • Las acciones con efectos irreversibles tienen confirmación humana antes de ejecutarse

  • El número de tools activas simultáneamente es manejable (sin docenas de herramientas con nombres similares)

  • Cada tool_result incluye el tool_use_id correspondiente.

Preguntas Frecuentes

¿Qué es tool use exactamente?

Tool use es un mecanismo por el que un LLM puede solicitar que tu código ejecute funciones externas durante una conversación. El modelo no ejecuta nada directamente: genera una petición estructurada (un bloque tool_use) con el nombre de la herramienta y los argumentos, y tu código decide si ejecutarla y cómo.

¿El modelo puede ejecutar código malicioso a través de las tools?

No directamente. El modelo genera una petición, pero tu código es quien ejecuta la herramienta real. Si el modelo pide invocar algo con parámetros peligrosos, tu handler puede rechazarlo, validar los argumentos o pedir confirmación. El riesgo está en implementar handlers que ejecuten sin validar lo que el modelo pidió, no en el mecanismo en sí.

¿Puedo usar tool use con cualquier LLM?

No todos los modelos lo soportan con el mismo formato. Anthropic, OpenAI y Google tienen implementaciones similares pero con diferencias en los campos exactos. Los ejemplos de este post usan la API de Anthropic. Si cambias de proveedor, la lógica del loop es la misma, pero tendrás que ajustar los nombres de campos.

¿Qué diferencia hay entre tool use y pedirle al modelo que responda en JSON?

Con JSON libre, tú parseas el texto y no hay garantía de formato. Con tool use, el modelo devuelve un objeto estructurado que sigue exactamente el input_schema que definiste. No necesitas regex ni parsing manual, y si el modelo se equivoca en un campo obligatorio, el error es más claro y manejable.