CodeGym /Kursy /ChatGPT Apps /System‑prompt i „kontrakt roli” ChatGPT

System‑prompt i „kontrakt roli” ChatGPT App

ChatGPT Apps
Poziom 5 , Lekcja 0
Dostępny

1. System‑prompt jako kontrakt, a nie ładny tekst

W poprzednich modułach patrzyliśmy na ChatGPT App od strony architektury: widżet, tools i serwer MCP. W tej lekcji skupimy się na tym, jakimi słowami wyjaśniamy modelowi jego rolę: co może robić, czego nie powinien i jak używać narzędzi. W istocie będziemy projektować system‑prompt jako kontrakt między wami a modelem.

W zwykłym czacie z ChatGPT być może przywykliście do promptów w duchu „Wyobraź sobie, że jesteś wesołym pomocnikiem‑piratem” albo „Wyjaśniaj wszystko jak pięcioletniemu dziecku”. To wszystko dotyczy osoby i stylu. W kontekście ChatGPT App termin system‑prompt ma zupełnie inne znaczenie.

System‑prompt tutaj to ukryta przed użytkownikiem pierwsza wiadomość roli system, która określa modelowi:

  • kim jest w ramach waszej aplikacji;
  • na jakim zadaniu jest skupiona;
  • czym się NIE zajmuje;
  • jak i kiedy powinna wywoływać narzędzia waszej aplikacji.

W istocie to specyfikacja zachowania, bardzo bliska formalnemu kontraktowi. Jeśli się umówicie — model stara się go przestrzegać. Jeśli się nie umówicie — będzie działać jak zwykły „ogólny” ChatGPT i cała wasza świetna aplikacja pozostanie gdzieś z boku.

Ważne, że dla ChatGPT App ten system‑prompt:

  • jest powiązany z samą aplikacją, a nie z jedną konkretną rozmową;
  • nakłada ramy zarówno na użycie widżetu, jak i na wywołania tools;
  • „żyje” tak długo, jak trwa sesja aplikacji (dopóki użytkownik rozmawia z waszą aplikacją).

Upraszczając, umowa jest taka: dajecie modelowi narzędzia i opisujecie jego rolę — jakim jest asystentem, co robi, czego nie robi, jak używa narzędzi i jak podchodzi do danych użytkownika. Model w zamian stara się zachowywać właśnie tak.

2. Gdzie system‑prompt żyje w architekturze

Aby nie wydawało się, że to jakaś magiczna istota, warto zobaczyć go na schemacie.

sequenceDiagram
    participant User as Użytkownik
    participant ChatGPT as ChatGPT + model
    participant App as Wasza aplikacja ChatGPT
    participant MCP as MCP/Backend

    Note over ChatGPT: Podczas inicjalizacji aplikacji
model otrzymuje system‑prompt User->>ChatGPT: "Dobierz prezent dla przyjaciela do 50 $" ChatGPT->>ChatGPT: Zastosowuje system‑prompt + opisy tools ChatGPT->>App: callTool recommend_gifts(...) App->>MCP: HTTP /tools/recommend_gifts MCP-->>App: Lista prezentów App-->>ChatGPT: Tool result (JSON) ChatGPT-->>User: Tekst + wskazanie widżetu z kartami

System‑prompt trafia do modelu w momencie inicjalizacji aplikacji, gdy ChatGPT postanawia „podłączyć” waszą aplikację do dialogu. Dalej każda decyzja ChatGPT — czy wołać tool, proponować widżet, co powiedzieć, gdy nie ma danych — przechodzi już przez pryzmat tego kontraktu.

Z punktu widzenia kodu w Apps SDK to zwykle po prostu łańcuch, leżący gdzieś obok konfiguracji aplikacji, na przykład:

// app/config/systemPrompt.ts
export const giftGeniusSystemPrompt = `
Jesteś GiftGenius, asystentem do doboru prezentów...
`;

Następnie ten łańcuch trafia tam, gdzie wasza aplikacja łączy się z ChatGPT. Techniczna „otoczka” może się różnić, ale dla was ważne jest: system‑prompt to taki sam artefakt kodu jak schema narzędzia czy komponent React i należy go projektować oraz przechowywać równie starannie.

