De una tool call a código ejecutable: el despachador de un agente

De una tool call a código ejecutable: el despachador de un agente

Una propuesta, no una ejecución

Imagina que una persona pregunta: «¿Cuánta proteína tiene el arroz?». El modelo puede responder con una tool call: search_foods y el argumento {"query":"arroz"}. Esa salida es datos del proveedor, no una llamada directa a una función de la app. El modelo no conoce el catálogo local, no tiene acceso a su almacenamiento y no puede invocar código por sí mismo.

El harness es el programa que recibe esa propuesta, decide si la acepta y conecta el nombre con una función local. El identificador de la llamada se conserva aparte para asociar después el resultado con la petición correcta. OpenAI, Anthropic y Google usan formatos distintos para transportar la tool call, pero una vez parseada, el despachador trabaja con el mismo par: name y args.

El recorrido completo

De la petición al resultado de una tool El modelo propone una llamada. El guard comprueba que esté permitida, el registro elige un handler, el handler consulta los datos y el resultado vuelve al modelo. 1 El modelo propone search_foods({ query: "arroz" }) 2 El guard comprueba nombre declarado · lectura permitida 3 El registro despacha search_foods → handler 4 El handler ejecuta consulta el catálogo local 5 El resultado vuelve string → respuesta del modelo
La flecha indica quién actúa después. El modelo propone; el código de la aplicación decide si ejecuta la llamada.

La primera barrera comprueba que el nombre esté declarado y que esa clase de acción esté permitida para la consulta. Solo entonces el registro busca el handler. En el ejemplo, search_foods es una lectura: su handler consulta el catálogo y devuelve una cadena con los resultados. Una escritura local exige además coordinar el efecto y confirmar que se guardó antes de comunicar éxito.

Elegir la función correcta

Un registro relaciona nombres públicos con funciones. Es la frontera exacta entre «el modelo quiere hacer esto» y «la aplicación ejecuta esta función». El ejemplo reducido muestra la decisión central:

const handlers = {
  search_foods: searchFoods,
  read_measurement: readMeasurement,
};

const handler = Object.hasOwn(handlers, call.name)
  ? handlers[call.name]
  : undefined;
if (!handler) return "Tool no reconocida";
return await handler(call.args, context, dependencies);

Comprobar que la propiedad pertenece al registro importa: un objeto JavaScript también hereda nombres como constructor y toString. Una búsqueda ingenua podría confundirlos con handlers. Mantener el registro fijo durante la ejecución garantiza que un mismo nombre elija siempre la misma función.

El schema que se anuncia al proveedor describe la forma esperada de los argumentos, pero no convierte una respuesta del modelo en entrada fiable. Cada handler debe validar los datos de su dominio antes de escribir. Añadir una validación genérica en la frontera del despachador es una mejora separada; no hay que atribuírsela a este registro.

Errores y efectos

Si el nombre no está declarado, el harness devuelve un error controlado y no ejecuta ninguna función. Si un handler falla, el turno debe recibir una respuesta útil en lugar de romper todo el chat. Para una lectura basta con devolver el dato o el error. Para una escritura, «se llamó al handler» no equivale a «se guardó»: el efecto se confirma después de persistirlo y los fallos ambiguos no se reintentan a ciegas.

Regla de diseño:

el modelo propone la acción; el código de la aplicación decide la autorización, ejecuta el handler y comprueba el resultado. El nombre de la tool nunca concede permiso por sí solo.

Devolver el resultado

El handler produce una salida textual. El bucle del proveedor la empaqueta junto con el identificador original: function_call_output en OpenAI, tool_result en Anthropic o function_result en Google Interactions. Después solicita otra respuesta al modelo. Así, el modelo puede explicar a la persona lo que encontró; no necesita inventar lo que había en el catálogo.

Tests del contrato

La prueba central recorre los nombres declarados con argumentos válidos, verifica que cada uno llegue a su comportamiento esperado y devuelva texto, y compara la lista con los handlers registrados. Otras pruebas cubren nombres desconocidos, incluidos los que hereda un objeto JavaScript, y comprueban que ninguna dependencia de escritura se invoca. Repetir el mismo caso con un contexto nuevo detecta cambios accidentales de ruta sin depender de un modelo remoto.

