1. Що таке Multi‑App‑сценарії й навіщо вони вам
Досі ми розглядали GiftGenius як єдиний зовнішній застосунок (App) у конкретному чаті: користувач обирає ваш застосунок у списку, ChatGPT підʼєднує ваші tools — і ви стаєте «головним героєм» історії. У реальному Store все інакше: користувач може підʼєднати одразу кілька застосунків, а ChatGPT вирішуватиме, який саме застосунок викликати у відповідь на конкретний запит.
Наприклад, в одному чаті можуть бути:
- корпоративний календарний застосунок, який знає дні народження колег;
- GiftGenius, який підбирає ідеї подарунків;
- commerce‑застосунок компанії, який уміє оформлювати замовлення й оплату.
Користувач пише: «Нагадайте мені про дні народження колег і одразу запропонуйте, що подарувати та як купити». Модель може послідовно викликати три різні застосунки: один — для календаря, другий — для ідей подарунків, третій — для оформлення покупки.
Важливо розуміти: у користувача немає кнопки «викличіть, будь ласка, застосунок № 2 і ось його HTTP‑ендпойнт». Він спілкується природною мовою, а ChatGPT виступає маршрутизатором: читає descriptions і метадані всіх доступних застосунків та вирішує, кого і коли викликати.
Звідси випливають три ключові думки:
- Триває конкуренція за контекст. Ваш застосунок має бути обраний серед десятків інших на основі описів, назв і поведінки.
- Метадані стають вашим «SEO для LLM» — саме вони визначають, чи «побачить» модель GiftGenius у потрібний момент, чи проігнорує його.
- Потрібно думати про інтероперабельність: ваші відповіді мають бути корисні не лише людині в чаті, а й іншим застосункам, які читають той самий контекст.
По суті, ми перетворюємо ізольований ChatGPT App на компонент більшої системи.
2. Як модель обирає App: ментальна модель маршрутизації
Маршрутизація в Multi‑App‑сценаріях працює приблизно так (дуже спрощено, але для розробки цього достатньо):
- У ChatGPT є список доступних Apps і їх tools із метаданими (імʼя, опис, JSON Schema параметрів, анотації та _meta).
- Користувач пише повідомлення.
- Модель будує внутрішнє представлення наміру (intent) і, по суті, виконує семантичний пошук у descriptions інструментів та застосунків, щоб зрозуміти, які інструменти доречні.
- Якщо критерії збігаються — викликає tool або пропонує відкрити App.
Є важливий нюанс: descriptions мають бути достатньо розрізнювальними. Формулювання «Пошук товарів» мало чим відрізняється від «Пошук подарунків» чи «Пошук книжок». Натомість «Пошук ідей подарунків у базі партнерів GiftGenius» суттєво звужує домен і підвищує шанси, що саме ваш інструмент буде обраний для «подарункових» запитів.
Друга деталь — уникайте конфліктів в іменах. Інструмент із назвою get_data у світі десятків Apps ні про що не говорить. А от giftgenius_get_gift_catalog — набагато зрозуміліша назва, особливо в парі з чітким description.
І нарешті, модель спирається на контекст. Якщо в чаті вже згадували «подарунки», «дні народження» і навіть назву GiftGenius, це додатково підсилює ваш застосунок «в очах» маршрутизатора.
3. Метадані та descriptions як «LLM‑SEO»
Щоб не ставитися до метаданих як до «обовʼязкової формальності в JSON», корисно сприймати їх як продуктову копірайтингову роботу. Офіційні рекомендації кажуть прямо: treat metadata like product copy — ставтеся до метаданих як до опису продукту. Також вони радять проєктувати «one job per tool».
Умовно можна виокремити кілька рівнів описів:
| Рівень | Для кого | Що описує |
|---|---|---|
| Manifest description | Людина + модель | Завдання всього App: навіщо його вмикати в чаті |
| Tool description | Модель (routing) | Коли використовувати конкретний tool і для яких завдань |
| Parameter descriptions | Модель (slot fill) | Як заповнювати аргументи й які значення допустимі |
|
Модель (UI) | Що саме зʼявляється у віджеті та чи потрібно це дублювати текстовою відповіддю моделі |
widgetDescription особливо важливий у світі віджетів: модель не «бачить» ваш React‑код. Вона лише знає, які props ви їй передасте й навіщо. Добре заповнене поле дає змогу моделі не вигадувати «за вас», а, навпаки, адаптувати текстові відповіді з урахуванням уже показаного UI.
Документація щодо Apps SDK підкреслює: ChatGPT вирішує, коли й як викликати ваш конектор (App), спираючись на метадані. Акуратні descriptions і описи параметрів підвищують recall — частку ситуацій, у яких модель узагалі згадує про ваш App, — і зменшують хибні спрацювання.
Міні‑приклад: старий і новий description для GiftGenius
Припустімо, раніше в нас було щось на кшталт:
export const appDescription = `
GiftGenius — помічник для пошуку й купівлі подарунків.
`;
З погляду людини все непогано. Але для маршрутизації у Multi‑App‑світі краще зробити акцент на тому, коли застосунок варто використовувати, і чого він не робить:
export const appDescription = `
GiftGenius — асистент з ідей подарунків.
Використовуйте цей застосунок, коли користувач просить придумати подарунок
для конкретної людини або події та вкластися в бюджет.
Не використовуйте його для загального онлайн-шопінгу чи планування особистих фінансів.
`;
Тепер моделі легше відрізнити GiftGenius від загального e‑commerce App або фінансового консультанта.
4. _meta["openai/widgetDescription"]: пояснюємо моделі наш UI
У таблиці вище ми окремо згадали _meta["openai/widgetDescription"]. Тепер зосередьмося на цьому рівні. Він допомагає моделі «уявити» ваш віджет і зрозуміти, які частини відповіді вже покриті UI, а що варто проговорити текстом.
Припустімо, наш основний інструмент suggest_gifts повертає список подарунків, а віджет відображає їх як горизонтальну карусель карток. В описі інструмента ми вже пояснили, коли його використовувати, а в widgetDescription пояснюємо, як виглядає результат.
Приклад фрагмента дескриптора інструмента (descriptor, спрощений, за мотивами рекомендацій):
const suggestGiftsTool = {
name: "suggest_gifts",
description: "Використовуйте це, щоб згенерувати ідеї подарунків у межах бюджету користувача.",
inputSchema: { /* ... */ },
_meta: {
"openai/widgetDescription":
"Показує горизонтальний список карток подарунків із ціною та кнопкою 'Купити'. Не повторюйте назви подарунків у текстовій відповіді."
}
};
Тут ми одразу досягаємо кількох цілей:
- Модель знає, що UI вже покаже назви й ціни — отже, у текстовій відповіді можна зосередитися на поясненнях і порадах, а не на дублюванні списку.
- Інші Apps (через модель) розуміють, що toolOutput — це не просто абзац тексту, а структурований список, який можна «підхопити» у своєму контексті.
І так, будьмо чесними: писати такі описи нудніше, ніж писати код. Зате саме вони потім заощадять вам години налагодження дивної поведінки моделі.
5. Анотації інструментів: readOnlyHint, destructiveHint, openWorldHint
У Multi‑App‑світі важливо не лише «коли викликати», а й наскільки безпечно викликати конкретний інструмент. Для цього Apps SDK вводить набір анотацій у дескрипторах інструментів.
Ідея така: анотації — це мʼякі підказки моделі про характер операції. Вони не замінюють серверну авторизацію, але суттєво впливають на те, як ChatGPT поводитиметься в ланцюжках.
Коротке резюме (концептуально):
| Анотація | Зміст | Типова поведінка моделі |
|---|---|---|
|
Нічого не змінює в даних | Можна викликати без зайвих підтверджень і досить часто |
|
Змінює стан (покупки, видалення) | Перед викликом запитати підтвердження в користувача |
|
«Виходить» у зовнішній світ (пошук, веб) | Модель обережніша з обсягом і якістю результату |
Анотації (readOnlyHint, destructiveHint, openWorldHint) — це частина стандартного опису інструмента. Вони можуть використовуватися не лише ChatGPT. Поле _meta["openai/isConsequential"] — вужчий, специфічний для ChatGPT сигнал. Він додатково допомагає моделі розрізняти «безпечні» та «наслідкові» виклики.
Подивімося на два інструменти GiftGenius:
- suggest_gifts — читання каталогу, безпечно.
- create_checkout_session — створення чекаут‑сесії, явна операція з побічним ефектом.
Приклад опису інструмента suggest_gifts
const suggestGiftsTool = {
name: "suggest_gifts",
description:
"Використовуйте це, коли користувач просить ідеї подарунків для конкретної людини або події.",
inputSchema: { /* ... */ },
annotations: {
readOnlyHint: true
},
_meta: {
"openai/widgetDescription": "Карусель подарунків із ціною та посиланням.",
"openai/isConsequential": false
}
};
Такий інструмент модель може викликати кілька разів поспіль, зокрема й «превентивно», щоб заздалегідь підготувати варіанти й не запитувати користувача про кожну дію.
Приклад опису інструмента create_checkout_session
const createCheckoutTool = {
name: "create_checkout_session",
description:
"Завершує купівлю вибраних подарунків через Instant Checkout.",
inputSchema: { /* ... */ },
annotations: {
destructiveHint: true
},
_meta: {
"openai/isConsequential": true
}
};
Тут ми явно сигналізуємо: це write‑операція, у неї є наслідки (кошти списано, замовлення створено), і модель має запитати підтвердження користувача перед викликом — особливо в довгих ланцюжках із кількома Apps.
Важливо не переоцінювати «магію»: навіть із destructiveHint ви зобовʼязані на сервері перевіряти вхідні дані, токени та права, як ми обговорювали в модулях про безпеку й авторизацію. Але з погляду Multi‑App‑оркестрації анотації допомагають моделі не застосовувати такі інструменти без потреби.
6. Ізольований App проти екосистеми: уточнюємо межі GiftGenius
Коли GiftGenius був єдиним App у чаті, можна було дозволити собі доволі широкий scope: підбір подарунків, поради щодо пакування, нагадування про свята, навіть невеликі мотиваційні тексти для привітань. Модель усе одно викличе лише ваші інструменти.
У Multi‑App‑сценарії підхід «я роблю все» починає шкодити:
- маршрутизатор гірше розрізняє, коли ви ідеальний кандидат, а коли краще використати інший App;
- ви починаєте перетинатися з календарем, загальним менеджером завдань, фінансовим планувальником тощо;
- за спільного використання кількох застосунків модель може обирати «не того» виконавця й плутатися в інструментах.
Кращий підхід — чітко окреслити зону відповідальності:
- GiftGenius: лише ідеї подарунків + допомога з купівлею через ACP/Checkout;
- CalendarApp: події та нагадування;
- Finance‑App: бюджет користувача загалом, особистий фінансовий план.
В описах Apps та інструментів корисно явно прописувати не лише «Use this when…», а й «Do not use when…». Офіційний discovery‑playbook саме це й рекомендує.
Міні‑приклад опису інструмента:
description: `
Використовуйте цей інструмент, коли користувач прямо просить порадити подарунки.
Не використовуйте його для загального пошуку товарів або порівняння цін.
`
Такі обмеження не лише допомагають маршрутизації, а й роблять поведінку вашого App передбачуванішою для продукту та QA.
7. Патерни композиції Apps: pipeline, handoff, shared context
У Multi‑App‑сценаріях на практиці спливають три повʼязані ідеї:
- pipeline — кілька Apps йдуть один за одним (календар → подарунки → commerce), кожен виконує свій крок;
- handoff — вихід одного App стає входом для наступного;
- shared context — уся ця передача відбувається через спільний текстовий контекст чату, без прямих HTTP‑викликів між застосунками.
Як ми вже натякали, Multi‑App‑сценарій — це не магія «App A викликає App B по HTTP». У поточній реалізації ChatGPT Apps ізоляція доволі жорстка: застосунки не викликають одне одного напряму, а комунікація йде через спільний текстовий контекст.
Базовий патерн можна сформулювати так:
- App A повертає в чат текст або JSON (часто всередині structuredContent/віджета).
- Модель читає цей вихід.
- У наступному ході вона може викликати App B, підставивши деталі з відповіді A в аргументи його tools.
Це називають text/context handoff: «Вихід App A → модель → вхід App B».
Приклад: CalendarApp + GiftGenius + CommerceApp
Розберімо конкретний сценарій.
Користувач: «У шефа завтра день народження, підберіть подарунок і одразу оформіть покупку».
Покроково:
-
Модель розуміє, що спершу потрібно зʼясувати дату й людину. Вона викликає інструмент календарного App, умовно corporate_calendar.list_upcoming_birthdays, і отримує структуру:
[ { "name": "Олексій Биков", "date": "2025-11-22", "relation": "manager" } ] -
Далі модель вирішує, що час покликати GiftGenius. Вона викликає ваш suggest_gifts з аргументами, отриманими з календаря:
{ "recipientName": "Олексій", "occasion": "birthday", "budget": 150, "relationship": "manager" }Віджет GiftGenius показує карусель подарунків, а текстова відповідь пояснює, чому ці ідеї доречні.
-
Користувач обирає один‑два варіанти (кнопкою у віджеті → widgetState), і модель викликає вже інструмент commerce‑App, наприклад corp_checkout.create_gift_order, з ID обраних SKU та адресою доставки.
З точки зору ChatGPT це три різні застосунки, але для користувача — єдина розмова. Ключ до того, щоб це працювало:
- чіткі descriptions інструментів кожного App;
- акуратні імена (corporate_calendar.list_upcoming_birthdays, а не просто list_events);
- узгоджений формат структурованих даних (щоб ідея подарунка була описана так, аби commerce‑App міг її зрозуміти).
Візуальна схема
Цей pipeline можна зобразити так:
sequenceDiagram
participant U as Користувач
participant C as ChatGPT (Router)
participant Cal as CalendarApp
participant G as GiftGenius
participant Com as CommerceApp
U->>C: У шефа завтра ДН, підбери й оформи подарунок
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: Пояснення + віджет GiftGenius
U->>C: Подобається варіант №2, купи його
C->>Com: tools.call(create_gift_order, { skuId, address })
Com-->>C: Підтвердження замовлення
C-->>U: Готово, замовлення оформлено
Ваше завдання як розробника GiftGenius — зробити так, щоб у цьому «хорі» ваш голос звучав чітко й по суті, а не заважав іншим.
8. Інтероперабельність: робимо відповіді придатними для інших Apps
У Multi‑App‑світі замало просто «гарно відповісти користувачу». Бажано, щоб ваш toolOutput можна було машинно обробити іншим застосунком: commerce‑App, аналітичним агентом, workflow‑оркестратором тощо.
Це означає кілька практичних речей:
- використовувати структурований JSON у відповідях інструментів, а не серіалізований «людський» текст;
- намагатися дотримуватися стабільних, зрозумілих полів.
Наприклад, можна типізувати результат suggest_gifts так:
export type GiftSuggestion = {
id: string;
title: string;
description: string;
price: number;
currency: string;
forPerson: string;
occasion: string;
purchaseUrl: string;
};
І у відповіді інструмента повертати масив таких обʼєктів:
{
"gifts": [
{
"id": "sku_123",
"title": "Настільний планетарій",
"description": "Міні-проєктор зоряного неба...",
"price": 89.99,
"currency": "USD",
"forPerson": "Олексій",
"occasion": "birthday",
"purchaseUrl": "https://shop.example.com/sku_123"
}
]
}
Віджет GiftGenius візьме це як props і гарно відрендерить картки, а commerce‑App, побачивши цей JSON у контексті, зможе підхопити id і purchaseUrl для подальшого оформлення замовлення.
Практика Multi‑App‑сценаріїв показує: хороший App повертає дані так, щоб їх міг «зʼїсти» інший App, а не лише очі людини.
9. Практичний рефакторинг GiftGenius під Multi‑App
Зведімо все до кількох конкретних змін у нашому навчальному застосунку.
Уточнюємо manifest‑description
Уявімо, що в нас є openai-app.json (або еквівалент у Next.js‑шаблоні) з описом:
{
"name": "GiftGenius",
"description": "Gift assistant for finding and buying presents."
}
Зробімо його чіткішим для маршрутизації:
{
"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."
}
Тепер із цього тексту вже видно, що це не загальний шопінг, не фінансовий консультант і не календар.
Переписуємо descriptions інструментів
Інструмент пошуку подарунків:
const suggestGiftsTool = {
name: "giftgenius_suggest_gifts",
description: `
Використовуйте цей інструмент, коли користувач просить ідеї подарунків для конкретної людини або групи,
за потреби — з урахуванням бюджету чи події.
Не використовуйте його для рекомендацій товарів, не повʼязаних із подарунками, або бронювання подорожей.
`
};
Інструмент, який підвантажує подробиці SKU з вашого каталогу (read‑only):
const getGiftDetailsTool = {
name: "giftgenius_get_gift_details",
description: `
Використовуйте це, щоб отримати більше деталей про подарунок, який GiftGenius запропонував раніше,
наприклад, коли користувач питає “розкажіть більше про варіант №2”.
`,
annotations: { readOnlyHint: true }
};
Інструмент купівлі — з destructiveHint, як уже показували.
Оновлюємо _meta["openai/widgetDescription"]
Припустімо, у нашому віджеті вже є картки з CTA «Купити». Підкажімо це моделі:
const giftWidgetMeta = {
_meta: {
"openai/widgetDescription": `
Показує список карток подарунків з описом, ціною та кнопкою 'Купити'.
Модель не повинна повторювати повний список у тексті, а лише прокоментувати вибір і допомогти ухвалити рішення.
`
}
};
Тепер модель рідше писатиме довжелезний список із десяти подарунків у чаті, якщо вони й так видимі у віджеті. Натомість вона зосередиться на поясненнях і логіці — що корисно і для UX, і для вартості токенів.
10. Multi‑App‑мислення для вашого майбутнього продукту
Важливо перемкнутися з режиму «як мені перемогти всіх конкурентів і бути єдиним App користувача» в режим «як зробити свій App ідеальним модулем у великій екосистемі».
Такий підхід дає кілька практичних переваг:
- вам простіше пояснювати користувачу й ревʼюерам Store, навіщо ваш App і коли він доречний;
- моделі простіше ухвалювати рішення про маршрутизацію: менше плутанини, менше «не тих» викликів;
- ви зможете свідомо проєктувати композиції: сьогодні — з календарем і commerce, завтра — з корпоративним HR‑ботом або внутрішньою CRM.
Офіційні посібники з discovery акцентують: проєктуйте «one job per tool», а метадані розглядайте як живий артефакт, який потрібно тестувати й оновлювати, а не як статичний текст із першого коміту.
Тут дуже допоможе те, що ви вже зробили в попередніх темах Модуля 20: golden‑кейси, LLM‑evals, CI‑прогон. Ви можете розширити набір кейсів сценаріями «у чаті є і GiftGenius, і CalendarApp» та відстежувати, як зміни в описах впливають на вибір App і якість відповідей.
11. Типові помилки при роботі з Multi‑App і композицією
Помилка № 1: опис App «я вмію все на світі».
Якщо ви пишете в manifest‑description щось на кшталт «розумний помічник для будь‑яких завдань», ви конкуруєте не лише з іншими Apps, а й із базовим ChatGPT. Маршрутизатору стає складно зрозуміти, коли він має кликати саме вас, а коли достатньо вбудованих можливостей. У Multi‑App‑світі перемагають застосунки з чітким, вузьким призначенням: «підбір подарунків», «керування календарем», «аналіз логів».
Помилка № 2: розмиті descriptions інструментів і конфлікти імен.
Інструменти з іменами get_data, process_request і поясненням «обробляє дані користувача» чудово підходять, щоб заплутати модель. У світі кількох Apps легко опинитися в ситуації, коли ваш tool викликається там, де йдеться про зовсім інший домен. Правильний шлях — поєднувати домен і дію (giftgenius_get_gift_catalog, calendar.list_birthdays) і явно описувати «Use this when… / Do not use when…».
Помилка № 3: ігнорування _meta["openai/widgetDescription"].
Розробники часто заповнюють лише description, а про _meta згадують у кращому разі заради локалізації. У результаті модель не розуміє, що саме показує віджет, і починає або дублювати UI текстом, або, навпаки, обіцяти користувачу «таблицю з цінами», якої у вашому віджеті немає. Кілька рядків у widgetDescription заощаджують багато таких непорозумінь.
Помилка № 4: відсутність анотацій readOnlyHint/destructiveHint.
Якщо всі ваші інструменти виглядають однаково «нейтральними», модель не розрізняє, які з них безпечно викликати часто, а які потребують підтвердження користувача. У багатокрокових сценаріях із кількома Apps це особливо критично: можна випадково зробити кілька write‑операцій поспіль без явної участі людини. Не забувайте позначати read‑only‑tools і чітко виділяти consequential/destructive дії.
Помилка № 5: відповіді, розраховані лише на людину, а не на інші Apps.
Повернути з інструмента просто «перелік подарунків» одним рядком тексту спокусливо, але тоді будь‑якому іншому App складніше використати цей результат. Структурований JSON із чіткими полями (id, price, currency, purchaseUrl, occasion) дає вам бонус і в UI, і в композиції: модель може підставити ці дані в аргументи інших tools без парсингу природної мови.
Помилка № 6: спроба в межах одного App реалізувати все, що потенційно може знадобитися користувачу.
Інколи хочеться: «ну раз я вже зробив GiftGenius, нехай він ще й календар веде, і листи колегам розсилає, і бюджет планує». В ізольованому світі це ще терпимо, але в Multi‑App‑контексті ви перетворюєтеся на комбайн, який конфліктує з іншими вузькими, добре заточеними Apps. Правильніше домовитися із собою: мій App робить X і робить це ідеально; решта — чужа відповідальність. Такий дизайн суттєво спрощує і UX, і розвиток екосистеми.
Помилка № 7: відсутність тестування поведінки в оточенні інших Apps.
Розробники часто тестують свій застосунок у Dev Mode в «чистому» чаті, де інших Apps немає. А в Store користувач легко може мати десяток підʼєднаних застосунків, частина з яких концептуально перетинаються з вашим. Не полінуйтеся створити тестовий сценарій, де в чаті є «сусідні» Apps (календар, загальний шопінг, фінанси), і прогнати golden‑кейси: чи правильно модель обирає GiftGenius у «подарункових» запитах і чи не плутає його з іншими учасниками.
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