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 — до нього ми дійдемо в цьому модулі), ви ловитимете регресії тижнями.
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