JavaRush /Курси /ChatGPT Apps /Від інструкцій до дизайну інструментів і метаданих: disco...

Від інструкцій до дизайну інструментів і метаданих: discovery та маршрутизація

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

1. Чому інструкцій недостатньо без якісних tools і метаданих

Варто зафіксувати одну неприємну істину: модель не бачить вашого коду. Вона не знає, які саме у вас контролери в Next.js, які функції в TypeScript і які чудові евристики ви заклали в сервіс рекомендацій.

Ваш застосунок вона «бачить» лише через кілька інтерфейсів:

  1. System‑prompt (рольовий контракт).
  2. Описи інструментів: імʼя, description, inputSchema, outputSchema, анотації тощо.
  3. Метадані самого застосунку: назва, іконка, короткий і довгий опис, категорії, conversation starters тощо.

Під час обробки запиту модель дивиться на контекст діалогу й ці метадані, щоб вирішити:

  • чи потрібно взагалі пропонувати якийсь застосунок;
  • якщо так — який саме з доступних;
  • і якщо застосунок обрано — який інструмент цього застосунку найкраще підходить під поточний запит.

У попередній частині Модуля 5 ми працювали з тим, що можна «сказати» моделі словами: system‑prompt і UX‑інструкціями. Тепер переходимо до того, що вона бачить, окрім тексту: tools і метадані.

Отже, завдання Модуля 5 насправді подвійне. Спершу в system‑prompt ви формулюєте «що цей застосунок має робити і як поводитися». А потім — у дизайні tools і метаданих — пакуєте це у форму, яку модель справді вміє використовувати, зокрема для discovery і маршрутизації.

Для себе це можна сформулювати так: system‑prompt — це конституція, а tools і метадані — це вже закони й уся «бюрократія» довкола: форми заяв, схеми баз даних тощо. Якщо обмежитися лише конституцією, далеко не заїдете.

2. Декомпозиція: «одне завдання — один tool», але з розумом

Почнемо з найболючішого: скільки взагалі інструментів робити й як їх правильно «розбити».

Інтуїтивний принцип такий: один інструмент — одне зрозуміле завдання. Це суттєво полегшує моделі вибір: у неї не одна монструозна функція do_everything, а кілька акуратних дій із вдалими назвами.

Для GiftGenius у нас можуть бути такі базові інструменти:

  • profile_to_segments — перетворити вільний опис отримувача (вік, інтереси, стосунки, контекст) на нормалізовані сегменти на кшталт "tech", "fitness", "gamer".
  • recommend_gifts — підібрати список id подарунків за сегментами, бюджетом, локаллю і приводом.
  • get_gift — отримати повну картку обраного подарунка (опис, медіа, SKU/варіанти) за його id.
  • (опційно) similar_gifts — за обраним подарунком запропонувати ще 3–5 схожих варіантів.

Теоретично можна було б зробити один gift_tool із параметром mode: "profile_to_segments" | "recommend" | "details" | "similar". Але тоді ви ускладнюєте життя і собі, і моделі: опис стає надто розлогим, inputSchema розростається, а під час вибору інструменту в моделі стає менше чітких «якорів».

Антипатерн: God Tool

Уявіть таку схему:

server.registerTool(
  "gift_tool",
  {
    description: "Різні операції з подарунками.",
    inputSchema: { /* 50 полів і прапорців */ },
  },
  async ({ input }) => { /* величезний switch за mode */ }
);

У «голові» моделі це виглядає так: «є якийсь абстрактний інструмент про подарунки, а далі розберемося». Це погіршує точність вибору, заважає discovery і ускладнює вам супровід.

Але й упасти в іншу крайність — зробити 50 мікроскопічних інструментів «на кожен рух» — теж погана ідея. Кожен додатковий інструмент потрапляє в контекст, навантажує увагу моделі й підвищує ризик помилок маршрутизації. Документація прямо попереджає: надто багато дрібних інструментів — мінус для якості, особливо коли їхні описи перетинаються.

Практичне правило, яким зручно користуватися:

  • усе, що користувач сприймає як один «крок» у сценарії (наприклад, первинний підбір подарунків за профілем), — гарний кандидат на окремий tool;
  • те, що завжди виконується строго всередині цього кроку й не має самостійного сенсу (наприклад, обчислити скоринг або записати перегляд карток у лог), краще залишити всередині реалізації інструменту.