Teraz, gdy jasne jest, gdzie system‑prompt żyje w architekturze i jak trafia do modelu, najważniejsze — co dokładnie do niego włożyć, aby był kontraktem, a nie tylko opisem „miłej persony”.

3. Rola aplikacji i granice odpowiedzialności

Pierwsza i najważniejsza sekcja każdego porządnego system‑promptkim jesteś i za co odpowiadasz.

Różnica między „personą” a „kontraktem aplikacji” jest prosta: powiedzieć „jesteś piratem i mówisz jak marynarz” — to kwestia tonu; powiedzieć „jesteś interfejsem do naszego katalogu prezentów, dobierasz prezenty i nie wchodzisz w inne tematy” — to już kontrakt.

Dla naszej aplikacji szkoleniowej GiftGenius, która dobiera prezenty, jądro roli może wyglądać tak:

Rola:
- Jesteś GiftGenius, asystentem do doboru prezentów naszego serwisu.
- Twoje zadanie — pomagać użytkownikowi wybrać odpowiedni prezent, używając tylko naszego katalogu.
- Nie udzielasz porad medycznych, prawnych ani finansowych.

Zwróć uwagę na akcenty.

Po pierwsze, wąsko definiujemy domenę: tylko dobór prezentów w ramach naszego serwisu. To potrzebne, aby model nie zaczynał „wyjaśniać fizyki kwantowej” zamiast otworzyć waszą aplikację i aby nie próbował budować za was dziwnych workflowów wokół innych aplikacji.

Po drugie, wyraźnie ustalamy czego aplikacja nie robi. Na przykład:

  • nie udziela porad na tematy niezwiązane z prezentami i zakupami;
  • nie wymyśla prezentów, których nie ma w katalogu;
  • nie podejmuje decyzji za użytkownika — jedynie proponuje opcje i uzasadnia.

Takie negatywne ograniczenia są często ważniejsze niż pozytywne instrukcje: model i tak „umie wszystko”, a wy w system‑prompt właśnie odcinacie zbędne rzeczy.

W bardziej złożonych środowiskach, gdzie jeden deweloper ma kilka aplikacji (np. „dobór prezentów” i „śledzenie dostawy zamówień”), w system‑prompt warto wprost napisać, że w tej aplikacji zajmujesz się tylko dobieraniem prezentów, nie bierzesz na siebie zarządzania zamówieniami i logistyką oraz nie uruchamiasz innych aplikacji. To zmniejsza ryzyko, że model zacznie się mylić, „czyja to sprawa”.

4. Kiedy wywoływać aplikację, a kiedy odpowiadać samemu

Kolejny krytyczny blok kontraktu: zasady używania narzędzi.

Jeśli ich nie określić, zwykle wszystko idzie w jedną z dwóch skrajności:

  • model prawie nigdy nie wywołuje waszej aplikacji, bo łatwiej i taniej odpowiedzieć „z głowy”;
  • albo odwrotnie — zaczyna wywoływać narzędzia z byle powodu, nawet przy najbardziej teoretycznych pytaniach.

W system‑prompt dla aplikacji należy dość jasno ustalić:

  • w jakich przypadkach należy użyć narzędzi;
  • w jakich przypadkach należy odpowiedzieć samemu, bez tools;
  • co robić, jeśli użytkownik jawnie prosi „nie uruchamiać aplikacji”.

Przykładowy fragment tekstu dla GiftGenius:

Praca z narzędziami:
- Używaj narzędzi aplikacji, gdy trzeba pobrać dane faktograficzne z katalogu (lista prezentów, ceny, typy produktów, dostępność dostawy i zniżek).
- Odpowiadaj samodzielnie, jeśli pytanie jest teoretyczne i nie wymaga dostępu do katalogu (np. "jakie prezenty zwykle daje się na parapetówkę").
- Jeśli użytkownik jawnie prosi "nie otwierać aplikacji" lub "odpowiedzieć bez widżetu", uszanuj to i nie wywołuj narzędzi.

Tu dzieje się kilka ważnych rzeczy.

Po pierwsze, wiążemy wywołanie tools z typem zapytania: dane faktyczne/katalogowe → narzędzie; ogólna teoria → model odpowiada sam.

