JavaRush /Курси /ChatGPT Apps /Multi‑App‑сценарії та композиція застосунків

Multi‑App‑сценарії та композиція застосунків

ChatGPT Apps
Рівень 20 , Лекція 4
Відкрита

1. Що таке Multi‑App‑сценарії й навіщо вони вам

Досі ми розглядали GiftGenius як єдиний зовнішній застосунок (App) у конкретному чаті: користувач обирає ваш застосунок у списку, ChatGPT підʼєднує ваші tools — і ви стаєте «головним героєм» історії. У реальному Store все інакше: користувач може підʼєднати одразу кілька застосунків, а ChatGPT вирішуватиме, який саме застосунок викликати у відповідь на конкретний запит.

Наприклад, в одному чаті можуть бути:

  • корпоративний календарний застосунок, який знає дні народження колег;
  • GiftGenius, який підбирає ідеї подарунків;
  • commerce‑застосунок компанії, який уміє оформлювати замовлення й оплату.

Користувач пише: «Нагадайте мені про дні народження колег і одразу запропонуйте, що подарувати та як купити». Модель може послідовно викликати три різні застосунки: один — для календаря, другий — для ідей подарунків, третій — для оформлення покупки.

Важливо розуміти: у користувача немає кнопки «викличіть, будь ласка, застосунок № 2 і ось його HTTP‑ендпойнт». Він спілкується природною мовою, а ChatGPT виступає маршрутизатором: читає descriptions і метадані всіх доступних застосунків та вирішує, кого і коли викликати.

Звідси випливають три ключові думки:

  • Триває конкуренція за контекст. Ваш застосунок має бути обраний серед десятків інших на основі описів, назв і поведінки.
  • Метадані стають вашим «SEO для LLM» — саме вони визначають, чи «побачить» модель GiftGenius у потрібний момент, чи проігнорує його.
  • Потрібно думати про інтероперабельність: ваші відповіді мають бути корисні не лише людині в чаті, а й іншим застосункам, які читають той самий контекст.

По суті, ми перетворюємо ізольований ChatGPT App на компонент більшої системи.

2. Як модель обирає App: ментальна модель маршрутизації

Маршрутизація в Multi‑App‑сценаріях працює приблизно так (дуже спрощено, але для розробки цього достатньо):

  1. У ChatGPT є список доступних Apps і їх tools із метаданими (імʼя, опис, JSON Schema параметрів, анотації та _meta).
  2. Користувач пише повідомлення.
  3. Модель будує внутрішнє представлення наміру (intent) і, по суті, виконує семантичний пошук у descriptions інструментів та застосунків, щоб зрозуміти, які інструменти доречні.
  4. Якщо критерії збігаються — викликає 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) Як заповнювати аргументи й які значення допустимі
_meta["openai/widgetDescription"]
Модель (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 / isConsequential
Змінює стан (покупки, видалення) Перед викликом запитати підтвердження в користувача
openWorldHint
«Виходить» у зовнішній світ (пошук, веб) Модель обережніша з обсягом і якістю результату

Анотації (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 ізоляція доволі жорстка: застосунки не викликають одне одного напряму, а комунікація йде через спільний текстовий контекст.

Базовий патерн можна сформулювати так:

  1. App A повертає в чат текст або JSON (часто всередині structuredContent/віджета).
  2. Модель читає цей вихід.
  3. У наступному ході вона може викликати App B, підставивши деталі з відповіді A в аргументи його tools.

Це називають text/context handoff: «Вихід App A → модель → вхід App B».

Приклад: CalendarApp + GiftGenius + CommerceApp

Розберімо конкретний сценарій.

Користувач: «У шефа завтра день народження, підберіть подарунок і одразу оформіть покупку».

Покроково:

  1. Модель розуміє, що спершу потрібно зʼясувати дату й людину. Вона викликає інструмент календарного App, умовно corporate_calendar.list_upcoming_birthdays, і отримує структуру:

    [
      { "name": "Олексій Биков", "date": "2025-11-22", "relation": "manager" }
    ]
    
  2. Далі модель вирішує, що час покликати GiftGenius. Вона викликає ваш suggest_gifts з аргументами, отриманими з календаря:

    {
      "recipientName": "Олексій",
      "occasion": "birthday",
      "budget": 150,
      "relationship": "manager"
    }
    

    Віджет GiftGenius показує карусель подарунків, а текстова відповідь пояснює, чому ці ідеї доречні.

  3. Користувач обирає один‑два варіанти (кнопкою у віджеті → 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 у «подарункових» запитах і чи не плутає його з іншими учасниками.

1
Опитування
LLM-Apps: нове покоління, рівень 20, лекція 4
Недоступний
LLM-Apps: нове покоління
LLM-Apps: нове покоління
Коментарі
ЩОБ ПОДИВИТИСЯ ВСІ КОМЕНТАРІ АБО ЗАЛИШИТИ КОМЕНТАР,
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