Припустімо, за цим принципом ви вже розклали сценарії на 2–4 інструменти. Далі постає важливе питання: як описати входи цих tools так, щоб модель могла ними користуватися без здогадок? Із цього й почнемо.

3. Проєктуємо сценарії на Input Schema

Тепер беремо один конкретний сценарій і чесно дивимося, які дані інструменту справді потрібні.

Візьмемо ситуацію: «Дарувальник у дедлайні: підібрати 5–7 ідей для друга 25 років, який любить футбол і настільні ігри; бюджет — до 50 USD».

Із jobs‑to‑be‑done зрозуміло, що завдання рекомендаційного ядра GiftGenius — звузити вибір до невеликого списку й зменшити тривожність «а раптом я виберу дурницю». На рівні спілкування в чаті асистенту потрібні:

  • базова інформація про отримувача (вік, стать, стосунок до дарувальника);
  • інтереси/хобі;
  • бюджет і валюта;
  • привід (ДН, ювілей, Новий рік тощо);
  • опційно — країна/місто для фільтрації за доставкою.

В архітектурі GiftGenius це розкладено на два кроки:

  1. profile_to_segments(input) приймає «сирі» дані (вік, інтереси, текстовий опис) і перетворює їх на нормалізовані сегменти, з якими зручно працювати далі.
  2. recommend_gifts(segments, budget, locale, occasion) уже за сегментами й бюджетом підбирає конкретні id подарунків із каталогу.

З погляду контракту ChatGPT ↔ MCP нам важливо описати саме другий крок — схему recommend_gifts, адже саме цей інструмент використовуватиметься в більшості сценаріїв підбору.

Водночас не потрібно одразу вимагати від користувача все. Модель може добрати частину даних через follow‑up (наприклад: «А який приблизно бюджет?»). Отже, частина полів у профілі може бути опційною. Але коли ми доходимо до recommend_gifts, у нього вже має бути нормалізований набір параметрів.

Приклад: TypeScript + JSON Schema для recommend_gifts

У MCP‑сервері на TypeScript це може виглядати так:

// apps/mcp/server.ts
import { McpServer } from "@openai/mcp-server";

const server = new McpServer();

server.registerTool(
  "recommend_gifts",
  {
    title: "Рекомендації подарунків",
    description:
      "Використовуйте цей інструмент, коли потрібно підібрати подарунки за сегментами отримувача, бюджетом, локаллю і приводом.",
    inputSchema: {
      type: "object",
      properties: {
        segments: {
          type: "array",
          description:
            "Список сегментів отримувача, наприклад ['tech', 'football_fan']. Зазвичай береться з profile_to_segments.",
          items: { type: "string" },
          minItems: 1
        },
        budget: {
          type: "object",
          description:
            "Діапазон бюджету на подарунок у валюті користувача (мінімум/максимум).",
          properties: {
            min: {
              type: "number",
              minimum: 0,
              description: "Мінімальна сума, яку користувач готовий витратити."
            },
            max: {
              type: "number",
              minimum: 0,
              description: "Максимальна сума, яку користувач готовий витратити."
            },
            currency: {
              type: "string",
              minLength: 3,
              maxLength: 3,
              description: "Трилітерний код валюти (наприклад, USD, EUR, UAH)."
            }
          },
          required: ["min", "max", "currency"]
        },
        locale: {
          type: "string",
          description:
            "Локаль користувача у форматі BCP‑47 (наприклад, 'uk-UA' або 'en-US')."
        },
        occasion: {
          type: "string",
          description:
            "Привід для подарунка, наприклад 'birthday', 'new_year', 'anniversary'."
        }
      },
      required: ["segments", "budget", "locale", "occasion"]
    }
  },
  async ({ input }) => {
    // Тут ми поки не ускладнюємо, повернемо заглушку
    return {
      content: [
        {
          type: "text",
          text: `Підбираю подарунки за сегментами ${input.segments?.join(
            ", "
          )} у бюджеті ${input.budget?.min}–${input.budget?.max} ${input.budget?.currency}...`
        }
      ],
      structuredContent: {}
    };
  }
);

Зверніть увагу на кілька моментів.

По‑перше, ми активно використовуємо enum‑подібні обмеження та зрозумілі описи. Навіть якщо формально це просто рядки, description підказує моделі, які саме значення очікуються. Це помітно підвищує шанс, що вона правильно заповнить аргументи. Замість розмитого рядка "привід": "щось на кшталт дня народження" у нас акуратна occasion: "birthday".

