JavaRush /Курси /ChatGPT Apps /System‑prompt і «рольовий контракт» ChatGPT

System‑prompt і «рольовий контракт» ChatGPT App

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

1. System‑prompt як контракт, а не «красивий текст»

У попередніх модулях ми розглядали ChatGPT App з погляду архітектури: віджет, tools і MCP‑сервер. У цій лекції перемкнемося на інше: як саме ми формулюємо роль моделі — що вона може робити, чого не повинна і як користуватися інструментами. Фактично ми проєктуватимемо system‑prompt як контракт між вами та моделлю.

У звичайному чаті з ChatGPT ви, можливо, звикли до промптів на кшталт «Уяви, що ти веселий помічник‑пірат» або «Пояснюй усе як для пʼятирічної дитини». Це все — про персону й стиль. У контексті ChatGPT App термін system‑prompt має зовсім інший зміст.

System‑prompt тут — це перше (приховане від користувача) повідомлення ролі system, яке задає моделі:

  • ким вона є в межах вашого App;
  • на якому завданні зосереджена;
  • чим вона не займається;
  • як і коли вона має викликати інструменти вашого застосунку.

По суті, це специфікація поведінки — дуже близька до формального контракту. Якщо ви про це домовилися, модель намагатиметься дотримуватися правил. Якщо ні — вона поводитиметься як звичайний «загальний» ChatGPT, а весь ваш чудовий App залишиться десь осторонь.

Важливо: у ChatGPT App цей system‑prompt:

  • привʼязується до самого застосунку, а не до одного конкретного діалогу;
  • задає рамки і для використання віджета, і для викликів tools;
  • діє стільки, скільки триває сесія App (поки користувач спілкується з вашим застосунком).

Якщо спростити, домовленість така: ви даєте моделі інструменти й описуєте її роль — який це асистент, що він робить і чого не робить, як користується інструментами та як ставиться до даних користувача. А модель у відповідь намагається поводитися саме так.

2. Де system‑prompt розташований в архітектурі

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

sequenceDiagram
    participant User as Користувач
    participant ChatGPT as ChatGPT + модель
    participant App as Ваш ChatGPT App
    participant MCP as MCP/Backend

    Note over ChatGPT: Під час ініціалізації App
моделі передано system‑prompt User->>ChatGPT: "Підбери подарунок другу до 50 $" ChatGPT->>ChatGPT: Застосовує system‑prompt + описи tools ChatGPT->>App: callTool recommend_gifts(...) App->>MCP: HTTP /tools/recommend_gifts MCP-->>App: Список подарунків App-->>ChatGPT: Tool result (JSON) ChatGPT-->>User: Текст + вказівка на віджет із картками

System‑prompt потрапляє до моделі в момент ініціалізації App — коли ChatGPT вирішує «підʼєднати» ваш застосунок до діалогу. Далі кожне рішення ChatGPT (чи викликати tool, чи запропонувати віджет, що сказати, якщо даних немає) проходить крізь призму цього контракту.

З погляду коду в Apps SDK це зазвичай просто рядок, який лежить десь поруч із конфігурацією App, наприклад:

// app/config/systemPrompt.ts
export const giftGeniusSystemPrompt = `
Ти — GiftGenius, асистент із підбору подарунків...
`;

Далі цей рядок потрапляє туди, де ваш застосунок зʼєднується з ChatGPT. Технічна «обвʼязка» може відрізнятися, але для вас важливо інше: system‑prompt — такий самий артефакт коду, як schema інструмента або React‑компонент. Тож його варто проєктувати й зберігати так само акуратно.

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

3. Роль App і межі відповідальності

Перший і головний розділ будь‑якого нормального system‑promptхто ви і за що відповідаєте.

Різниця між «персоною» і «App‑контрактом» проста. Сказати «ти пірат і говориш у стилі моряка» — це про тон. А сказати «ти інтерфейс до нашого каталогу подарунків, добираєш подарунки й не берешся за інші теми» — це вже контракт.

Для нашого навчального застосунку GiftGenius, який підбирає подарунки, ядро ролі може виглядати так:

Роль:
- Ти — GiftGenius, асистент із підбору подарунків нашого сервісу.
- Твоє завдання — допомагати користувачеві обрати відповідний подарунок, використовуючи лише наш каталог.
- Ти не даєш медичних, юридичних і фінансових порад.

Зверніть увагу на акценти.

По‑перше, ми чітко звужуємо домен: лише підбір подарунків у межах нашого сервісу. Це потрібно, щоб модель не починала «пояснювати квантову фізику» замість того, щоб відкрити ваш App. Також — щоб вона не намагалася будувати дивні робочі процеси навколо інших застосунків.

По‑друге, ми прямо фіксуємо те, чим App не займається. Наприклад:

  • не дає порад на теми, не повʼязані з подарунками та покупками;
  • не вигадує подарунки, яких немає в каталозі;
  • не ухвалює рішення замість користувача, а лише пропонує варіанти й аргументує.

Такі негативні обмеження часто важливіші за позитивні інструкції: модель і так «уміє все», а у system‑prompt ви якраз відсікаєте зайве.

У складніших середовищах, де в одного розробника є кілька App (наприклад, «підбір подарунків» і «відстеження доставки замовлень»), у system‑prompt варто прямо написати, що в цьому App ви займаєтеся лише підбором подарунків. Також варто зазначити, що ви не керуєте замовленнями й логістикою та не запускаєте інші застосунки. Це знижує ризик того, що модель почне плутатися: «чия це справа».

4. Коли викликати App, а коли відповідати самостійно

Наступний критичний блок контракту — правила використання інструментів.

Якщо їх не прописати, зазвичай усе зводиться до однієї з двох крайнощів:

  • модель майже ніколи не викликає ваш App, бо їй простіше й дешевше відповісти самій «із голови»;
  • або навпаки — починає викликати інструменти з будь‑якого приводу, навіть для суто теоретичних запитань.

У system‑prompt для App потрібно досить чітко зафіксувати:

  • у яких випадках потрібно використовувати інструменти;
  • у яких випадках потрібно відповідати самостійно, без tools;
  • що робити, якщо користувач прямо просить «не запускати застосунок».

Приклад текстового фрагмента для GiftGenius:

Робота з інструментами:
- Використовуй інструменти App, коли треба отримати фактичні дані з каталогу (список подарунків, ціни, типи товарів, наявність доставки та знижок).
- Відповідай самостійно, якщо питання теоретичне й не потребує доступу до каталогу (наприклад, "які подарунки зазвичай дарують на новосілля").
- Якщо користувач явно просить "не відкривати застосунок" або "відповісти без віджета", поважай це і не викликай інструменти.

Тут відбувається кілька важливих речей.

По‑перше, ми привʼязуємо виклик tools до типу запиту: фактичні (каталожні) дані → інструмент; загальна теорія → модель відповідає сама.

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

По‑третє, так ми керуємо частотою використання App. Добрий system‑prompt допомагає моделі знайти баланс: застосунок використовується тоді, коли це справді потрібно, але не перетворюється на настирливе спливне вікно, яке зʼявляється завжди.

Пізніше, у наступній лекції про UX‑інструкції, ми окремо обговоримо, як саме модель має анонсувати запуск віджета і що казати після завершення сценарію. Тут нас цікавлять саме правила ухвалення рішення: використовувати App чи ні.

5. Безпечне використання tools і робота з даними користувача

Тепер — про безпеку й здоровий глузд.

Інструменти вашого App бувають різними:

  • ті, що працюють із публічними даними (каталоги подарунків, наявність товарів, умови доставки);
  • ті, що працюють із персональними даними та/або виконують дії від імені користувача (створення замовлення, списання коштів, зміна налаштувань).

У system‑prompt варто зазначити, як модель має ставитися до цих відмінностей.

Типовий набір правил:

Безпека та конфіденційність:
- Не виконуй дії, що потребують згоди користувача (купівля, оформлення підписки, зміна персональних даних), без явного підтвердження в чаті.
- Не передавай в інструменти більше даних, ніж необхідно для їхньої роботи (мінімізація даних).
- Якщо запит стосується чутливих даних (здоров’я, фінанси, діти), спочатку уточни, чи підтверджує користувач передачу цих даних у застосунок.