Aprender a construir un agente completo

Este artículo forma parte de una serie sobre cómo construir un agente o harness usando una app de gimnasio como caso práctico. El paso anterior fue reconstruir una llamada completa desde el stream; aquí vimos dónde se transforma en código. El siguiente límite es validar sistemáticamente los argumentos antes de cualquier efecto.

Ver el índice completo de la serie sobre el agente de Gymnasia.

Seguir leyendo

Últimos posts -->

¿Has visto estos proyectos?

Gymnasia

Gymnasia Gymnasia
Expo
React Native
TypeScript
OpenAI
Anthropic

App de fitness con dos agentes que se ejecutan íntegramente en el dispositivo, sin backend, de forma que los datos del usuario nunca salen del móvil. Un coach conversacional BYOK con tools locales y system prompt remoto con fallback offline, y un estimador de comidas que saca los macronutrientes de una foto del plato, con lectura de códigos de barras contra OpenFoodFacts.

LangGraph Deep Researcher

LangGraph Deep Researcher LangGraph Deep Researcher
Python
LangGraph
FastAPI
React
TypeScript
Docker

Sistema multiagente de investigación construido con LangGraph. Un supervisor descompone tu pregunta en temas y lanza subagentes de búsqueda en paralelo; cada uno comprime sus hallazgos antes de pasarlos a un agente redactor que escribe el informe final en markdown con sus fuentes. Streaming en vivo por WebSockets, modelo configurable por rol y claves de API propias que nunca se guardan en el servidor.

Tau

Tau Tau
Python
LangChain

Sistema multiagente de tutoría para estudiantes de secundaria, con un agente por asignatura y material de curso elaborado y validado por un equipo de profesores. Llegó a usarse con alumnos reales en un colegio privado en España y en un instituto en Colombia.

Ver todos los proyectos -->
>_ Disponible para proyectos

¿Tienes un proyecto con IA?

Hablemos.

maximofn@gmail.com

Especialista en Machine Learning e Inteligencia Artificial. Desarrollo soluciones con IA generativa, agentes inteligentes y modelos personalizados.

¿Quieres ver alguna charla?

Últimas charlas -->

¿Quieres mejorar con estos tips?

Últimos tips -->

Usa esto en local

Los espacios de Hugging Face nos permite ejecutar modelos con demos muy sencillas, pero ¿qué pasa si la demo se rompe? O si el usuario la elimina? Por ello he creado contenedores docker con algunos espacios interesantes, para poder usarlos de manera local, pase lo que pase. De hecho, es posible que si pinchas en alún botón de ver proyecto te lleve a un espacio que no funciona.

Flow edit

Flow edit Flow edit

Edita imágenes con este modelo de Flow. Basándose en SD3 o FLUX puedes editar cualquier imagen y generar nuevas

FLUX.1-RealismLora

FLUX.1-RealismLora FLUX.1-RealismLora
Ver todos los contenedores -->
>_ Disponible para proyectos

¿Tienes un proyecto con IA?

Hablemos.

maximofn@gmail.com

Especialista en Machine Learning e Inteligencia Artificial. Desarrollo soluciones con IA generativa, agentes inteligentes y modelos personalizados.

¿Quieres entrenar tu modelo con estos datasets?

short-jokes-dataset

HuggingFace

Dataset de chistes en inglés

Uso: Fine-tuning de modelos de generación de texto humorístico

231K filas 2 columnas 45 MB
Ver en HuggingFace →

opus100

HuggingFace

Dataset con traducciones de inglés a español

Uso: Entrenamiento de modelos de traducción inglés-español

1M filas 2 columnas 210 MB
Ver en HuggingFace →

netflix_titles

HuggingFace

Dataset con películas y series de Netflix

Uso: Análisis de catálogo de Netflix y sistemas de recomendación

8.8K filas 12 columnas 3.5 MB
Ver en HuggingFace →
Ver más datasets -->