По‑друге, описи полів пишуться не «для людей у команді», а буквально як підказки для моделі: що це за поле, які типові значення, чи є приклад. Автори документації Apps SDK прямо рекомендують додавати зрозумілі людині descriptions і приклади для кожного параметра.

Чого не має бути у вхідній схемі

Типові «паразитні» поля, які часто намагаються туди «втиснути»:

  • внутрішні ідентифікатори (tenantId, internalSegment), які й так можна додати на сервері;
  • речі, яких модель ніяк не може знати (наприклад, deploymentRegion) — це вже ваша зона відповідальності;
  • поля‑дублікати історії чату (наприклад, userPrompt): модель і так бачить початкове повідомлення, не змушуйте її копіювати.

Input Schema — це саме те, що модель має визначити й заповнити, а не «спільний мішок усього підряд».

4. Output Schema: не лише дані, а й сенс

В Apps SDK результат інструменту повертається в діалог як повідомлення role: tool. Далі модель уже вирішує, що з ним робити: як оформити відповідь, які follow‑up поставити, чи потрібно відкрити віджет тощо. Тож дизайн вихідної схеми не менш важливий, ніж вхідної.

Є два підходи.

Варіант «сирі дані» виглядає так:

{
  "items": [
    { "id": "GIFT_1" },
    { "id": "GIFT_2" }
  ]
}

Модель бачить просто список id — без розуміння, чому ці варіанти взагалі тут опинилися, скільки було кандидатів і які з них найкращі. Вона може щось домислити, але ймовірність дивних відповідей буде вищою.

Семантично насичений варіант:

{
  "items": [
    {
      "id": "GIFT_1",
      "score": 0.92,
      "reason": "Сильно матчиться з сегментом 'football_fan' і вкладається в бюджет."
    },
    {
      "id": "GIFT_2",
      "score": 0.81,
      "reason": "Підходить для шанувальника настільних ігор, трохи ближче до верхньої межі бюджету."
    }
  ],
  "meta": {
    "totalCandidates": 27,
    "returned": 5,
    "segmentsUsed": ["football_fan", "board_games"],
    "budget": { "min": 20, "max": 50, "currency": "USD" },
    "advice": "Краще почати з варіантів із найбільшим score та зрозумілим поясненням."
  }
}

Тепер модель може чесно пояснити, чому саме ці подарунки, і будувати follow‑up: «Я знайшов 27 варіантів, показую 5 найкращих — ось чому саме вони».

Приклад: описуємо Output Schema для recommend_gifts

Додамо до опису інструменту схему результату (навіть якщо технічно її можна не вказувати, краще це зробити — це частина контракту з моделлю):

const recommendGiftsOutputSchema = {
  type: "object",
  properties: {
    items: {
      type: "array",
      items: {
        type: "object",
        properties: {
          id: { type: "string", description: "ID подарунка в каталозі." },
          score: {
            type: "number",
            description: "Оцінка відповідності профілю (0..1)."
          },
          reason: {
            type: "string",
            description:
              "Коротке пояснення, чому подарунок підходить (може бути згенеровано на backend)."
          }
        },
        required: ["id", "score"]
      },
      description: "Список рекомендованих подарунків з оцінками релевантності."
    },
    meta: {
      type: "object",
      properties: {
        totalCandidates: {
          type: "integer",
          description: "Скільки всього кандидатів знайшлося в каталозі."
        },
        returned: {
          type: "integer",
          description: "Скільки подарунків повернув цей виклик."
        },
        advice: {
          type: "string",
          description:
            "Загальна порада: наприклад, з якого типу подарунків варто почати."
        }
      }
    }
  },
  required: ["items"]
};

І використовуємо цю схему всередині реалізації:

server.registerTool(
  "recommend_gifts",
  {
    title: "Рекомендації подарунків",
    description:
      "Використовуйте, коли потрібно підібрати 3–7 подарунків за сегментами і бюджетом. Повертає id подарунків і оцінки відповідності; докладні картки отримуйте через get_gift.",
    inputSchema: /* як вище */,
    // Не завжди формально вказують outputSchema, але для документації корисно:
    // outputSchema: recommendGiftsOutputSchema
  },
  async ({ input }) => {
    const recommendations = await recommendFromCatalog(input); // наша бізнес-логіка

    return {
      content: [
        {
          type: "text",
          text: `Знайшов ${recommendations.items.length} відповідних ідей. Зараз покажу найкращі.`
        }
      ],
      structuredContent: {
        items: recommendations.items,
        meta: {
          totalCandidates: recommendations.meta.totalCandidates,
          returned: recommendations.items.length,
          advice: recommendations.meta.advice
        }
      }
    };
  }
);