Так ми одразу розвʼязуємо кілька завдань.

По‑перше, захищаємо користувача від неочікуваної активності: модель не має права самостійно купити подарунок або оформити замовлення, навіть якщо ви дали їй такий tool. Спочатку — текстове підтвердження, потім — виклик інструмента.

По‑друге, знижуємо ризик зайвого витоку даних: модель загалом схильна «затягувати все, що бачить» в аргументи інструмента. Ви ж прямо просите обмежитися мінімально потрібними полями.

По‑третє, ми окремо підкреслюємо чутливі домени, де навіть без фінансового tool можуть виникати юридичні або етичні ризики.

Хороша практика — для небезпечних інструментів окремо дописувати в їхньому описі (description), що вони змінюють стан або проводять оплату. І дублювати це на рівні system‑prompt. Так у вас зʼявляється подвійний барʼєр: і в контракті, і в описі конкретного tool.

6. Формат і стиль system‑prompt: пишемо як специфікацію

Одна з найпоширеніших помилок — писати system‑prompt як маркетинговий текст: «Ти — інноваційний, неймовірно розумний асистент, який робить світ кращим…». Це красиво, але моделі від цього ні холодно, ні жарко. Її цікавить:

  • хто я;
  • що робити;
  • чого не робити;
  • як користуватися інструментами;
  • як ставитися до даних та інших App.

Тому краще ставитися до system‑prompt як до специфікації:

  • розбивати на логічні блоки: «Роль», «Завдання», «Межі», «Робота з інструментами», «Безпека»;
  • усередині блоків писати короткі, однозначні за змістом фрази;
  • явно відділяти «робити» від «не робити» (так, у самому промпті списки цілком доречні).

Фрагмент структурованого промпта для GiftGenius може виглядати так:

Роль:
- Ти — GiftGenius, асистент із підбору подарунків нашого сервісу.

Завдання:
- Допомагай користувачеві підібрати подарунки під задачу, інтереси отримувача та бюджет.
- Пояснюй плюси й мінуси кожного варіанта простою мовою.

Не роби:
- Не вигадуй подарунки, яких немає в каталозі.
- Не обіцяй функцій сервісу, яких немає (наприклад, безкоштовну доставку, якщо за каталогом вона платна).

Стиль зручно зробити нейтральним і «сухим»: це не рекламний текст, а контракт. Чим менше двозначностей — тим стабільніша поведінка.

Ще одна важлива практика — версіонувати й зберігати system‑prompt у репозиторії разом із кодом. У промптів теж є версії, і зміни в них ламають поведінку не гірше, ніж зміни в TypeScript‑логіці. Значно приємніше під час перегляду PR побачити diff:

- Не вигадуй подарунки, яких немає в каталозі.
+ Не вигадуй подарунки, яких немає в каталозі, навіть якщо користувач прямо просить «вигадати щось».

ніж намагатися згадати, що ви «трошки підкрутили формулювання прямо в інтерфейсі».

7. Повний приклад system‑prompt для нашого навчального App

Зберімо все разом і напишімо акуратний system‑prompt для нашого навчального GiftGenius. Розібʼємо його на частини, щоб було легше читати й правити.

Спочатку опишемо роль і завдання:

Роль:
- Ти — GiftGenius, асистент із підбору подарунків нашого сервісу.
- Ти спілкуєшся з користувачем ввічливо та по‑діловому, без сленгу і жартів, якщо користувач сам їх не використовує.

Завдання:
- Допомагай підібрати подарунки за заданими параметрами (профіль отримувача, його інтереси, привід, бюджет).
- Пояснюй, чому ти пропонуєш саме ці варіанти, простою і зрозумілою мовою.

Тепер пропишемо межі та обмеження:

Межі відповідальності:
- Працюєш лише з каталогом наших подарунків і їхніми метаданими.
- Не вигадуй подарунки, акції та знижки, яких немає в каталозі або у відповіді інструментів.
- Не давай медичних, юридичних і фінансових порад.
- Не відповідай за роботу інших застосунків або сайтів; якщо користувач запитує про це — скажи, що не можеш допомогти.

Додамо правила роботи з інструментами:

Робота з інструментами:
- Використовуй tool `profile_to_segments`, коли потрібно перетворити вільний опис отримувача на сегменти інтересів.
- Використовуй tool `recommend_gifts`, коли потрібно знайти або відфільтрувати подарунки за параметрами користувача (сегменти, бюджет, привід, локаль).
- Використовуй tool `get_gift`, коли користувачеві потрібні подробиці про конкретний подарунок (опис, тип, ціна, обмеження доставки).
- Перед викликом інструментів постарайся уточнити відсутні параметри (вік отримувача, бюджет, привід), якщо без них результат буде марним.
- Якщо запит теоретичний (наприклад, "як загалом підбирати подарунки на першу річницю весілля"), відповідай сам, не викликаючи інструменти.

Тепер — блок про безпеку і дії від імені користувача:

Безпека:
- Не оформлюй купівлю, підписку чи надсилання подарунка без явного підтвердження користувача в чаті.
- Якщо для роботи інструмента потрібні особисті дані (електронна пошта отримувача, адреса доставки, імʼя), спочатку поясни користувачеві, навіщо вони потрібні, і попроси підтвердження.
- Не передавай в інструменти більше даних, ніж необхідно (наприклад, не відправляй повне повідомлення цілком, якщо достатньо віку, інтересів і бюджету).

І загальний тон/глобальні правила:

Загальні правила:
- Якщо інструменти повертають порожній результат, чесно скажи про це і запропонуй послабити умови (змінити бюджет, тип подарунка, категорію або привід).
- Якщо користувач просить "не відкривати застосунок" або "обійтися без віджета", поважай це і відповідай лише текстом, без виклику tools.
- Якщо запит не пов’язаний із подарунками або покупками, відповідай як базовий ChatGPT і не використовуй інструменти GiftGenius.

У підсумку ця конструкція дуже схожа на приклад із додаткових матеріалів: є розділи «Роль», «Завдання», «Робити/не робити», «Робота з інструментами», «Безпека», «Загальні правила».

У коді Next.js ви можете оформити це окремим модулем:

// app/config/giftGeniusPrompt.ts
export const giftGeniusSystemPrompt = `
Роль:
- Ти — GiftGenius, асистент із підбору подарунків нашого сервісу.
...

Загальні правила:
- Якщо запит не пов’язаний із подарунками, відповідай як базовий ChatGPT і не використовуй інструменти GiftGenius.
`;

А потім використати цю константу в конфігурації App (як саме — залежить від версії Apps SDK, але ідея одна й та сама: цей текст потрапляє в роль system під час ініціалізації діалогу App).

8. Динамічний контекст у system‑prompt

Іноді в system‑prompt потрібно трохи «підмішати» динаміки: поточну дату, локаль, тип користувача (новий/постійний клієнт), статус підписки тощо.

Наприклад, якщо ваш каталог подарунків і ціни відрізняються для різних регіонів, ви можете передавати в system‑prompt актуальний регіон:

export function buildSystemPrompt(locale: string) {
  return `
Роль:
- Ти — GiftGenius, асистент із підбору подарунків для регіону ${locale}.

Межі:
- Використовуй лише ті подарунки і ціни, які доступні в регіоні ${locale}.
...
`;
}

Apps SDK під час ініціалізації App може передати вам _meta["openai/locale"], а ви на його основі згенеруєте потрібний варіант промпта. Детально локалізацію ми розбиратимемо пізніше, але вже зараз корисно бачити: system‑prompt не обовʼязково має бути статичним.

Головне — не перетворювати його на «спаґеті» з умов. Якщо логіка стає надто складною, краще розділити App або винести умови в tools (наприклад, щоб MCP‑сервер сам обирав потрібне джерело даних за locale), а в system‑prompt лишити тільки високорівневі правила.

9. Як system‑prompt повʼязаний з описом tools і UX‑інструкціями

Ця лекція зосереджена на system‑prompt, але в реальному App він не існує сам по собі. Є ще описи інструментів (description, inputSchema) і приклади follow-up‑фраз, які ви задаватимете в наступних темах. Усе це разом утворює єдину систему інструкцій.

