1. Qué son los escenarios Multi‑App y por qué los necesitas
Hasta ahora veíamos GiftGenius como una única aplicación externa en un chat concreto: el usuario elige tu App en la lista, ChatGPT levanta tus tools y tú eres el «protagonista» de la historia. En la Store real es distinto: el usuario puede conectar varias Apps a la vez, y ChatGPT decidirá qué aplicación exactamente invocar en respuesta a cada solicitud.
Por ejemplo, en un mismo chat pueden estar:
- una App corporativa de calendario que conoce los cumpleaños de los compañeros;
- GiftGenius, que selecciona ideas de regalos;
- la commerce‑App de la empresa, que sabe tramitar pedidos y pagos.
El usuario escribe: «Recuérdame los cumpleaños de mis compañeros y sugiere al instante qué regalar y cómo comprarlo». El modelo puede invocar en secuencia tres Apps diferentes: una para el calendario, otra para las ideas de regalos y una tercera para el checkout.
Es importante entender que el usuario no tiene un botón de «invoca, por favor, la App n.º 2 y aquí está su HTTP endpoint». Se comunica en lenguaje natural y ChatGPT actúa como router: lee las descriptions y metadatos de todas las aplicaciones disponibles y decide a quién llamar y cuándo.
De aquí se desprenden tres ideas clave:
- Hay competencia por el contexto. Tu App debe ser elegida entre decenas de otras en función de las descripciones, los nombres y el comportamiento.
- Los metadatos se convierten en tu «SEO para LLM» — son los que determinan si el modelo verá GiftGenius en el momento oportuno o lo ignorará.
- Hay que pensar en la interoperabilidad: tus respuestas deben ser útiles no solo para la persona en el chat, sino también para otras Apps que leen el mismo contexto.
En esencia, convertimos una ChatGPT App aislada en un componente de un sistema más grande.
2. Cómo el modelo elige la App: modelo mental de enrutamiento
El enrutamiento en escenarios Multi‑App funciona más o menos así (muy simplificado, pero útil para desarrollo):
- ChatGPT tiene una lista de Apps disponibles y sus tools con metadatos (nombre, descripción, JSON Schema de parámetros, anotaciones y _meta).
- El usuario escribe un mensaje.
- El modelo construye una representación interna de la intención (intent) y, en esencia, hace una búsqueda semántica por las descriptions de las tools y aplicaciones para entender qué herramientas son pertinentes.
- Si se cumplen los criterios, invoca la tool o propone abrir la App.
Aquí hay un matiz importante: las descriptions deben ser suficientemente diferenciadas (discriminativas). Formulaciones como «Búsqueda de productos» se parecen demasiado a «Búsqueda de regalos» o «Búsqueda de libros», mientras que «Búsqueda de ideas de regalos en la base de socios de GiftGenius» acota mucho el dominio y aumenta las probabilidades de que tu herramienta sea elegida para consultas de regalos.
Segundo detalle: evita colisiones de nombres. Una herramienta llamada get_data en un mundo con decenas de Apps no dice nada; giftgenius_get_gift_catalog es mucho más claro. Y más aún si se combina con una description precisa.
Y, por último, el modelo se apoya en el contexto: si en el chat ya se mencionaron «regalos», «cumpleaños» e incluso el nombre GiftGenius, eso resalta tu App a ojos del router.
3. Metadatos y descriptions como LLM‑SEO
Para no tratar los metadatos como «un trámite obligatorio en JSON», es útil verlos como un trabajo de redacción de producto. Las recomendaciones oficiales lo dicen claramente: trata los metadatos como product copy y diseña «one job per tool».
Podemos distinguir varias capas de descripciones:
| Nivel | Para quién | Qué describe |
|---|---|---|
| Manifest description | Persona + modelo | La tarea de toda la App: por qué incluirla en el chat |
| Tool description | Modelo (routing) | Cuándo usar una tool concreta y para qué tareas |
| Parameter descriptions | Modelo (slot fill) | Cómo rellenar argumentos, qué valores son válidos |
|
Modelo (UI) | Qué aparece exactamente en el widget y si hay que duplicarlo con una respuesta textual del modelo |
widgetDescription es especialmente importante en el mundo de los widgets: el modelo no «ve» tu código React, solo sabe qué props le pasarás y para qué. Un campo bien rellenado le permite no inventar «por ti», sino, al contrario, adaptar las respuestas textuales teniendo en cuenta la UI ya mostrada.
La documentación de Apps SDK subraya que ChatGPT decide cuándo y cómo invocar tu conector (App) basándose en los metadatos. Unas descriptions cuidadas y documentación de parámetros aumentan el recall —la proporción de situaciones en las que el modelo recuerda tu App— y reducen los falsos positivos.
Mini‑ejemplo: descripción antigua vs nueva para GiftGenius
Supongamos que antes teníamos algo así:
export const appDescription = `
GiftGenius — asistente para buscar y comprar regalos.
`;
Desde el punto de vista de una persona está bien, pero para el enrutamiento en un mundo Multi‑App es mejor enfatizar cuándo usar la App y qué no hace:
export const appDescription = `
GiftGenius — asistente de ideas de regalos.
Úsala cuando el usuario pida idear un regalo
para una persona o un motivo concretos y ajustarse a un presupuesto.
No la uses para compras online genéricas ni para planificación de finanzas personales.
`;
Ahora al modelo le resulta más fácil distinguir GiftGenius de una commerce App general o de un asesor financiero.
4. _meta["openai/widgetDescription"]: explicamos nuestra UI al modelo
En la tabla anterior mencionamos por separado _meta["openai/widgetDescription"]. Ahora nos centramos en este nivel: ayuda al modelo a «imaginar» tu widget y entender qué partes de la respuesta ya están cubiertas por la UI y qué conviene explicar en texto.
Supongamos que nuestra herramienta principal suggest_gifts devuelve una lista de regalos, y el widget los renderiza como un carrusel horizontal de tarjetas. En la descripción de la herramienta ya explicamos cuándo usarla y, en widgetDescription, explicamos cómo se ve el resultado.
Fragmento de descriptor de herramienta (simplificado, inspirado en las recomendaciones):
const suggestGiftsTool = {
name: "suggest_gifts",
description: "Use this to generate gift ideas within user's budget.",
inputSchema: { /* ... */ },
_meta: {
"openai/widgetDescription":
"Muestra una lista horizontal de tarjetas de regalos con precio y un botón 'Comprar'. No repitas los nombres de los regalos en la respuesta de texto."
}
};
Con esto logramos varias cosas a la vez:
- El modelo sabe que la UI ya mostrará nombres y precios; por tanto, en la respuesta textual puede centrarse en explicaciones y consejos, no en duplicar la lista.
- Otras Apps (a través del modelo) entienden que toolOutput no es solo un párrafo de texto, sino una lista estructurada que pueden «aprovechar» en su propio contexto.
Y sí, seamos honestos: escribir estas descripciones es más aburrido que programar, pero luego te ahorran horas de depurar comportamientos extraños del modelo.
5. Anotaciones de herramientas: readOnlyHint, destructiveHint, openWorldHint
En el mundo Multi‑App importa no solo «cuándo invocar», sino también cuán seguro es invocar una herramienta concreta. Para ello, Apps SDK introduce un conjunto de anotaciones en los descriptores de herramientas.
La idea es: las anotaciones son sugerencias suaves para el modelo sobre la naturaleza de la operación. No sustituyen a la autorización en el servidor, pero influyen mucho en cómo se comporta ChatGPT en las cadenas.
Resumen corto (conceptual):
| Anotación | Significado | Comportamiento típico del modelo |
|---|---|---|
|
No cambia datos | Se puede invocar a menudo y sin confirmaciones extra |
|
Cambia el estado (compras, eliminación) | Pedir confirmación al usuario antes de invocar |
|
Sale al «mundo exterior» (búsqueda, web) | El modelo es más cauto con el volumen y la calidad del resultado |
Las anotaciones (readOnlyHint, destructiveHint, openWorldHint) forman parte de la descripción estándar de la herramienta y pueden usarse no solo por ChatGPT. El campo _meta["openai/isConsequential"] es una señal más específica de ChatGPT que ayuda adicionalmente al modelo a distinguir invocaciones «seguras» de las «con consecuencias».
Veamos dos herramientas de GiftGenius:
- suggest_gifts: lectura de catálogo, segura.
- create_checkout_session: creación de checkout, acción con side‑effects.
Ejemplo de descripción de la herramienta suggest_gifts
const suggestGiftsTool = {
name: "suggest_gifts",
description:
"Use this when the user asks for gift ideas for a person or occasion.",
inputSchema: { /* ... */ },
annotations: {
readOnlyHint: true
},
_meta: {
"openai/widgetDescription": "Carrusel de regalos con precio y enlace.",
"openai/isConsequential": false
}
};
El modelo puede invocar esta herramienta varias veces seguidas, incluso de forma «preventiva», para preparar opciones por adelantado sin preguntar al usuario por cada acción.
Ejemplo de descripción de la herramienta create_checkout_session
const createCheckoutTool = {
name: "create_checkout_session",
description:
"Finalize purchase of selected gifts via Instant Checkout.",
inputSchema: { /* ... */ },
annotations: {
destructiveHint: true
},
_meta: {
"openai/isConsequential": true
}
};
Aquí señalamos explícitamente: es una operación de escritura, con consecuencias (se cobra dinero, se crea un pedido) y el modelo debe pedir confirmación al usuario antes de invocarla, especialmente en cadenas largas con varias Apps.
No sobreestimes la «magia»: incluso con destructiveHint debes volver a verificar en el servidor los datos de entrada, los tokens y los permisos, como comentamos en los módulos de seguridad y autorización. Pero, en términos de orquestación Multi‑App, las anotaciones ayudan al modelo a no «disparar» estas herramientas sin necesidad.
6. App aislada vs ecosistema: acotamos los límites de GiftGenius
Cuando GiftGenius era la única App en el chat, podías permitirte un alcance bastante amplio: selección de regalos, consejos de envoltorio, recordatorios de festivos, incluso pequeños textos de felicitación. El modelo invocaría igualmente solo tus herramientas.
En un escenario Multi‑App ese enfoque «yo hago de todo» empieza a perjudicar:
- al router le cuesta más distinguir cuándo eres el candidato ideal y cuándo es mejor usar otra App;
- empiezas a solaparte con el calendario, un gestor de tareas general, un planificador financiero, etc.;
- al usar varias aplicaciones a la vez, el modelo puede elegir al «ejecutor equivocado» y confundirse con las herramientas.
La mejor estrategia es delimitar con claridad tu responsabilidad:
- GiftGenius: solo ideas de regalos + ayuda con la compra mediante ACP/Checkout;
- CalendarApp: eventos y recordatorios;
- Finance‑App: el presupuesto del usuario en general, plan financiero personal.
En las descripciones de la App y de las herramientas conviene indicar explícitamente no solo «Use this when…», sino también «Do not use when…». El discovery playbook oficial recomienda exactamente esto.
Mini‑ejemplo de descripción de herramienta:
description: `
Use this tool when the user explicitly asks for gift suggestions.
Do not use for generic product discovery or price comparison.
`
Estas restricciones no solo ayudan al enrutamiento, sino que también hacen que el comportamiento de tu App sea más predecible para producto y QA.
7. Patrones de composición de Apps: pipeline, handoff, shared context
En escenarios Multi‑App aparecen en la práctica tres ideas relacionadas:
- pipeline: varias Apps van una detrás de otra (calendario → regalos → commerce), cada una realiza su paso;
- handoff: la salida de una App se convierte en la entrada de la siguiente;
- shared context: toda esa transmisión ocurre a través del contexto textual compartido del chat, sin llamadas HTTP directas entre aplicaciones.
Como ya insinuamos, un escenario Multi‑App no es magia de «la App A llama a la App B por HTTP». En la implementación actual de ChatGPT Apps, el aislamiento es bastante estricto: las aplicaciones no se invocan directamente entre sí; la comunicación se realiza a través del contexto textual compartido.
El patrón básico se puede formular así:
- La App A devuelve al chat texto o JSON (a menudo dentro de structuredContent/widget).
- El modelo lee esa salida.
- En el siguiente turno puede invocar la App B, incorporando detalles de la respuesta de A en los argumentos de sus tools.
A esto se le llama text/context handoff: «Salida de la App A → modelo → entrada de la App B».
Ejemplo: CalendarApp + GiftGenius + CommerceApp
Analicemos un escenario concreto.
Usuario: «Mañana es el cumpleaños del jefe, elige un regalo y tramita la compra».
Paso a paso:
-
El modelo entiende que primero hay que identificar la fecha y la persona. Invoca la herramienta de la App de calendario, por ejemplo corporate_calendar.list_upcoming_birthdays, y obtiene una estructura:
[ { "name": "Aleksey Bykov", "date": "2025-11-22", "relation": "manager" } ] -
Después el modelo decide que es hora de llamar a GiftGenius. Invoca tu suggest_gifts con argumentos obtenidos del calendario:
{ "recipientName": "Aleksey", "occasion": "birthday", "budget": 150, "relationship": "manager" }El widget de GiftGenius muestra un carrusel de regalos y la respuesta textual explica por qué esas ideas son adecuadas.
-
El usuario elige una o dos opciones (con un botón en el widget → widgetState) y el modelo invoca ya la herramienta de la commerce‑App, por ejemplo corp_checkout.create_gift_order, con los ID de los SKU seleccionados y la dirección de envío.
Desde el punto de vista de ChatGPT son tres aplicaciones distintas, pero para el usuario es una única conversación. La clave para que funcione:
- descriptions claras para las herramientas de cada App;
- nombres cuidadosos (corporate_calendar.list_upcoming_birthdays, y no simplemente list_events);
- un formato coherente de datos estructurados (para que la idea de regalo esté descrita de forma que la commerce‑App pueda entenderla).
Esquema visual
Podemos representar este pipeline así:
sequenceDiagram
participant U as Usuario
participant C as ChatGPT (Router)
participant Cal as CalendarApp
participant G as GiftGenius
participant Com as CommerceApp
U->>C: Mañana es el cumple del jefe, elige y tramita el regalo
C->>Cal: tools.call(list_upcoming_birthdays)
Cal-->>C: [{ name, date, relation }]
C->>G: tools.call(suggest_gifts, { recipient, occasion, budget })
G-->>C: gift suggestions (+ widget)
C-->>U: Explicaciones + widget de GiftGenius
U->>C: Me gusta la opción #2, cómprala
C->>Com: tools.call(create_gift_order, { skuId, address })
Com-->>C: Order confirmation
C-->>U: Listo, pedido tramitado
Tu tarea como desarrollador de GiftGenius es hacer que en este coro tu voz suene clara y al grano, sin interferir con las demás.
8. Interoperabilidad: hacemos que las respuestas sean útiles para otras Apps
En el mundo Multi‑App no basta con «responder bonito al usuario». Es deseable que tu toolOutput se pueda procesar automáticamente por otra aplicación: commerce‑App, un agente analítico, un orquestador de workflows, etc.
Esto implica un par de cosas prácticas:
- usar JSON estructurado en las respuestas de las herramientas, no texto «humano» serializado;
- procurar mantener campos estables y comprensibles.
Por ejemplo, podemos tipar el resultado de suggest_gifts así:
export type GiftSuggestion = {
id: string;
title: string;
description: string;
price: number;
currency: string;
forPerson: string;
occasion: string;
purchaseUrl: string;
};
Y en la respuesta de la herramienta devolver un array de esos objetos:
{
"gifts": [
{
"id": "sku_123",
"title": "Planetario de escritorio",
"description": "Mini proyector de cielo estrellado...",
"price": 89.99,
"currency": "USD",
"forPerson": "Aleksey",
"occasion": "birthday",
"purchaseUrl": "https://shop.example.com/sku_123"
}
]
}
El widget de GiftGenius tomará esto como props y renderizará las tarjetas; y la commerce‑App, al ver este JSON en el contexto, podrá aprovechar id y purchaseUrl para el checkout posterior.
La práctica en escenarios Multi‑App demuestra: una buena App devuelve los datos de forma que otra App pueda «consumirlos», no solo los ojos de una persona.
9. Refactor práctico de GiftGenius para Multi‑App
Resumamos todo en varios cambios concretos en nuestra aplicación de ejemplo.
Ajustamos el manifest‑description
Imaginemos que tenemos un openai-app.json (o equivalente en la plantilla de Next.js) con la descripción:
{
"name": "GiftGenius",
"description": "Gift assistant for finding and buying presents."
}
Lo haremos más explícito para el enrutamiento:
{
"name": "GiftGenius",
"description": "Assistant for gift ideas and purchase flows. Use this app when the user asks what to gift a specific person or for a specific occasion within a budget. Do not use for generic online shopping or personal finance planning."
}
Ahora ya se ve que no es una app de shopping general, ni un asesor financiero ni un calendario.
Reescribimos las descriptions de las herramientas
Herramienta de búsqueda de regalos:
const suggestGiftsTool = {
name: "giftgenius_suggest_gifts",
description: `
Use this when the user asks for gift ideas for a specific person or group,
optionally with a budget or occasion.
Do not use for non-gift product recommendations or travel booking.
`
};
Herramienta que recupera detalles de SKU de tu catálogo (solo lectura):
const getGiftDetailsTool = {
name: "giftgenius_get_gift_details",
description: `
Use this to fetch more details for a gift suggested earlier by GiftGenius,
for example when the user asks “tell me more about option #2”.
`,
annotations: { readOnlyHint: true }
};
La herramienta de compra — con destructiveHint, como ya mostramos.
Actualizamos _meta["openai/widgetDescription"]
Supongamos que en nuestro widget ya hay tarjetas con un CTA «Comprar». Sugerimos esto al modelo:
const giftWidgetMeta = {
_meta: {
"openai/widgetDescription": `
Muestra una lista de tarjetas de regalos con descripción, precio y botón 'Comprar'.
El modelo no debe repetir la lista completa en el texto, sino comentar la selección y ayudar a decidir.
`
}
};
Ahora el modelo escribirá con menos frecuencia un listado interminable de diez regalos en el chat si ya son visibles en el widget, y se centrará en explicaciones y lógica — útil tanto para el UX como para el coste de tokens.
10. Mentalidad Multi‑App para tu futuro producto
Es importante cambiar el chip del modo «cómo vencer a todos los competidores y ser la única App del usuario» al modo «cómo hacer que mi App sea el módulo ideal en un gran ecosistema».
Este enfoque aporta varias ventajas prácticas:
- te resulta más fácil explicar al usuario y a los revisores de la Store para qué sirve tu App y cuándo es apropiada;
- el modelo toma decisiones de enrutamiento con más facilidad: menos confusiones, menos invocaciones «equivocadas»;
- podrás diseñar composiciones conscientemente: hoy con calendario y commerce, mañana con un bot de RR. HH. corporativo o un CRM interno.
Las guías oficiales de discovery enfatizan: diseña «one job per tool», y trata los metadatos como un artefacto vivo que hay que probar y actualizar, no como un texto estático del primer commit.
Te ayudará mucho lo que ya hiciste en los temas anteriores del Módulo 20: golden cases, LLM evals, ejecución en CI. Puedes ampliar el conjunto de casos con escenarios «en el chat están GiftGenius y CalendarApp», y seguir cómo los cambios en las descripciones afectan a la elección de la App y a la calidad de las respuestas.
11. Errores habituales al trabajar con Multi‑App y composición
Error n.º 1: descripción de la App «sé hacer de todo».
Si en el manifest‑description pones algo como «asistente inteligente para cualquier tarea», compites no solo con otras Apps, sino también con el ChatGPT básico. Al router le resulta difícil entender cuándo debe llamarte a ti y cuándo bastan las capacidades integradas. En el mundo Multi‑App ganan las aplicaciones con un propósito claro y estrecho: «selección de regalos», «gestión de calendario», «análisis de logs».
Error n.º 2: descriptions de herramientas difusas y colisiones de nombres.
Herramientas con nombres como get_data, process_request y la explicación «procesa los datos del usuario» son perfectas para confundir al modelo. En un mundo con varias Apps es fácil acabar en situaciones donde se invoca tu tool cuando se habla de otro dominio por completo. El camino correcto es vincular dominio y acción (giftgenius_get_gift_catalog, calendar.list_birthdays) y describir explícitamente «Use this when… / Do not use when…».
Error n.º 3: ignorar _meta["openai/widgetDescription"].
Los desarrolladores suelen rellenar solo description y se acuerdan de _meta como mucho por la localización. Como resultado, el modelo no entiende qué muestra exactamente el widget y empieza o bien a duplicar la UI con texto, o bien a prometer al usuario una «tabla con precios» que tu widget no tiene. Un par de líneas en widgetDescription evita muchas de estas situaciones.
Error n.º 4: ausencia de anotaciones readOnlyHint/destructiveHint.
Si todas tus herramientas parecen igual de «neutras», el modelo no distingue cuáles se pueden invocar a menudo de forma segura y cuáles requieren confirmación del usuario. En escenarios multietapa con varias Apps esto es especialmente crítico: puedes ejecutar varias operaciones de escritura seguidas sin participación explícita de la persona. No olvides marcar las tools de solo lectura y destacar claramente las acciones consequentials/destructivas.
Error n.º 5: respuestas pensadas solo para personas, no para otras Apps.
Devolver desde una herramienta simplemente «una lista de regalos» en una línea de texto es tentador, pero entonces a cualquier otra App le resultará difícil usar ese resultado. Un JSON estructurado con campos claros (id, price, currency, purchaseUrl, occasion) te da ventajas tanto en la UI como en la composición: el modelo puede incorporar esos datos en los argumentos de otras tools sin parsear lenguaje natural.
Error n.º 6: intentar en una sola App implementar todo lo que el usuario podría necesitar.
A veces apetece: «ya que hice GiftGenius, que también lleve el calendario, envíe correos a colegas y planifique presupuestos». En un mundo aislado aún pasa; en contexto Multi‑App te conviertes en una navaja suiza que entra en conflicto con otras Apps estrechas y bien enfocadas. Es mejor pactar contigo mismo: mi App hace X y lo hace de forma impecable; lo demás es responsabilidad ajena. Este diseño simplifica mucho el UX y la evolución del ecosistema.
Error n.º 7: no probar el comportamiento en un entorno con otras Apps.
Los desarrolladores a menudo prueban su aplicación en Dev Mode en un chat «limpio», donde no hay otras Apps. Pero en la Store el usuario puede tener una decena de aplicaciones conectadas, algunas de las cuales se solapan conceptualmente con la tuya. No te dé pereza crear un escenario de prueba donde en el chat haya Apps vecinas (calendario, shopping general, finanzas) y ejecutar golden cases: ¿elige bien el modelo GiftGenius en las consultas de regalos y no lo confunde con otros participantes?
GO TO FULL VERSION