Ми робимо дві речі: даємо моделі мінімальний текст для користувача і водночас кладемо семантичний JSON, за яким вона може далі будувати діалог і follow‑up.

При цьому get_gift уже за id підтягне повні картки (назва, медіа, SKU тощо), а віджет GiftGenius відрендерить їх як картки подарунків.

5. Назви й описи інструментів як основа discovery

Тепер найцікавіше: як назви й описи tools впливають на те, чи викличе їх модель.

Документація й найкращі практики щодо метаданих радять:

  • використовувати action‑oriented імена: profile_to_segments, recommend_gifts, get_gift, similar_gifts, а не tool1, search, do_stuff;
  • починати опис у стилі «Use this when… / Використовуйте цей інструмент, коли…», описуючи тригерні сценарії та обмеження («не використовуйте для…»).

Це напряму повʼязано з вашим golden prompt set. Формулювання в описі мають перетинатися з реальними запитами користувачів. Якщо в описі написано «Використовуйте, коли користувач просить підібрати подарунок за бюджетом і інтересами отримувача», а у golden prompt у вас є «підібрати подарунок другові‑геймеру до 50 USD», моделі значно легше зіставити запит з інструментом.

Приклад хорошого опису інструменту

Розглянемо додатковий інструмент GiftGenius — similar_gifts, який допомагає розширити добірку завдяки схожим ідеям на основі конкретного подарунка:

server.registerTool(
  "similar_gifts",
  {
    title: "Схожі подарунки",
    description:
      "Використовуйте цей інструмент, коли користувач обрав конкретний подарунок і хоче побачити ще кілька схожих варіантів. Не використовуйте для першого підбору з нуля — для цього є recommend_gifts.",
    inputSchema: {
      type: "object",
      properties: {
        giftId: {
          type: "string",
          description:
            "Ідентифікатор подарунка з попередньої добірки, для якого потрібно знайти схожі варіанти."
        },
        limit: {
          type: "integer",
          description:
            "Скільки схожих подарунків повернути (типово 3–5).",
          minimum: 1,
          default: 5
        }
      },
      required: ["giftId"]
    }
  },
  async () => {
    /* ... */
  }
);

Важливі моменти:

  • Ми явно говоримо, коли інструмент варто використовувати, а коли — ні.
  • В описі фігурують слова «схожі варіанти», «обрав конкретний подарунок» — саме ті, які часто траплятимуться в реальних запитах користувача.
  • Уникаємо перетину з областю recommend_gifts — це зменшує конкуренцію між інструментами під час вибору.

Приклад поганого опису

description: "Робота з подарунками."

Модель із такого опису практично нічого не розуміє. Такий інструмент може спрацювати лише тоді, коли GPT уже намагається «смикнути» щось навмання.

6. Анотації та hints: як підказати моделі «серйозність» дії

Інструмент — це не лише імʼя і схема, а й анотації, які підказують ChatGPT, наскільки небезпечна або важлива дія і чи потрібно просити підтвердження користувача. У специфікації Apps SDK для цього є різні hints, як‑от readOnlyHint, destructiveHint, openWorldHint та інші.

  • readOnlyHint: true каже, що інструмент лише читає дані й не змінює стан. Тоді асистент може пропустити зайві підтвердження і викликати його вільніше.
  • destructiveHint: true сигналізує, що інструмент може щось видалити або безповоротно змінити, тому слід показати користувачу явне «Ви впевнені?».
  • openWorldHint: true вказує, що дія зачіпає зовнішній світ (постинг у соцмережах, створення запису поза обліковим записом тощо) — і про це теж важливо попередити.

Мінімальний рівень — без підтверджень

Якщо у вас є public readonly tools, має сенс позначати їх як readOnlyHint: true. Приклад:

"annotations": {
  "readOnlyHint": true,
  "destructiveHint": false,
  "openWorldHint": false
}

Такі інструменти можна викликати без зайвих діалогових підтверджень із боку GPT.

Одне підтвердження

Якщо у вас є tools, які щось змінюють на сервері, логічно позначати їх як readOnlyHint: false:

