1. Qué es un run multietapa y en qué se diferencia de una solicitud «puntual»
Cuando trabajabas solo con ChatGPT App y herramientas MCP, el panorama era bastante lineal: llegó la solicitud del usuario → GPT decidió llamar a una o varias herramientas → diste una respuesta al usuario. Esto todavía puede considerarse «un único paso lógico», incluso si dentro de la herramienta hiciste algo más complejo.
Para un agente, un run es objetivo + una serie de pasos. Ya no pensamos en categorías de «un prompt — una respuesta», sino que percibimos la tarea como un mini proyecto que el agente lleva de principio a fin.
La diferencia puede imaginarse así:
| Tipo de interacción | Qué hace el modelo | Dónde está la lógica |
|---|---|---|
| Llamada normal de herramienta en ChatGPT App | Decide si llamar o no a la herramienta, rellena los argumentos y compone la respuesta a partir del resultado | La lógica de negocio principal y la secuencia de acciones están en una sola herramienta o en el backend |
| Run de agente (Agents SDK) | Planifica varios pasos, decide cuándo y qué tool invocar, analiza resultados intermedios y puede revisar el plan | La lógica de «cómo avanzar hacia la meta» está en parte en la instrucción system del agente y en parte nace en la mente del modelo |
Aquí hay un punto importante: no estás obligado a ceder toda la planificación al modelo. Suele salir un híbrido: codificas de forma rígida las grandes fases del guion (por ejemplo, «primero recopilar los requisitos, luego elegir regalos y después preparar la ficha»), y dentro de cada fase permites al agente utilizar sus herramientas con bastante libertad.
Mini analogía
Una llamada puntual a una herramienta es como pedir un mensajero: «recoge un documento y tráelo a la oficina».
Un run agente multietapa es como un asistente personal: «Prepárame un regalo para un compañero por su cumpleaños: averigua qué le gusta, elige varias opciones, comprueba la entrega y prepara todo en una presentación bonita». El asistente decide por su cuenta qué acciones realizar por el camino.
Un poco más adelante en la lección veremos también cómo estos runs multietapa se integran en el stack que ya conoces de Apps SDK → MCP → backend, de modo que para ChatGPT y el widget la lógica del agente parezca una herramienta MCP normal y bien encapsulada.
2. Cómo planifica pasos el propio modelo: vista desde arriba
En términos de Agents SDK, cada run puede representarse cómodamente como una terna:
- Goal (objetivo): descripción textual de la tarea, que llega a las instrucciones system/user del agente.
- Tools: conjunto de herramientas disponibles con buenas descripciones y JSON Schema.
- State: historial de pasos y estado estructurado que guardas externamente (BD, Redis, lo que sea).
Después, entra en juego el ciclo de run ya conocido: el modelo mira el objetivo y las herramientas disponibles y en cada paso decide:
- «Ahora tengo suficiente información — puedo entregar el resultado final al usuario»;
- o «Necesito llamar a la herramienta X con estos argumentos»;
- o «He recibido el resultado de la herramienta; ahora debo interpretarlo, filtrarlo y quizá llamar a otra herramienta».
A nivel de pseudocódigo, la idea se ve así (recuerda: es un modelo mental, no una API real):
while (!done && steps < MAX_STEPS) {
const modelResponse = await callModel({
system: agentPolicy,
messages: history,
tools,
});
if (modelResponse.type === "tool_call") {
const toolResult = await callTool(modelResponse.toolName, modelResponse.args);
history.push({ role: "tool", content: toolResult });
} else {
// respuesta final
done = true;
return modelResponse.content;
}
steps++;
}
En el Agents SDK real, todo este ciclo ya está implementado y «oculto» dentro de la biblioteca. Describes al agente de forma declarativa y el SDK hace girar al modelo y a las herramientas en bucle hasta que obtiene la respuesta final o choca con los límites de pasos/tiempo.
La tarea del arquitecto es:
- formular el objetivo y la instrucción system para que el modelo planifique pasos razonables;
- armar un conjunto de herramientas sin solapamientos semánticos;
- definir limitaciones de pasos y tiempo;
- pensar qué pasos pueden ser paralelos.
Cuando ya tenemos objetivo, herramientas y una idea del estado, la siguiente pregunta es — con qué pasos avanzar hacia ese objetivo. No todos los pasos son iguales: algunos son estrictamente secuenciales y otros se pueden paralelizar.
3. Pasos secuenciales y paralelos
Ahora que entendemos la base del ciclo de run del agente, conviene ver qué tipos de pasos existen dentro de un proceso así. En un flujo de trabajo con agentes hay dos tipos grandes: secuenciales y paralelos.
Pasos secuenciales
Son aquellos en los que el resultado del paso A es crítico para el paso B. Por ejemplo, en nuestro GiftGenius de aprendizaje:
- Primero hay que entender quién es el destinatario del regalo: compañero, familiar, edad, intereses.
- Después elegir un conjunto de candidatos mediante el tool search_gifts.
- Luego filtrarlos según presupuesto y restricciones.
- A continuación, preparar las fichas para el widget con un formato atractivo.
- Y solo entonces, quizá, proponer pasar al checkout.
Cada paso siguiente depende de los datos del anterior, por lo que la ejecución resulta estrictamente secuencial.
En pseudocódigo del comportamiento del agente, puede verse como un «plan interno» del modelo:
1. Preguntar al usuario sobre el destinatario y el presupuesto
2. Llamar al tool search_gifts(profile, budget)
3. Llamar al tool filter_by_constraints(gifts, constraints)
4. Formar la lista final y la descripción
El modelo no escribe una lista así en código, pero podemos empujarlo hacia esa estructura mediante instrucciones system, ejemplos de diálogo y descripciones de herramientas.
Pasos paralelos
A veces los pasos pueden ejecutarse de forma independiente. Por ejemplo, queremos comparar propuestas de regalos de tres tiendas a la vez:
- search_gifts_amazon
- search_gifts_etsy
- search_gifts_local_store
Desde el punto de vista del agente, son tres llamadas independientes a herramientas que pueden ejecutarse en paralelo para reducir el tiempo total de respuesta.
En Agents SDK (y en general en los frameworks modernos de agentes) suele haber soporte integrado para llamadas paralelas a herramientas si el modelo, en un mismo paso, propone varias invocaciones a la vez. El escenario canónico: el modelo en su respuesta describe una lista de llamadas, el SDK las ejecuta en concurrencia, recoge los resultados y los añade como un conjunto de mensajes de tipo tool al siguiente paso del modelo.
Desde el punto de vista de la planificación, se ve así:
// Paso del agente: el modelo decidió llamar a tres herramientas
const calls = [
{ name: "search_gifts_amazon", args: {...} },
{ name: "search_gifts_etsy", args: {...} },
{ name: "search_gifts_local_store", args: {...} },
];
const results = await Promise.all(
calls.map(c => callTool(c.name, c.args))
);
// Después, todos los resultados se añaden al contexto antes del siguiente paso del modelo
Si has escrito frontend en JS/TS, ya te habrás topado con la idea de peticiones en paralelo: por ejemplo, cuando lanzas varios fetch() a la vez mediante Promise.all. Ahora la misma idea aparece dentro del ciclo de run del agente, solo que la decisión de qué se puede ejecutar en paralelo la toma en gran medida el propio modelo.
4. Ejemplo de flujo de trabajo para GiftGenius: pasos, objetivos y herramientas
En la sección sobre pasos secuenciales ya dividimos de forma intuitiva el comportamiento de GiftGenius en etapas. Ahora formalicemos ese mismo escenario multietapa como un flujo de trabajo de agente: describamos el objetivo, los pasos y vinculemos todo con las herramientas y la configuración del agente. Por ahora no nos ataremos a una API concreta del Agents SDK; describiremos la estructura y añadiremos algo de TypeScript ficticio para fijar ideas.
Objetivo (goal)
Que el objetivo suene así:
Ayudar al usuario a elegir entre 3 y 5 opciones de regalo para un destinatario concreto, teniendo en cuenta presupuesto, ocasiones y restricciones de entrega, y devolver una lista estructurada de tarjetas de regalo para el widget GiftGenius.
Pasos principales
Describamos la variante mínima de 4 pasos:
- Ajuste del contexto del destinatario
Objetivo: recopilar información sobre a quién se regala (edad, sexo, intereses, relación con quien regala), además del presupuesto y la fecha del evento.
Herramientas: posiblemente ninguna; diálogo puro modelo ↔ usuario. - Búsqueda y selección inicial de regalos
Objetivo: obtener una muestra «cruda» de regalos.
Herramientas: search_gifts(profile, budget) — tool que consulta nuestro catálogo/sistema de búsqueda y devuelve una lista de candidatos. - Filtrado y ordenación
Objetivo: descartar opciones no aptas (no hay entrega a la región, se sale del presupuesto, restricciones no adecuadas) y ordenar por relevancia.
Herramientas: filter_and_score_gifts(candidates, constraints) — herramienta pura e idempotente. - Formateo del resultado para el widget
Objetivo: llevar los datos a un formato apto para UI: título, breve descripción, imagen, precio, CTA.
Herramientas: format_gift_cards(gifts) — puede ser una herramienta de código (generación de estructura) o una herramienta LLM (textos estéticos).
Cómo podría verse en la configuración del agente
Imaginemos que tenemos un constructor de agente (pseudocódigo):
import { createAgent } from "@acme/agents-sdk";
import { tools } from "./gift-tools";
export const giftAgent = createAgent({
name: "gift-guru",
system: `
Eres el agente GiftGenius; ayudas a elegir regalos.
Objetivo: proponer 3–5 opciones que realmente se puedan comprar,
teniendo en cuenta el perfil del destinatario, el presupuesto y las restricciones de entrega.
Primero aclara los detalles importantes y luego usa las herramientas de búsqueda y filtrado.
No llames a herramientas si todavía no conoces el presupuesto o los intereses clave.
Termina cuando tengas una lista clara de tarjetas de regalo.
`,
tools, // aquí estarán search_gifts, filter_and_score_gifts, format_gift_cards
maxSteps: 12,
timeoutMs: 15000,
});
Fíjate en varios detalles:
- En la instrucción system decimos explícitamente que el agente debe aclarar primero los detalles y solo después llamar a las herramientas de búsqueda. Esto reduce el riesgo de que el modelo empiece a disparar herramientas con un contexto demasiado difuso.
- Limitamos maxSteps para que el agente no caiga en bucles infinitos.
- El timeout timeoutMs es necesario para que todo el run no consuma media vida del usuario.
5. Autoorquestación por el modelo: qué «dejar a su criterio» y qué fijar estrictamente
Un agente es un equilibrio entre la libertad del modelo y la estructura rígida que tú defines.
Si das demasiada libertad al modelo y no marcas límites, obtendrás un «caos creativo»: llamadas a tools innecesarias, pasos repetidos, bucles poco obvios. Si, por el contrario, lo codificas todo con rigidez en el backend como un autómata finito, el modelo se convierte en un decorador de texto y no en un ejecutor inteligente de tareas.
Qué suele dejarse al modelo
En el contexto de GiftGenius y escenarios similares, tiene sentido confiar al modelo:
- la formulación de preguntas al usuario (cómo aclarar intereses, cómo preguntar con tacto por el presupuesto);
- la decisión de cuándo hay información suficiente para lanzar la búsqueda;
- la elección de qué herramientas usar dentro de una fase (por ejemplo, qué tool de tienda usar si hay varias);
- la generación de textos de descripciones, explicaciones y comparativas.
Qué conviene fijar de forma rígida
Al mismo tiempo, conviene fijar de antemano:
- las grandes fases del guion («Recopilación de información» → «Búsqueda» → «Filtrado» → «Formateo» → «Final»);
- los límites de pasos y tiempo;
- las condiciones en las que el agente debe «parar» y decir honestamente al usuario que la tarea es irresoluble (por ejemplo, si el presupuesto es de 5 dólares, pero se necesita un gadget electrónico caro con entrega para mañana);
- la política de idempotencia de las herramientas y las estrategias de reintento.
Ejemplo híbrido: fases como estado y el detalle a cargo del modelo
Puedes introducir en el state del agente un campo phase, que tome los valores "collect_profile" | "search" | "filter" | "format" | "done". Entonces tu backend (o el propio Agents SDK, si soporta una máquina de estados personalizada) controlará qué herramientas están disponibles en cada fase.
Pseudocódigo:
type Phase = "collect_profile" | "search" | "filter" | "format" | "done";
interface GiftAgentState {
phase: Phase;
profile?: UserProfile;
candidates?: GiftCandidate[];
finalGifts?: GiftCard[];
}
La instrucción system del agente puede incluir una breve descripción de las fases, y en tu código limitarás la lista de tools que presentas al modelo según la fase actual. Este es un ejemplo de tool gating, que se analiza con más detalle en el módulo sobre flujos de trabajo.
6. Control de bucles infinitos y repeticiones inútiles
Si dejas el ciclo de run del agente sin control, tarde o temprano se comportará como un estudiante ante la fecha límite: «aclarando y reescribiendo» infinitamente para no entregar el trabajo. Nuestra tarea es evitar que se quede colgado.
Hay tres fuentes típicas de bucles infinitos:
- El modelo no está seguro de la respuesta y sigue reformulando la misma consulta a la herramienta con cambios mínimos.
- La herramienta devuelve sistemáticamente un error o un resultado vacío y el agente insiste en «probar otra vez».
- El agente queda atascado entre dos herramientas, llamando primero a una y luego a otra, sin avanzar hacia la respuesta final.
Límite de pasos (maxSteps)
El mecanismo más simple y obligatorio es limitar la cantidad de pasos. En la mayoría de implementaciones de Agents SDK puedes indicar maxSteps al lanzar un run o en la configuración del agente. En cuanto se alcanza el límite, el SDK termina el run con un estado especial (por ejemplo, aborted_by_max_steps). Después decides cómo mostrárselo al usuario.
En GiftGenius podemos suponer que una selección razonable de regalos cabe en ~10 pasos (un par de aclaraciones, un par de búsquedas, filtrado, formateo). Ponemos, por ejemplo, 12–15 pasos con margen y tratamos con cuidado la situación en la que se alcanza el límite:
const run = await giftAgent.run({
input: userGoal,
maxSteps: 12, // sobrescribimos el valor por defecto
});
if (run.status === "max_steps_exceeded") {
// Mostramos un mensaje honesto al usuario
}
Límite de tiempo (timeout)
A veces el problema no está en la cantidad de pasos, sino en la duración total. Las herramientas pueden ser lentas y la red, inestable. Por eso es útil indicar timeoutMs tanto a nivel de cada llamada de tool como a nivel de todo el run.
Por ejemplo, puedes decidir que:
- cada llamada a una API externa (búsqueda de regalos en un partner) no debe durar más de 3–5 segundos;
- todo el run de elección de regalos debe caber en 15 segundos.
Si salta el timeout, cierras cuidadosamente el run, quizá mostrando al usuario un resultado parcial y una explicación honesta de que «parte de las fuentes no respondió a tiempo».
Detección de repeticiones
Un patrón más avanzado (pero útil) es detectar llamadas repetidas a herramientas con los mismos argumentos. Si ves que el agente ha llamado ya tres veces seguidas a search_gifts(profile, budget) con los mismos parámetros, es señal de que está atascado.
Puedes añadir al state un contador de llamadas por clave (toolName, argsHash) y, si el contador sobrepasa el umbral, o bien:
- interrumpir el run y devolver al usuario un error comprensible;
- o inyectar al modelo una instrucción adicional: «ya has intentado llamar a esta herramienta tres veces con los mismos parámetros; intenta cambiar de estrategia o pregunta al usuario».
Pseudocódigo:
function shouldAbortToolCall(toolName: string, args: unknown, state: GiftAgentState) {
const key = `${toolName}:${hashArgs(args)}`;
const count = state.toolCallCounts[key] ?? 0;
if (count >= 3) return true;
state.toolCallCounts[key] = count + 1;
return false;
}
Donde hashArgs es cualquier función determinista de serialización de argumentos (por ejemplo, JSON.stringify con ordenación de claves).
7. Criterios claros de finalización de la tarea
Una de las diferencias clave entre un agente «de juguete» y uno de producción es la existencia de criterios claros de finalización. Si no existen, el modelo puede o bien abandonar la tarea demasiado pronto («aquí tienes unos regalos, arregla el resto») o, por el contrario, seguir mejorando el resultado sin fin.
En GiftGenius podemos formular una regla simple:
- El agente termina cuando tiene entre 3 y 5 regalos con los campos completos: id, title, shortDescription, price, imageUrl, purchaseUrl, y han pasado el filtrado por presupuesto y entrega.
- Si, tras como máximo N intentos de búsqueda y filtrado, hay menos de 3 regalos adecuados, el agente informa honestamente al usuario de que no ha sido posible encontrar nada razonable y propone ampliar el presupuesto o relajar las restricciones.
Estos criterios pueden codificarse directamente en la instrucción system del agente y/o en la validación del resultado tras el run.
Ejemplo de validación del resultado tras el run:
if (run.status === "completed") {
const gifts = run.output.gifts; // supongamos que nuestro agente devuelve JSON estructurado
if (!gifts || gifts.length < 3) {
// El agente "terminó", pero el resultado es pobre — podemos:
// 1) mostrar una explicación honesta,
// 2) proponer al usuario cambiar las condiciones.
} else {
// Todo ok — mostramos el widget con los regalos
}
}
Es importante no esperar del modelo una comprensión mágica del éxito del negocio. Como desarrollador, debes formular explícitamente las condiciones de un resultado «satisfactorio» y comprobarlas.
8. Dónde se implementa exactamente la orquestación: agente, backend, widget
Ya comentamos que la orquestación puede vivir en distintos niveles: en el agente, en el backend, en el widget.
Desde el punto de vista de procesos multietapa, la lógica es aproximadamente así.
Agente (Agents SDK) se encarga del flujo «mental»:
- cómo dividir el objetivo en pasos;
- qué herramientas llamar y en qué orden;
- qué preguntas adicionales plantear al usuario.
Backend suele encargarse de:
- la implementación de las herramientas (búsqueda, filtrado, comercio, etc.);
- el almacenamiento del estado y checkpoints;
- restricciones de negocio rígidas (límites de presupuesto, permisos, disponibilidad por regiones).
Widget (Apps SDK) gestiona:
- la visualización del progreso (stepper, barra de progreso, «paso 2 de 4»);
- los formularios de entrada;
- los detalles de UX, como deshabilitar botones cuando faltan datos.
Una buena práctica es pensar así: el agente dirige el trabajo de las herramientas y el diálogo, y el widget de UI dirige la experiencia visual del usuario. Se coordinan a través de datos estructurados (ToolOutput, salida del agent run).
9. Mini ejemplo de código: lanzar el agente multietapa GiftGenius desde una herramienta MCP
Ahora, como prometimos al principio de la lección, conectemos el concepto nuevo con el stack ya conocido de Apps SDK → MCP → backend y mostremos un pequeño ejemplo de cómo una herramienta MCP puede invocar un run de agente.
Imagina que en tu app/mcp/route.ts existe el tool run_gift_workflow, que:
- recibe la solicitud textual del usuario (su objetivo);
- lanza el agente giftAgent;
- devuelve un resultado estructurado para el widget.
El código está simplificado y es ficticio, pero da una idea del encaje:
// app/mcp/route.ts
import { server } from "@modelcontextprotocol/sdk/server";
import { z } from "zod";
import { giftAgent } from "@/agents/giftAgent";
server.registerTool(
"run_gift_workflow",
{
title: "Elegir regalos",
description: "Lanza el agente multietapa de selección de regalos",
inputSchema: {
userGoal: z
.string()
.describe("Tarea del usuario, por ejemplo: quiero un regalo para un colega de hasta $50"),
},
},
async ({ userGoal }) => {
const run = await giftAgent.run({ // aquí lanzamos el agente con 12 pasos y timeout de 15 s
input: userGoal,
maxSteps: 12,
timeoutMs: 15000,
});
return {
status: run.status,
gifts: run.output?.gifts ?? [],
debug: run.debugInfo, // se puede quitar más tarde
};
}
);
Después, ChatGPT App puede llamar a este MCP-tool como a cualquier otro, y tu widget GiftGenius puede construir el UI basándose en gifts. Obtienes un flujo multietapa «bajo el capó», mientras que externamente para ChatGPT todo parece una única herramienta bien hecha.
10. Errores típicos al diseñar procesos multietapa
Error n.º 1: «Que el modelo se las apañe; yo solo le doy todas las herramientas».
Cuando el agente tiene a su disposición una decena de tools solapados semánticamente y sin una instrucción system clara ni fases, el modelo empieza a dar tumbos: llamar a lo mismo de formas distintas, duplicar consultas, entrar en bucles. Es mejor invertir tiempo en el diseño: dividir el escenario en fases, limitar la lista de herramientas dentro de cada fase y especificar explícitamente la estrategia en el prompt system.
Error n.º 2: Ausencia de límites de pasos y tiempo.
Si no defines maxSteps y timeout, en producción obtendrás rápidamente runs «errantes» que consumen recursos mientras los usuarios no ven nada. Los límites no son «opcionales», son higiene básica. Y es importante manejar con sentido las situaciones de superación de límites, no simplemente caer con un 500 mudo.
Error n.º 3: No hay criterios explícitos de finalización.
El modelo termina el run cuando «le parece suficiente», pero su idea de «suficiente» puede estar lejos de los requisitos de negocio. Si no formalizas los criterios de éxito (cuántos regalos, qué campos, qué filtros superados) y no los verificas, obtendrás un UX inestable: hoy cinco opciones excelentes, mañana una «regular» y tres duplicados.
Error n.º 4: No se rastrean llamadas repetidas a herramientas.
El agente puede atascarse en el patrón «recibí un error → reformulé la consulta con dos palabras menos → volví a llamar a la misma herramienta». Si no rastreas llamadas repetidas por (toolName, args), esos bucles quedarán invisibles hasta que mires los logs y te horrorices. Contadores simples y el hash de argumentos ayudan mucho.
Error n.º 5: Mezclar orquestación e implementación de lógica de negocio en una sola herramienta.
A veces se intenta esconder todo el flujo dentro de un único MCP-tool o función del agente: búsqueda, filtrado, formateo y toma de decisiones. Al final, el agente pierde sentido — el modelo no puede controlar el proceso paso a paso, pierdes transparencia y la posibilidad de reutilizar partes del escenario. Mejor extraer las etapas en tools independientes y dar al agente su composición.
Error n.º 6: Falta de conexión con el estado y los checkpoints.
Un proceso multietapa sin guardar el estado intermedio y checkpoints se convierte en un monolito frágil: si algo se cae en medio, el usuario debe empezar de cero. Es especialmente crítico en escenarios en los que el usuario va y viene entre pasos o regresa tras un tiempo. Usa un almacén de estado, guarda la fase, el perfil, los candidatos y da al agente la posibilidad de continuar desde donde corresponde.
Error n.º 7: Ignorar la capa de UX.
A veces los desarrolladores se entusiasman con el flujo interno del agente y olvidan que el usuario solo ve el widget y los mensajes del chat. Si en el UI no hay un progreso claro, estados como «buscando regalos…», «filtrando opciones…», el usuario pensará que la App «se colgó» o «no hace nada», aunque el agente esté orquestando un proceso complejo. Al planificar un run multietapa, piensa desde el inicio cómo se reflejará en la interfaz.
GO TO FULL VERSION