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, и чтобы она не пыталась за вас строить странные workflow’ы вокруг других приложений.

Во‑вторых, мы явно фиксируем то, чего App не делает. Например:

  • не даёт советы на темы, не связанные с подарками и покупками;
  • не придумывает подарки, которых нет в каталоге;
  • не принимает решение за пользователя, а только предлагает варианты и аргументирует.

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

Для более сложных окружений, где у одного разработчика несколько App (например, «подбор подарков» и «отслеживание доставки заказов»), в system‑prompt полезно прямо написать, что в этом App ты занимаешься только подбором подарков, не берёшься за управление заказами и логистикой и не запускаешь другие приложения. Это снижает риск того, что модель начнёт путаться, «чьё это дело».

4. Когда вызывать App, а когда отвечать самому

Следующий критический блок контракта: правила использования инструментов.

Если их не прописать, обычно всё уходит в одну из двух крайностей:

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

В system‑prompt для App нужно довольно чётко зафиксировать:

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

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

Работа с инструментами:
- Используй инструменты App, когда нужно получить фактические данные из каталога (список подарков, цены, типы товаров, наличие доставки и скидок).
- Отвечай самостоятельно, если вопрос теоретический и не требует доступа к каталогу (например, "какие подарки обычно дарят на новоселье").
- Если пользователь явно просит "не открывать приложение" или "ответить без виджета", уважай это и не вызывай инструменты.

Здесь происходит несколько важных вещей.

Во‑первых, мы привязываем вызов tools к типу запроса: фактические/каталожные данные → инструмент; общая теория → модель сама отвечает.

Во‑вторых, мы явно говорим про уважение намерений пользователя: если человек пишет «не надо ничего запускать, просто объясни», модель не должна игнорировать этот сигнал.

В‑третьих, мы тем самым управляем частотой использования App. Хороший system‑prompt помогает модели находить баланс: App используется, когда нужно, но не превращается в назойливый поп‑ап, который лезет всегда.

Позже, в следующей лекции про 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 увидеть дифф:

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

чем пытаться вспомнить, что вы «чуть‑чуть подкрутили формулировку прямо в интерфейсе».

7. Полный пример system‑prompt для нашего учебного App

Давайте соберём всё вместе и напишем аккуратный system‑prompt для нашего учебного GiftGenius. Разобьём его на кусочки, чтобы было легче читать и править.

Сначала опишем роль и задачи:

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

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

Теперь пропишем границы и ограничения:

Границы ответственности:
- Работаешь только с каталогом наших подарков и их метаданными.
- Не придумывай подарки, акции и скидки, которых нет в каталоге или ответе инструментов.
- Не давай медицинских, юридических и финансовых советов.
- Не отвечай за работу других приложений или сайтов; если пользователь спрашивает про это — скажи, что не можешь помочь.

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

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

Теперь — блок про безопасность и действия от имени пользователя:

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

И общий тон/глобальные правила:

Общие правила:
- Если инструменты возвращают пустой результат, честно скажи об этом и предложи ослабить условия (изменить бюджет, тип подарка, категорию или повод).
- Если пользователь просит "не открывать приложение" или "обойтись без виджета", уважай это и отвечай только текстом, без вызова 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 задаёт общую философию: «инструменты только для фактических данных», «не придумывать подарки», «не покупать без подтверждения»;
  • descriptions 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 в репозитории, не смотреть диффы и не прогонять набор тестовых запросов (golden prompt set — до него мы дойдём в этом модуле), вы будете ловить регрессии неделями.

1
Задача
ChatGPT Apps, 5 уровень, 0 лекция
Недоступна
Статический system‑prompt как артефакт кода + страница предпросмотра
Статический system‑prompt как артефакт кода + страница предпросмотра
1
Задача
ChatGPT Apps, 5 уровень, 0 лекция
Недоступна
Динамический buildSystemPrompt(locale, today) + API для просмотра
Динамический buildSystemPrompt(locale, today) + API для просмотра
Комментарии
ЧТОБЫ ПОСМОТРЕТЬ ВСЕ КОММЕНТАРИИ ИЛИ ОСТАВИТЬ КОММЕНТАРИЙ,
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