"annotations": {
  "readOnlyHint": false,
  "destructiveHint": false,
  "openWorldHint": false
}

Модель, побачивши такий інструмент, найімовірніше попросить у користувача підтвердження один раз (зазвичай це модальне діалогове вікно в UI ChatGPT).

Небезпечна дія

Якщо у вас є tool, який щось видаляє на сервері, позначте його як destructiveHint: true:

"annotations": {
  "readOnlyHint": false,
  "destructiveHint": true,
  "openWorldHint": false
}

Модель буде дуже обережно викликати цей tool і двічі уточнить:

  • спочатку попросить підтвердження в користувача в тексті,
  • потім платформа покаже стандартне діалогове вікно.

Для нашого GiftGenius у межах цього модуля ми поки не пишемо commerce‑інструменти, але можна накидати, як виглядатиме майбутній create_gift_order:

server.registerTool(
  "create_gift_order",
  {
    title: "Створення замовлення на подарунок",
    description:
      "Використовуйте лише після явної згоди користувача купити обраний подарунок. Створює замовлення в системі та повертає статус.",
    inputSchema: {
      type: "object",
      properties: {
        giftId: {
          type: "string",
          description: "ID подарунка, який користувач обрав."
        },
        deliveryEmail: {
          type: "string",
          description: "Email, на який потрібно надіслати цифровий подарунок."
        }
      },
      required: ["giftId", "deliveryEmail"]
    },
    annotations: {
      destructiveHint: true,
      openWorldHint: true
    }
  },
  async () => {
    /* ... */
  }
);

Анотації не заміняють ваших перевірок прав на сервері. Вони лише допомагають ChatGPT вибудувати UX: запитати підтвердження, показати попередження й не запускати такі інструменти «наосліп».

7. Метадані застосунку і два рівні discovery

Інструменти — це половина історії. Друга половина — як користувач узагалі знаходить і запускає ваш застосунок.

В екосистемі ChatGPT є два ключові рівні discovery.

Перший — in‑conversation discovery. Коли користувач пише щось у чаті (навіть без явної згадки застосунку), модель дивиться:

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

На основі цього вона вирішує, чи варто запропонувати якийсь застосунок, і якщо так — який і з яким сценарієм. Тут особливо важливі описи інструментів і самого застосунку. Якщо в них є «тригери» на кшталт «підбір подарунків», «ідея подарунка», «бюджет подарунка», шанс, що модель обере ваш застосунок, різко зростає.

Другий рівень — глобальний discovery: каталог і launcher. Там уже вирішує людина: вона очима обирає застосунок за назвою, іконкою, коротким описом і тегами. Тут важливо, щоб ви чесно й зрозуміло пояснили, що робить ваш застосунок, для кого він і в чому його основна цінність.

Можна звести це в невелику таблицю:

Шар Що бачить модель/користувач Що важливо в метаданих
In‑conversation Текст діалогу, описи tools і застосунку Тригерні формулювання, action‑найменування, обмеження
Каталог/launcher Назва, іконка, short/long description, теги Чітке позиціонування, зрозумілі ціннісні пропозиції

Для GiftGenius можна, наприклад, сформулювати:

  • Назва: GiftGenius — підбір подарунків за 60 секунд.
  • Короткий опис: Збирає профіль отримувача і пропонує 5–7 ідей подарунків із можливістю миттєвої покупки всередині ChatGPT.
  • Опис для in‑conversation: Використовуйте цей застосунок, коли користувач просить допомогти обрати подарунок, не знає, що подарувати, називає бюджет, інтереси отримувача або привід.

Ці формулювання дуже бажано синхронізувати з тим, що ви вже писали в system‑prompt і в описі інструменту recommend_gifts. Тоді модель бачить цілісну картину, а не набір суперечливих текстів.

8. Як маршрутизація працює «в голові» ChatGPT

Зберемо все докупи й подивімося на типовий шлях запиту — без заглиблення в протокол MCP. До цього перейдемо в наступних модулях.

Нехай користувач пише:

«Допоможи вигадати подарунок брату: він обожнює футбол і настолки, бюджет — до 50 доларів».