Po drugie, mówimy wprost o szacunku dla intencji użytkownika: jeśli ktoś pisze „nie uruchamiaj niczego, po prostu wyjaśnij”, model nie powinien ignorować tego sygnału.

Po trzecie, w ten sposób zarządzamy częstotliwością użycia aplikacji. Dobry system‑prompt pomaga modelowi znaleźć równowagę: aplikacja jest używana, gdy trzeba, ale nie zamienia się w natrętny pop‑up, który pojawia się zawsze.

Później, w następnej lekcji o instrukcjach UX, osobno porozmawiamy o tym, jak dokładnie model powinien zapowiadać uruchomienie widżetu i co mówić po zakończeniu scenariusza. Tu interesują nas właśnie zasady podejmowania decyzji: użyć aplikacji czy nie.

5. Bezpieczne użycie tools i praca z danymi użytkownika

Teraz — o bezpieczeństwie i zdrowym rozsądku.

Narzędzia waszej aplikacji bywają różne:

  • takie, które pracują z danymi publicznymi (katalogi prezentów, dostępność towarów, warunki dostawy);
  • takie, które pracują z danymi osobowymi i/lub wykonują działania w imieniu użytkownika (utworzenie zamówienia, obciążenie środków, zmiana ustawień).

W system‑prompt trzeba zaznaczyć, jak model ma podchodzić do tych różnic.

Typowy zestaw zasad:

Bezpieczeństwo i poufność:
- Nie wykonuj działań wymagających zgody użytkownika (zakup, uruchomienie subskrypcji, zmiana danych osobowych) bez wyraźnego potwierdzenia na czacie.
- Nie przekazuj do narzędzi więcej danych, niż jest to konieczne do ich działania (minimalizacja danych).
- Jeśli prośba dotyczy danych wrażliwych (zdrowie, finanse, dzieci), najpierw zapytaj, czy użytkownik potwierdza przekazanie tych danych do aplikacji.

Tutaj od razu rozwiązujemy kilka zadań.

Po pierwsze, chronimy użytkownika przed nieoczekiwaną aktywnością: model nie ma prawa sam z siebie kupić prezentu lub złożyć zamówienia, jeśli daliście mu takie narzędzie. Najpierw — potwierdzenie tekstowe, potem — wywołanie narzędzia.

Po drugie, ograniczamy ryzyko zbędnego wycieku danych: model ma skłonność „wrzucić wszystko, co widzi” do argumentów narzędzia; wy zaś prosicie wyraźnie, by ograniczyć się do minimalnie potrzebnych pól.

Po trzecie, osobno podkreślamy wrażliwe domeny, gdzie nawet bez narzędzia finansowego mogą pojawić się ryzyka prawne/etyczne.

Dobrą praktyką jest dopisanie dla niebezpiecznych narzędzi w ich opisie (description), że zmieniają stan lub dokonują płatności, oraz zduplikowanie tego na poziomie system‑prompt. Dzięki temu macie podwójną barierę: i w kontrakcie, i w opisie konkretnego narzędzia.

6. Format i styl system‑prompt: piszemy jak specyfikację

Jednym z najczęstszych błędów jest pisanie system‑prompt jak tekstu marketingowego: „Jesteś innowacyjnym, niesamowicie mądrym asystentem, który czyni świat lepszym…”. To ładne, ale modelowi ani zimno, ani ciepło. Interesuje go:

  • kim jestem;
  • co robić;
  • czego nie robić;
  • jak korzystać z narzędzi;
  • jak odnosić się do danych i innych aplikacji.

Dlatego lepiej traktować system‑prompt jak specyfikację:

  • dzielić na logiczne bloki: „Rola”, „Zadania”, „Granice”, „Praca z narzędziami”, „Bezpieczeństwo”;
  • w środku pisać krótkimi, jednoznacznymi zdaniami;
  • wyraźnie wyróżniać „robić” i „nie robić” (tak, listy wewnątrz samego promptu są jak najbardziej na miejscu).

Fragment zorganizowanego promptu dla GiftGenius może wyglądać tak:

Rola:
- Jesteś GiftGenius, asystentem do doboru prezentów naszego serwisu.

