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ántos carbohidratos tiene el arroz?». El modelo puede proponer una tool call: search_foods con el argumento {"query":"arroz"}. Esa salida es datos del proveedor, no una llamada directa a una función de la app. El modelo todavía no sabe qué arroces hay en el catálogo ni cuántos carbohidratos tiene cada uno: necesita que la app haga la búsqueda.

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 lee carbohidratos por 100 g 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.

Para responder sobre el arroz, la app comprueba primero que search_foods sea una tool declarada y que la búsqueda esté permitida. Después, el registro entrega la llamada a la función de búsqueda. Esta consulta solo lee el catálogo: devuelve las fichas que coincidan con «arroz», incluidos sus carbohidratos por 100 g, y no modifica la dieta de la persona.

Elegir la función correcta

Un registro es una lista cerrada que relaciona cada nombre de tool con una función de la aplicación. En nuestra pregunta sobre el arroz, el despachador recibe search_foods, encuentra ese nombre en la lista y ejecuta la función que busca alimentos. El registro no elige una variedad de arroz ni calcula sus carbohidratos: solo decide qué función debe atender la petición. Si el nombre no está en la lista, no ejecuta ninguna función. La lista permanece fija durante el turno, así que un mismo nombre siempre lleva a la misma acción.

En el artículo sobre cómo definir las tools vimos que cada una se presenta al LLM con name, description e inputSchema. Para search_foods, esa información ayuda al modelo a enviar «arroz» como texto en query. Pero describir el argumento esperado no garantiza que el modelo lo envíe bien. El despachador busca el name y pasa los argumentos a la función elegida; no corrige un argumento erróneo. Por ejemplo, antes de añadir arroz a una comida, esa función comprueba que el alimento exista y que los gramos sean válidos.

Errores y efectos

Si el modelo escribe mal el nombre y pide search_foodz, el despachador indica que no reconoce esa tool y no ejecuta nada. Si pide search_foods pero la búsqueda no encuentra arroz, el resultado lo dice. Si el catálogo no está disponible, el resultado también informa de esa situación: «no encuentro arroz» y «no puedo consultar el catálogo» no significan lo mismo. Ninguno de esos casos permite inventar una cifra de carbohidratos.

Un fallo al ejecutar una función es otro caso. Por ejemplo, si se intenta añadir arroz a una comida y falla el acceso al almacenamiento, el despachador devuelve un error controlado al modelo para que explique que no se pudo confirmar el guardado, sin romper toda la conversación.

Una consulta termina al devolver el dato encontrado o informar de que no se pudo obtener. Una escritura exige comprobar el efecto: si la persona pide añadir arroz a una comida, no basta con haber llamado a la función; la app solo debe comunicar éxito cuando el alimento se haya guardado. Si no puede determinar si se guardó, no repite la operación automáticamente, porque podría añadirlo dos veces.

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

Supongamos que search_foods devuelve la ficha «Arroz blanco (cocido)», que en el catálogo indica 28 g de carbohidratos por 100 g. La app envía ese resultado al modelo junto con el identificador de la tool call original, para que sepa a qué búsqueda corresponde. Solo entonces puede contestar: «El arroz blanco cocido del catálogo tiene 28 g de carbohidratos por 100 g». Si aparecen varias variedades de arroz, debe distinguirlas; si no hay resultados, debe decirlo en vez de inventar una cifra.

Cada proveedor tiene su formato para devolver el resultado: function_call_output en OpenAI, tool_result en Anthropic y function_result en Google Interactions. Después de enviarlo, la app pide al modelo que continúe y redacte la respuesta para la persona.

Tests del contrato

Para probar el despachador sin depender de un LLM remoto, los tests simulan sus tool calls. Una prueba envía search_foods con {"query":"Arroz"} y comprueba que la función de búsqueda devuelva el arroz de los datos de prueba. Otras llamadas comprueban el resto de nombres publicados. También se compara esa lista con la de handlers: si se anuncia una tool sin código que la ejecute, el test falla.

Después prueban nombres desconocidos y verifican que no se produzca ninguna escritura. Por último, repiten cada caso con datos de partida independientes: la misma llamada debe seguir la misma ruta y producir el mismo resultado. Así se detectan cambios accidentales en el despachador sin esperar a que un modelo elija la tool durante una conversación real.

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 -->