Грубо спрощений алгоритм:

  1. Модель аналізує повідомлення й історію. Бачить слова «подарунок», «брат», «футбол», «настолки», «бюджет 50».
  2. Порівнює це з описами доступних застосунків та їхніх інструментів. Для GiftGenius описи явно містять «підбір подарунків за інтересами і бюджетом», тож імовірність того, що застосунок релевантний, висока.
  3. Якщо застосунок ще не активний у цій сесії, модель формує репліку‑анонс: «Я можу відкрити застосунок GiftGenius, який допоможе підібрати подарунок за вашими параметрами. Відкрити?» — це ми заздалегідь прописали в UX‑інструкціях.
  4. Після згоди користувача модель обирає всередині застосунку інструмент recommend_gifts, тому що саме його опис найкраще відповідає поточному наміру. Тут і імʼя, і description, і структура inputSchema працюють як вхідні сигнали.
  5. Модель заповнює аргументи інструменту на основі запиту: спочатку (за потреби) викликає profile_to_segments, щоб із тексту «брат, любить футбол і настолки» отримати сегменти ["football_fan", "board_games"], потім викликає recommend_gifts з segments, budget: {min: 0, max: 50, currency: "USD"}, locale, occasion: "birthday".
  6. MCP‑сервер виконує інструмент, формує structured output з items і meta та повертає його.
  7. Модель читає JSON, який ви описали в outputSchema, і будує відповідь: пояснює, що знайшла, чому саме ці подарунки, і пропонує follow‑up («хочете звузити за категорією?», «показати схожі на цей подарунок?» або «оформити покупку цього подарунка?»).

Ось проста блок‑схема цього процесу:

flowchart TD
  A[User: запит про подарунок] --> B[ChatGPT аналізує контекст]
  B --> C[Порівняння з метаданими App і tools]
  C -->|релевантно| D[Анонс GiftGenius]
  D -->|користувач погоджується| E["Виклик recommend_gifts (+ profile_to_segments)"]
  E --> F[MCP‑сервер GiftGenius]
  F --> G[JSON‑результат з items/meta]
  G --> H[Модель формує відповідь і follow‑up]

Що краще ви описали інструменти й сценарії, то менше тут випадковості — і то стабільнішою буде маршрутизація.

Insight: Tool Call SEO

В екосистемі Apps у вас скоро буде не лише конкуренція за увагу людей у каталозі, а й конкуренція за увагу самої моделі. На один і той самий користувацький запит ChatGPT може викликати десяток різних застосунків, а вибір відбуватиметься не там, де презентація краща, а у «пошуковій видачі» всередині «голови» моделі. Цей невидимий шар дедалі більше нагадує SEO — тільки замість сторінок у вас tools і MCP‑сервери.

Модель, по суті, ранжує кандидатів: спочатку на рівні застосунку, потім — на рівні окремих інструментів. Вона дивиться на назву, descriptions, схеми, анотації й співвідносить їх із формулюваннями запиту. Якщо в описі recommend_gifts є «підбір подарунків за бюджетом і інтересами отримувача», а в запиті звучить «підібрати подарунок другові‑геймеру за 50 USD», у цього інструменту більше шансів «потрапити в топ» видачі, ніж у абстрактного search з описом «робота з подарунками».

Звідси народжується практична ідея Tool Call SEO: ставитися до імен, descriptions, enum‑значень і метаданих як до ключових слів і сніпетів. Ви не просто описуєте контракт для розробників — ви оптимізуєте його під реальний трафік запитів із вашого golden prompt set. Надто загальні формулювання, перехрещені області кількох tools, God‑інструменти без чіткої ніші — усе це знижує «CTR» вашого застосунку в «голові» моделі.

9. Невелика практична вправа

Спробуйте подумки (або у своєму репозиторії) виконати таке.

Спочатку оберіть один із ключових сценаріїв GiftGenius — наприклад, «Підібрати подарунок колезі по роботі з обмеженим бюджетом».

Сформулюйте для нього:

  1. Який окремий інструмент під цей сценарій потрібен: це чистий recommend_gifts, чи вам потрібен ще спеціалізований інструмент для B2B‑кейсу, або, скажімо, достатньо після recommend_gifts використати similar_gifts для варіацій?
  2. Які поля справді необхідні у вхідній схемі recommend_gifts. Які поля можна спитати в користувача окремо (через follow‑up), а не змушувати модель вгадувати.
  3. Як має виглядати outputSchema, щоб модель могла чесно пояснити вибір і запропонувати наступні кроки (наприклад, перемкнутися на B2B‑режим, показати лише цифрові подарунки, звузити за ціновим діапазоном).