Zadania:
- Pomagaj użytkownikowi dobrać prezenty do zadania, zainteresowań obdarowywanego i budżetu.
- Wyjaśniaj plusy i minusy każdej opcji prostym językiem.

Nie rób:
- Nie wymyślaj prezentów, których nie ma w katalogu.
- Nie obiecuj funkcji serwisu, których nie ma (np. darmowej dostawy, jeśli według katalogu jest płatna).

Styl warto utrzymać neutralny i „suchy”: to nie tekst sprzedażowy, tylko kontrakt. Im mniej dwuznaczności — tym stabilniejsze zachowanie.

Jeszcze jedna ważna praktyka: wersjonowanie i przechowywanie system‑prompt w repozytorium razem z kodem. Prompty też mają wersje, a zmiany w nich potrafią psuć zachowanie nie gorzej niż zmiany w logice TypeScript. Znacznie przyjemniej w review PR zobaczyć diff:

- Nie wymyślaj prezentów, których nie ma w katalogu.
+ Nie wymyślaj prezentów, których nie ma w katalogu, nawet jeśli użytkownik wyraźnie prosi „wymyśl coś”.

niż próbować sobie przypomnieć, że „troszkę podkręciliście sformułowanie prosto w interfejsie”.

7. Pełny przykład system‑prompt dla naszej aplikacji

Zbierzmy wszystko razem i napiszmy staranny system‑prompt dla naszej aplikacji szkoleniowej GiftGenius. Podzielimy go na kawałki, żeby łatwiej było czytać i edytować.

Najpierw opiszmy rolę i zadania:

Rola:
- Jesteś GiftGenius, asystentem do doboru prezentów naszego serwisu.
- Komunikujesz się z użytkownikiem uprzejmie i rzeczowo, bez slangu i żartów, jeśli użytkownik sam ich nie używa.

Zadania:
- Pomagaj dobrać prezenty według zadanych parametrów (profil obdarowywanego, jego zainteresowania, okazja, budżet).
- Wyjaśniaj, dlaczego proponujesz właśnie te opcje, prostym i zrozumiałym językiem.

Teraz określmy granice i ograniczenia:

Granice odpowiedzialności:
- Pracujesz tylko z naszym katalogiem prezentów i jego metadanymi.
- Nie wymyślaj prezentów, promocji i zniżek, których nie ma w katalogu ani w odpowiedzi narzędzi.
- Nie udzielaj porad medycznych, prawnych ani finansowych.
- Nie odpowiadasz za działanie innych aplikacji lub stron; jeśli użytkownik o to pyta — powiedz, że nie możesz pomóc.

Dodajmy zasady pracy z narzędziami:

Praca z narzędziami:
- Używaj narzędzia `profile_to_segments`, gdy trzeba zamienić swobodny opis obdarowywanego na segmenty zainteresowań.
- Używaj narzędzia `recommend_gifts`, gdy trzeba znaleźć lub odfiltrować prezenty według parametrów użytkownika (segmenty, budżet, okazja, lokalizacja).
- Używaj narzędzia `get_gift`, gdy użytkownik potrzebuje szczegółów konkretnego prezentu (opis, typ, cena, ograniczenia dostawy).
- Przed wywołaniem narzędzi postaraj się doprecyzować brakujące parametry (wiek obdarowywanego, budżet, okazja), jeśli bez nich wynik będzie bezwartościowy.
- Jeśli pytanie jest teoretyczne (np. "jak ogólnie dobierać prezenty na pierwszą rocznicę ślubu"), odpowiadaj sam, nie wywołując narzędzi.

Teraz — blok o bezpieczeństwie i działaniach w imieniu użytkownika:

Bezpieczeństwo:
- Nie finalizuj zakupu, subskrypcji ani wysyłki prezentu bez wyraźnego potwierdzenia użytkownika na czacie.
- Jeśli do działania narzędzia potrzebne są dane osobowe (e‑mail obdarowywanego, adres dostawy, imię), najpierw wyjaśnij użytkownikowi, po co są potrzebne, i poproś o potwierdzenie.
- Nie przekazuj do narzędzi więcej danych, niż to konieczne (np. nie wysyłaj całej wiadomości w całości, jeśli wystarczą wiek, zainteresowania i budżet).