Керування викликом tools:

  • system‑prompt задає загальну філософію: «інструменти — лише для фактичних даних», «не вигадувати подарунки», «не купувати без підтвердження»;
  • описи tools уточнюють, що саме робить recommend_gifts, які параметри потрібні і коли його слід викликати;
  • follow-up‑фрази задають стиль діалогу після виклику інструмента: як чесно сказати, що нічого не знайдено, як запропонувати змінити запит, як підсумувати результати.

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

  • викликає App тоді, коли це справді потрібно;
  • не галюцинує подарунки/товари поза базою;
  • зрозуміло пояснює користувачеві, що сталося (знайшлося / не знайшлося / потрібна додаткова інформація).

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

10. Типові помилки під час роботи з system‑prompt ChatGPT App

Помилка № 1: писати system‑prompt «для краси», а не як контракт.
Дуже часто розробники обмежуються загальними фразами на кшталт «Допомагай користувачеві розвʼязувати завдання як можеш» і «Будь дружнім». Від цього моделі не стає зрозуміліше, коли викликати App, де проходять межі відповідальності, чи можна вигадувати дані та що робити у разі помилок інструмента. У результаті половина логіки «розмазується» по шарах коду й у голові автора, замість того щоб бути зафіксованою в явному контракті.

Помилка № 2: надто широка роль («допомагай у всьому»).
Якщо в розділі ролі ви пишете «Ти — асистент, який допомагає користувачеві в усіх питаннях», модель радісно починає робити саме це — і не завжди згадує про ваш App. У результаті App перетворюється на необовʼязкову опцію, якою майже не користуються, бо модель вважає, що й так упорається. Краще прямо вказати нішу: підбір подарунків, робота з каталогом подарунків, допомога в конкретному домені.

Помилка № 3: немає правил, коли викликати інструменти.
Формулювання на кшталт «використовуй інструменти за потреби» — надто розпливчасте. Модель може або повністю ігнорувати tools, або викликати їх навіть там, де можна було б відповісти «із голови». Треба чітко розділяти сценарії: фактичні дані → tool, фонові пояснення → відповідь самої моделі, явна відмова користувача від App → лише текст.

Помилка № 4: спроба «лікувати» галюцинації однією фразою «не вигадуй».
Фраза «не галюцинуй» сама по собі мало допомагає. Важливо прямо описати, що саме заборонено вигадувати (товари/подарунки поза каталогом, позиції без ID, неіснуючі знижки) і що робити за порожнього результату (чесно казати, що нічого не знайдено). Без цього модель усе одно намагатиметься «догодити» і генерувати вигадані варіанти. Потрібен повний набір: глобальна заборона в system‑prompt, обмеження в описах tools і шаблони відповідей на випадки «нічого немає».

Помилка № 5: ігнорувати безпеку та згоду користувача.
Якщо в system‑prompt не сказано, що купівля, бронювання або зміна персональних даних потребують явного підтвердження, модель може, керуючись «благим наміром», викликати інструмент самостійно. З погляду UX це катастрофа. Завжди прописуйте: будь‑які дії з фінансами або обліковим записом виконуються тільки після явної згоди в чаті.

Помилка № 6: не враховувати існування інших App та інструментів.
У світі, де в одного облікового запису може бути кілька App і багато tools, не можна розраховувати, що модель «сама здогадається», який застосунок використати. Якщо system‑prompt не фіксує, що це саме App для вибору подарунків і лише для нього, модель може непередбачувано перемикатися між різними App або намагатися застосовувати інструменти не за призначенням.

Помилка № 7: правити system‑prompt нашвидкуруч — без версіонування і тестів.
Спокусливо зайти в конфіг, змінити один рядок і повірити, що «стало тільки краще». На практиці будь‑яка правка промпта може ламати поведінку в інших сценаріях. Якщо не зберігати system‑prompt у репозиторії, не дивитися diff і не проганяти набір тестових запитів (golden prompt set — до нього ми дійдемо в цьому модулі), ви ловитимете регресії тижнями.

Коментарі
ЩОБ ПОДИВИТИСЯ ВСІ КОМЕНТАРІ АБО ЗАЛИШИТИ КОМЕНТАР,
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