А потім подивіться на свій golden prompt set із минулої лекції й перевірте:

  • чи є для кожного еталонного запиту очевидний інструмент (recommend_gifts, get_gift, similar_gifts тощо);
  • чи не вийшло так, що два інструменти однаково «пасують» до одного й того ж запиту (overlapping tools);
  • чи потрібно посилити описи або перейменувати якийсь tool, щоб модель менше плуталася.

Це саме той процес, який ви повторюватимете перед кожною серйозною зміною промпта, схем або логіки — по суті, міні‑eval якості discovery.

Якщо звести все вище до чек‑ліста, на цьому етапі вам потрібно:

  • чесно розкласти сценарії на 2–4 осмислені інструменти;
  • акуратно описати inputSchema/outputSchema із прикладами й enum‑ами;
  • навести лад у назвах, descriptions і анотаціях;
  • синхронізувати це з system‑prompt і метаданими застосунку.

У наступних модулях ми вже дивитимемося, як це все працює через MCP і як діагностувати дивну поведінку discovery/маршрутизації.

10. Типові помилки під час проєктування tools і метаданих

Помилка №1: «Ми все описали в system‑prompt, а інструменти якось розберуться».
Якщо ви чудово розписали роль застосунку, межі відповідальності й UX‑поведінку, але водночас лишили інструменти з назвами tool1, search, do_stuff і схемами без описів, модель просто не зможе повʼязати ваш гарний текст із реальними викликами. Для ChatGPT інструменти — це основний інтерфейс. Без грамотних метаданих жоден system‑prompt не врятує.

Помилка №2: God Tool, який робить усе підряд.
Бажання «оптимізувати» й зробити одну функцію з параметром mode зрозуміле, але воно призводить до монструозних JSON‑схем, плутанини в описах і погіршення маршрутизації. Модель починає гадати, який режим використати, а ви — підтримувати величезний switch на сервері. Краще кілька чітких інструментів під конкретні кроки сценарію, ніж один «зроби все».

Помилка №3: Вхідна схема, переповнена полями «про всяк випадок».
Часто розробники намагаються одразу протягнути через inputSchema усі параметри, які будь‑коли можуть знадобитися, і ще кілька внутрішніх полів. У підсумку модель намагається вгадати те, чого вона не може знати (наприклад, tenantId), а ви потім дивуєтеся дивним значенням. Input Schema має містити лише те, що модель реально може вивести з діалогу або уточнити запитанням. Внутрішні деталі додавайте на сервері.

Помилка №4: «Німі» output‑дані без метаінформації.
Повернути з інструменту просто масив обʼєктів — спокусливо. Але так ви позбавляєте модель розуміння, чому ці результати зʼявилися. Без полів на кшталт score, reason, searchCriteria, totalCandidates їй складніше будувати чесні пояснення і follow‑up. Додавання невеликої meta‑обгортки з критеріями пошуку й порадами часто радикально покращує якість відповіді.

Помилка №5: Порожні описи на кшталт «Робота з подарунками», «Пошук курсів», «Обробка даних».
Такі описи погані тим, що не дають моделі ані тригерів, ані обмежень. Вона не знає, коли саме потрібно викликати інструмент і в якій області він застосовний. Гарний опис починається з «Використовуйте цей інструмент, коли…» і містить конкретні сценарії та заборони на кшталт «Не використовуйте для…». Ідеально, якщо ці формулювання перетинаються із золотими запитами з вашого golden prompt set.

Помилка №6: Ігнорування анотацій і змішування read‑only та дій, що змінюють стан.
Якщо ви не позначаєте інструменти, які лише читають дані (readOnlyHint), і ті, що здійснюють дії (destructiveHint, openWorldHint), модель не може вибудувати правильний UX підтверджень. У підсумку або зайві «Ви впевнені?» на кожному кроці, або, навпаки, «тихі» покупки й зміни без згоди користувача. Анотації — дешевий і ефективний спосіб підказати моделі важливість операції.

Помилка №7: Метадані застосунку для каталогу й метадані для in‑conversation живуть у різних всесвітах.
Буває, що короткий опис у каталозі написаний маркетологом («Революційний AI‑асистент, який змінює ваше життя»), а descriptions tools і system‑prompt — розробником («підбір подарунків за бюджетом»). У результаті в каталозі незрозуміло, про що взагалі застосунок, а модель у чаті не може зіставити запити на кшталт «що це за сервіс?» із реальними можливостями застосунку. Пишіть метадані як єдину специфікацію, а не як два незалежні маркетингові тексти.

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