I ogólny ton/zasady globalne:

Ogólne zasady:
- Jeśli narzędzia zwracają pusty wynik, powiedz o tym wprost i zaproponuj poluzowanie warunków (zmienić budżet, typ prezentu, kategorię lub okazję).
- Jeśli użytkownik prosi "nie otwierać aplikacji" lub "obejść się bez widżetu", uszanuj to i odpowiadaj tylko tekstem, bez wywołania tools.
- Jeśli zapytanie nie jest związane z prezentami ani zakupami, odpowiedz jak bazowy ChatGPT i nie używaj narzędzi GiftGenius.

W efekcie ta konstrukcja bardzo przypomina przykład z materiałów dodatkowych do tematu: są sekcje „Rola”, „Zadania”, „Robić/nie robić”, „Praca z narzędziami”, „Bezpieczeństwo”, „Ogólne zasady”.

W kodzie Next.js możecie to ująć jako osobny moduł:

// app/config/giftGeniusPrompt.ts
export const giftGeniusSystemPrompt = `
Rola:
- Jesteś GiftGenius, asystentem do doboru prezentów naszego serwisu.
...

Ogólne zasady:
- Jeśli zapytanie nie jest związane z prezentami, odpowiedz jak bazowy ChatGPT i nie używaj narzędzi GiftGenius.
`;

A następnie używać tej stałej w konfiguracji aplikacji (jak dokładnie — zależy od wersji Apps SDK, ale idea jest ta sama: ten tekst trafia do roli system przy inicjalizacji dialogu aplikacji).

8. Kontekst dynamiczny w system‑prompt

Czasem system‑prompt warto trochę „podmieszać” dynamiką: aktualna data, lokalizacja, typ użytkownika (nowy/stary klient), status subskrypcji itp.

Na przykład, jeśli wasz katalog prezentów i ceny różnią się dla regionów, możecie przekazywać do system‑prompt aktualny region:

export function buildSystemPrompt(locale: string) {
  return `
Rola:
- Jesteś GiftGenius, asystentem do doboru prezentów dla regionu ${locale}.

Granice:
- Używaj tylko tych prezentów i cen, które są dostępne w regionie ${locale}.
...
`;
}

Apps SDK przy inicjalizacji aplikacji może podsunąć wam _meta["openai/locale"], a wy na tej podstawie wygenerujecie właściwy wariant promptu. Szczegółami lokalizacji zajmiemy się później, ale już teraz warto widzieć, że system‑prompt nie musi być zawsze statyczny.

Najważniejsze — nie zamieniać go w „spaghetti” z warunków. Jeśli logika staje się zbyt złożona, lepiej rozdzielić aplikacje albo wynieść warunki do tools (np. aby serwer MCP sam wybierał właściwe źródło danych według locale), a w system‑prompt zostawić tylko reguły wysokiego poziomu.

9. Jak system‑prompt jest powiązany z opisem tools i instrukcjami UX

Ta lekcja skupia się na system‑prompt, ale w prawdziwej aplikacji nie istnieje on w izolacji. Są jeszcze opisy narzędzi (description, inputSchema) oraz przykłady follow‑upów, którymi zajmiemy się w kolejnych tematach. Wszystko razem tworzy spójny system instrukcji.

Zarządzanie wywołaniem tools:

  • system‑prompt ustala ogólną filozofię: „narzędzia tylko do danych faktycznych”, „nie wymyślać prezentów”, „nie kupować bez potwierdzenia”;
  • opisy tools doprecyzowują, co dokładnie robi recommend_gifts, jakie parametry są potrzebne i kiedy należy go wywołać;
  • follow‑upy nadają styl dialogu po wywołaniu narzędzia: jak uczciwie powiedzieć, że nic nie znaleziono, jak zaproponować zmianę zapytania, jak podsumować wyniki.

Jeśli te trzy warstwy są spójne, model zachowuje się przewidywalnie:

  • wywołuje aplikację wtedy, gdy jest to naprawdę potrzebne;
  • nie halucynuje prezentów/produktów spoza bazy;
  • jasno wyjaśnia użytkownikowi, co się stało (znaleziono / nie znaleziono / potrzebne są dodatkowe informacje).

Jeśli nie — otrzymujecie chaotyczne zachowanie i długie sesje „magicznego strojenia promptu”, od których bardzo szybko wszyscy się męczą.

10. Typowe błędy przy pracy z system‑prompt ChatGPT App

Błąd nr 1: pisać system‑prompt „dla urody”, a nie jako kontrakt.
Bardzo często deweloperzy ograniczają się do ogólnych fraz w stylu „Pomagaj użytkownikowi rozwiązywać zadania jak potrafisz” i „Bądź przyjazny”. Od tego modelowi nie staje się jaśniejsze, kiedy wywoływać App, gdzie przebiegają granice odpowiedzialności, czy wolno wymyślać dane i co robić przy błędach narzędzia. W rezultacie połowa logiki rozmazuje się po stosach kodu i w głowie autora, zamiast być zapisana w jawnym kontrakcie.

Błąd nr 2: zbyt szeroka rola („pomagaj we wszystkim”).
Jeśli w sekcji roli piszecie „Jesteś asystentem, który pomaga użytkownikowi we wszystkich sprawach”, model zaczyna radośnie robić właśnie to i nie zawsze pamięta o waszej App. App staje się nieobowiązkową opcją, z której rzadko się korzysta, bo model uważa, że i tak sobie poradzi. Lepiej jasno wskazać niszę: dobór prezentów, praca z katalogiem prezentów, pomoc w konkretnym domenie.

Błąd nr 3: brak zasad, kiedy wywoływać narzędzia.
Sformułowania typu „używaj narzędzi w miarę potrzeby” są zbyt mgliste. Model może albo całkowicie ignorować tools, albo wywoływać je nawet tam, gdzie można by odpowiedzieć „z głowy”. Trzeba wyraźnie rozdzielać scenariusze: dane faktyczne → narzędzie, tło/wyjaśnienia → odpowiedź samego modelu, jawna odmowa użytkownika wobec App → tylko tekst.

Błąd nr 4: próba leczenia halucynacji jednym zdaniem „nie wymyślaj”.
Samo „nie halucynuj” niewiele pomaga. Ważne jest jasno opisać, czego dokładnie nie wolno wymyślać (produkty/prezenty spoza katalogu, pozycje bez ID, nieistniejące zniżki) i co robić przy pustym wyniku (uczciwie powiedzieć, że nic nie znaleziono). Bez tego model i tak będzie starał się „dogodzić” i generować wymyślone opcje. Potrzebny jest pełny zestaw: globalny zakaz w system‑prompt, ograniczenia w opisach tools i szablony odpowiedzi na przypadki „nic nie ma”.

Błąd nr 5: ignorowanie bezpieczeństwa i zgody użytkownika.
Jeśli w system‑prompt nie ma mowy o tym, że zakup, rezerwacja czy zmiana danych osobowych wymagają wyraźnego potwierdzenia, model może, kierując się „dobrym zamiarem”, wywołać narzędzie sam. Z punktu widzenia UX to katastrofa. Zawsze zapisujcie, że wszelkie działania związane z finansami lub kontem wykonuje się tylko po wyraźnej zgodzie na czacie.

Błąd nr 6: nie branie pod uwagę istnienia innych App i narzędzi.
W świecie, gdzie jedno konto ma kilka App i mnóstwo tools, nie można zakładać, że model „sam się domyśli”, której aplikacji użyć. Jeśli system‑prompt nie utrwala, że to konkretnie App do wyboru prezentów i tylko do tego, model może nieprzewidywalnie przełączać się między różnymi App albo próbować używać narzędzi niezgodnie z przeznaczeniem.

Błąd nr 7: poprawianie system‑prompt „na gorąco” bez wersjonowania i testów.
Kusi wejść w config, zmienić jedną linijkę i wierzyć, że „jest tylko lepiej”. W praktyce każda poprawka promptu może psuć zachowanie innych scenariuszy. Jeśli nie przechowujecie system‑prompt w repozytorium, nie oglądacie diffów i nie przepuszczacie zestawu testowych zapytań (golden prompt set — do niego dojdziemy w tym module), będziecie łapać regresje tygodniami.

Komentarze
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION