CodeGym /Kursy /ChatGPT Apps /Połączenie system‑prompt<...

Połączenie system‑prompt, opisów narzędzi i follow‑upów: walka z halucynacjami

ChatGPT Apps
Poziom 5 , Lekcja 2
Dostępny

1. Wprowadzenie

Czasem na kursach prompt‑engineeringu pokazują magiczne zaklęcie:

„Nie wymyślaj faktów i mów „nie wiem”, jeśli nie ma informacji.”

Niestety (albo na szczęście) dla ChatGPT App to nie działa jak srebrna kula. Powód jest prosty: model widzi nie tylko Twój system‑prompt, ale także:

  • listę narzędzi z ich opisami i schematami;
  • wyniki tych narzędzi;
  • historię dialogu, włącznie z wcześniejszymi follow‑upami.

Jeśli te trzy warstwy — system‑prompt, opisy narzędzi, wzorce follow‑up — przeczą sobie lub po prostu „wiszą w powietrzu”, model zaczyna zachowywać się jak klasyczny junior: pracowity, ale skłonny do zmyślania.

W kursie używamy idei „trójwarstwowej ochrony przed halucynacjami” (defense in depth):

  • poziom 1 — globalne zasady w system‑prompt;
  • poziom 2 — lokalne ograniczenia w definicji każdego narzędzia;
  • poziom 3 — obsługa błędów, pustych wyników i follow‑upów.

W tej lekcji zbierzemy wszystkie trzy warstwy w jeden, spójny kontrakt dla naszej aplikacji edukacyjnej GiftGenius.

Insight

W nowej architekturze ChatGPT Apps pole na system‑prompt zniknęło — formalnie już go nie ma. Nie oznacza to jednak, że trzeba rezygnować z globalnych instrukcji dla modelu. Jest obejście: dla ChatGPT opisy narzędzi są takim samym tekstem promptu jak klasyczny system. Właśnie przez nie można przywrócić pełnoprawny „firmware mózgu” aplikacji.

Utwórz narzędzie pomocnicze, np. about lub about_app. Nie jest przeznaczone do częstego wywoływania, ale jego description jest czytany przez model na równi z innymi narzędziami. Dlatego w opisie można umieścić cały system‑prompt. Najlepiej dodać go po krótkim technicznym opisie samego narzędzia. W efekcie model na starcie dostaje Twój system‑prompt w czystej postaci — i stosuje go do całego dalszego dialogu oraz wywołań tools.

Aby uprościć utrzymanie, polecam dodawać na początku system‑promptu wyraźną wersję, np. SYSTEM_PROMPT_VERSION: v3. Pozwala to ustalić, dlaczego model zachowuje się inaczej: od razu widać, na jakim zestawie instrukcji pracuje. Jeśli w produkcji pojawi się dziwne zachowanie, łatwo zrozumieć, czy błąd dotyczy starej wersji promptu czy nowej.

Przykład:

tool description 

### Global assistant behavior for the entire GiftGenius App (ver 3.01)
*(system-level guidelines, not user-facing text)*

system prompt text

2. Jakie halucynacje zdarzają się w ChatGPT App (na przykładzie katalogu prezentów)

Żeby wiedzieć, jak leczyć, warto nazwać chorobę. W kontekście katalogu (prezenty, produkty, taryfy) najczęściej spotykane są takie typy halucynacji.

Po pierwsze, zmyślone pozycje katalogu. Użytkownik prosi o „cyfrowy certyfikat na roczną subskrypcję dla konkretnej usługi” albo bardzo egzotyczny prezent, którego w Waszym feedzie nie ma, a model, chcąc być pomocny, radośnie wymyśla „Super Space Flight 3000 — lot w kosmos”, którego w bazie GiftGenius nigdy nie było.

Po drugie, zmyślone atrybuty. Prezent w katalogu jest, ale model „upiększa” rzeczywistość: zmienia cenę, typ prezentu (cyfrowy vs fizyczny), dostępność dostawy do kraju użytkownika czy termin ważności certyfikatu, bo tak „logiczniej” albo „lepiej brzmi”.

Po trzecie, halucynacyjne działania. Model pisze „już kupiłem ten prezent i wysłałem kod na Twój e‑mail, z karty pobrano $49”, chociaż Wasz backend GiftGenius nawet nie próbował nic kupować i żaden ACP/Stripe‑flow nie został uruchomiony.

Wreszcie jest przypadek łączony: narzędzie zwróciło pustą listę (brak prezentów pod takie filtry i budżet), a model, żeby nie zasmucać użytkownika, wymyśla parę „przykładowych opcji” i nie tłumaczy, że w katalogu ich nie ma.

Naszym celem jest doprowadzić do tego, aby w takich sytuacjach model:

  • uczciwie przyznawał, że w katalogu nie ma dokładnego dopasowania;
  • nie wymyślał nowych prezentów i nie zmieniał wartości pól;
  • wyjaśniał użytkownikowi, co dokładnie się stało, i proponował zrozumiały kolejny krok.

I robić to nie za pomocą „zaklęć” w przypadkowych miejscach, lecz poprzez spójny kontrakt system‑prompttoolsfollow‑ups.

3. Warstwa 1: wzmacniamy system‑prompt przeciw halucynacjom

Aby zatrzymać zmyślone prezenty, atrybuty i działania z poprzedniej sekcji, wzmocnimy najpierw górną warstwę — system‑prompt: nadamy modelowi ogólną „filozofię” zachowania w katalogu.

W pierwszej lekcji modułu napisaliście już bazowy system‑prompt w stylu: „Jesteś GiftGenius, pomagasz dobierać prezenty…” i ustaliliście granice odpowiedzialności asystenta oraz pracę z narzędziami.

Teraz dodamy tam jawne reguły antyhalucynacyjne.

Logika jest taka: system‑prompt określa ogólną filozofię zachowania. Nie zna szczegółów każdego narzędzia, ale może:

  • zakazać wymyślania prezentów / cen / dostępności poza katalogiem;
  • opisać zachowanie, jeśli narzędzia zwróciły pusty wynik lub błąd;
  • wymagać wyraźnego rozróżnienia między danymi z katalogu prezentów a wszelką „wiedzą ogólną modelu”.

Przykład fragmentu system‑prompt (stała TypeScript w naszym projekcie Next.js, np. config/systemPrompt.ts):

// config/systemPrompt.ts
export const SYSTEM_PROMPT = `
# Rola
Jesteś GiftGenius, asystentem doboru prezentów
na podstawie katalogu prezentów naszej aplikacji.

# Dane i ograniczenia
- Nie wymyślaj prezentów, których nie ma w katalogu lub w odpowiedziach narzędzi.
- Nie zmyślaj cen, typu prezentu (cyfrowy/fizyczny),
  regionów dostawy ani innych atrybutów.
- Jeśli narzędzie nie zwróciło potrzebnych danych albo wystąpił błąd,
  powiedz o tym uczciwie i nie próbuj zgadywać wartości.

# Praca z narzędziami
- Zawsze używaj narzędzi katalogu prezentów do wszelkich danych faktycznych:
  listy prezentów, cen, typów, dostępności, SKU.
- Jeśli narzędzie zwróciło pusty wynik, powiedz, że przy bieżących warunkach
  nie ma pasujących prezentów i zaproponuj poluzowanie filtrów
  (zmianę budżetu, kategorii, typu prezentu).
`;

Jest tu kilka ważnych elementów.

Po pierwsze, rozdzielamy „wiedzę ogólną modelu” i „dane aplikacji”. Model wciąż może wyjaśnić, czym różni się prezent cyfrowy od fizycznego albo jakie są typowe okazje, ale wszelkie konkretne prezenty, ceny i SKU muszą pochodzić wyłącznie z narzędzi GiftGenius.

Po drugie, jasno opisujemy, co robić w przypadku błędów/pustej odpowiedzi: nie milczeć, nie „kreatywować”, tylko uczciwie powiedzieć użytkownikowi, że nic się nie znalazło, i zaproponować zmianę parametrów.

Po trzecie, koncentrujemy się nie na abstrakcyjnym „nie halucynuj”, lecz na konkretnych typach zachowań powiązanych z naszym domenowym kontekstem (katalog prezentów i zakup cyfrowych/fizycznych SKU).

Ta warstwa sama w sobie bardzo pomaga, ale łatwo ją „przykryć” nieuważnym opisem narzędzia. Przejdźmy do opisów narzędzi.

4. Warstwa 2: opisy narzędzi (tool descriptions) i schematy jako formalna część kontraktu

Model decyduje, kiedy i jak wywołać Twoje narzędzie, przede wszystkim na podstawie:

  • nazwy narzędzia;
  • description narzędzia;
  • inputSchema / outputSchema (JSON Schema opisujący pola).

Czyli opis narzędzia (tool description) to nie dokumentacja „dla ludzi”, tylko kolejna część promptu, tylko że sformalizowana. I wiele halucynacji rodzi się właśnie tutaj.

Wyobraźmy sobie nasze narzędzie recommend_gifts, które backend realizuje jako dobór prezentów z katalogu GiftGenius.

Zły wariant opisu mógłby wyglądać tak:

// ŹLE: zbyt ogólne
const recommendGiftsTool = {
  name: "recommend_gifts",
  description: "Dobiera prezenty dla użytkownika",
  inputSchema: {
    type: "object",
    properties: {
      profile: { type: "string" }
    }
  }
};

Formalnie wszystko poprawne, ale model z tego nie rozumie:

  • gdzie są granice narzędzia;
  • co robić, jeśli nie znaleziono prezentów;
  • że nie wolno wymyślać prezentów i cen spoza katalogu.

Dobry opis narzędzia robi kilka rzeczy naraz: jasno definiuje domenę, wyjaśnia, kiedy wywoływać narzędzie, i twardo wskazuje, że nie wolno zmyślać wyników.

Przykład (zaadaptowany do kontraktu GiftGenius z segments, budget, locale, occasion):

// config/tools.ts
export const recommendGiftsTool = {
  name: "recommend_gifts",
  description: `
Dobór prezentów w katalogu GiftGenius.

Używaj tego narzędzia, gdy potrzebujesz uzyskać listę realnych prezentów
według segmentów profilu obdarowywanego, budżetu, lokalizacji i okazji.
Narzędzie zwraca tylko te prezenty, które faktycznie istnieją
w katalogu GiftGenius.

NIE wymyślaj prezentów i ich atrybutów poza wynikami tego narzędzia.
Jeśli narzędzie zwróciło pustą listę, nie wymyślaj alternatyw,
tylko przejdź do dialogu: zaproponuj zmianę budżetu, typu prezentu,
okazji lub innych parametrów.
  `.trim(),
  inputSchema: {
    type: "object",
    properties: {
      segments: {
        type: "array",
        description:
          "Segmenty profilu obdarowywanego, np. ['tech', 'fitness'].",
        items: { type: "string" }
      },
      budget: {
        type: "object",
        description: "Przedział budżetu na prezent.",
        properties: {
          min: {
            type: "number",
            description: "Kwota minimalna, nieujemna.",
            minimum: 0
          },
          max: {
            type: "number",
            description: "Kwota maksymalna, większa od 0.",
            exclusiveMinimum: 0
          },
          currency: {
            type: "string",
            description: "Trzyliterowy kod waluty, np. 'USD' lub 'RUB'.",
            minLength: 3,
            maxLength: 3
          }
        },
        required: ["min", "max", "currency"]
      },
      locale: {
        type: "string",
        description:
          "Lokalizacja użytkownika (format język/region), np. 'ru-RU' lub 'en-US'.",
        minLength: 2
      },
      occasion: {
        type: "string",
        description:
          "Okazja na prezent: np. 'birthday', 'anniversary', 'new_year'."
      }
    },
    required: ["segments", "budget", "locale", "occasion"]
  }
};

Tutaj description robi kilka pożytecznych rzeczy.

Wyraźnie mówi, że narzędzie działa tylko na katalogu prezentów GiftGenius i że wszelkie konkretne prezenty i ceny muszą pochodzić wyłącznie z jego wyniku.

Wyjaśnia, kiedy używać narzędzia: gdy potrzeba listy konkretnych prezentów pod parametry obdarowywanego, a nie ogólnej teorii o prezentach.

Ustala zachowanie przy pustym wyniku: nie wymyślać, lecz przejść do dialogu (co później sformalizujemy we follow‑upach).

A inputSchema pomaga modelowi bardziej niezawodnie wydobyć encje z prośby użytkownika: segmenty, budżet, lokalizację i okazję. Określenie jawnej struktury i ograniczeń dla pól (min, max, currency o stałej długości) również zmniejsza prawdopodobieństwo dziwnych kombinacji i błędów w parsowaniu.

Można dodać i drugą stronę — nie tylko „kiedy wywoływać”, ale też kiedy nie wywoływać. Na przykład, jeśli zapytanie jest wyraźnie teoretyczne:

description: `
...
Nie używaj tego narzędzia, jeśli użytkownik zadaje ogólne pytanie
o prezenty bez prośby o dobór dla konkretnej osoby
(np. "jakie prezenty są ogólnie popularne na Nowy Rok").
W takich przypadkach odpowiadaj sam na czacie.
`.trim()

W ten sposób synchronizujesz opis narzędzia z zasadami z system‑prompt dotyczącymi zapytań „teoretycznych” vs „praktycznych”.

5. Warstwa 3: follow‑upy jako warstwa UX i safety

Nawet jeśli system‑prompt i opisy narzędzi są napisane idealnie, życie nie jest idealne:

  • backend może zwrócić błąd;
  • katalog — pustą listę;
  • wyniki — niejednoznaczne lub zbyt liczne.

Jeśli nie opiszemy, co powiedzieć po wywołaniu narzędzia, model będzie improwizować: czasem dobrze, czasem — z wymyślonymi faktami.

W lekcji 2 widzieliście już podstawowe instrukcje UX: jak model ogłasza uruchomienie App, jak kończy scenariusz i co mówi użytkownikowi „na wyjściu”. Teraz dodamy do tego wzorce follow‑upów, które ograniczają halucynacje.

Te wzorce zwykle opisuje się bezpośrednio w system‑prompt w osobnym bloku, np. „Dialog po pracy z narzędziami”.

Przykład fragmentu:

// kontynuacja SYSTEM_PROMPT
export const SYSTEM_PROMPT = `
# ... poprzednie sekcje ...

# Dialog po pracy z narzędziami

- Jeśli narzędzie doboru prezentów zwróciło pustą listę:
  1) uczciwie powiedz, że przy bieżących filtrach nie znaleziono pasujących prezentów;
  2) zaproponuj użytkownikowi zmianę 1–2 kluczowych parametrów
     (budżet, typ prezentu, zainteresowania obdarowywanego, okazja).

- Jeśli narzędzie zwróciło zbyt wiele opcji:
  1) wybierz 3–7 najbardziej trafnych;
  2) powiedz wprost, według jakich kryteriów je wybrałeś
     (dopasowanie do zainteresowań, mieszczą się w budżecie, ocena).

- Jeśli wystąpił błąd narzędzia:
  1) nie zmyślaj danych;
  2) powiedz, że wystąpił błąd techniczny i zaproponuj
     spróbować później lub uprościć zapytanie.
`.trim();

W ten sposób „wszywamy” w model przykładowe wypowiedzi follow‑up. Będzie je formułował własnymi słowami, ale z zadaną strukturą:

  • konstatacja faktu (pusto / za dużo / błąd);
  • uczciwe przyznanie ograniczenia;
  • delikatna propozycja kolejnego kroku.

Dla katalogu prezentów to kluczowe: zamiast „wszystko ok, oto trzy prezenty” w przypadku pustego wyniku model powie coś w stylu:

„Przy Twoich bieżących warunkach (cyfrowy prezent o kosmosie do $5 z dostawą tylko w USA) w naszym katalogu nic nie ma. Chcesz, żebym zwiększył budżet albo zaproponował inne kategorie?”

I zwróć uwagę: to już nie jest kod narzędzi, a właśnie instrukcje w promptach, które definiują oczekiwany UX.

6. Łączymy wszystko: ewolucja naszego GiftGenius

Spójrzmy na mini‑ewolucję naszej aplikacji i stopniowo zmniejszmy poziom halucynacji.

Wersja wyjściowa: gdzie wszystko się sypie

Załóżmy, że mieliśmy bardzo minimalistyczny system‑prompt:

export const SYSTEM_PROMPT = `
Jesteś asystentem doboru prezentów.
Pomagaj użytkownikowi znajdować odpowiednie pomysły.
`;

Narzędzie było opisane tak:

export const recommendGiftsTool = {
  name: "recommend_gifts",
  description: "Dobiera prezenty dla użytkownika",
  inputSchema: { type: "object" }
};

Instrukcji follow‑up brak.

Co będzie się działo w praktyce:

  • jeśli użytkownik poprosi o „cyfrowy prezent dla znajomego gracza do $10”, a w bazie pod takie filtry nic nie ma, model może:
    • albo w ogóle nie wywołać narzędzia i wymyślić prezenty z głowy;
    • albo wywołać narzędzie, dostać pustą listę, ale o tym nie powiedzieć i wymyślić opcje;
  • jeśli backend zwróci błąd, model może uznać, że „coś jednak powinno być” i zacząć zgadywać.

Tak powstaje klasyczna sytuacja: w czacie — piękna odpowiedź, w bazie — nic podobnego.

Nowa wersja system‑prompt

Przepiszmy system‑prompt, uwzględniając trzy warstwy ochrony. Część widzieliście wyżej, złóżmy go w całość:

// config/systemPrompt.ts
export const SYSTEM_PROMPT = `
# Rola
Jesteś GiftGenius, asystentem doboru prezentów
na podstawie katalogu prezentów naszej aplikacji.

# Zakres odpowiedzialności
- Twoim zadaniem jest pomóc użytkownikowi wybrać odpowiednie prezenty
  z katalogu, wyjaśniając plusy i minusy opcji.
- Nie składaj obietnic dotyczących faktycznego zakupu lub wysyłki prezentu —
  tylko pomagasz dobrać i porównać opcje.
  Zakupu i wydania kodu/linku dokonuje backend po wyraźnej zgodzie użytkownika.

# Dane i ograniczenia
- Nie wymyślaj prezentów, których nie ma w katalogu lub w odpowiedziach narzędzi.
- Nie zmyślaj cen, typu prezentu, dostępności ani regionów dostawy.
- Jeśli narzędzie nie zwróciło danych albo wystąpił błąd,
  nie próbuj zgadywać — powiedz o tym.

# Praca z narzędziami
- Używaj narzędzi katalogu prezentów (np. profile_to_segments,
  recommend_gifts, get_gift) do wszelkich danych faktycznych
  (lista prezentów, ceny, typy, SKU, opisy).
- Odpowiadaj sam (bez narzędzi), jeśli pytanie jest teoretyczne
  i nie wymaga doboru konkretnego prezentu.

# Dialog po pracy z narzędziami
- Przy pustym wyniku: wyjaśnij uczciwie, że nic nie znaleziono
  przy bieżących warunkach, i zaproponuj zmianę 1–2 parametrów.
- Przy dużej liczbie opcji: wybierz 3–7 najbardziej pasujących
  i wyjaśnij kryteria wyboru.
- Przy błędzie narzędzia: nie wymyślaj danych, przeproś za problem
  i zaproponuj spróbować ponownie albo uprościć zapytanie.
`.trim();

Teraz model dokładnie wie:

  • gdzie jest doradcą prezentowym, a gdzie „back‑office” (to u nas za narzędziami);
  • jakich danych dokładnie nie wolno zmyślać;
  • jak zachować się w typowych nieidealnych sytuacjach.

Nowy description i schemat dla recommend_gifts

Wzmocniliśmy system‑prompt, teraz dopracujmy narzędzie i zbierzmy finalną wersję jego opisu — na bazie idei z sekcji 4.

// config/tools.ts
export const recommendGiftsTool = {
  name: "recommend_gifts",
  description: `
Dobór prezentów w katalogu GiftGenius.

Używaj tego narzędzia, gdy potrzebujesz:
- uzyskać listę realnych prezentów z aktualnymi cenami, typem
  (digital/physical) i tagami;
- zawęzić wybór według segmentów zainteresowań, budżetu, lokalizacji i okazji.

NIE UŻYWAJ narzędzia:
- gdy użytkownik zadaje ogólne teoretyczne pytanie o prezenty
  bez prośby o dobór dla konkretnej osoby;
- do wymyślania prezentów, których nie ma w katalogu.

Jeśli wynik jest pusty, NIE wymyślaj prezentów samodzielnie,
tylko zwróć sterowanie do dialogu (stosuj się do instrukcji system‑prompt).
  `.trim(),
  inputSchema: {
    type: "object",
    properties: {
      segments: {
        type: "array",
        description:
          "Segmenty profilu obdarowywanego: np. 'tech', 'sport', 'books'.",
        items: { type: "string" }
      },
      budget: {
        type: "object",
        description:
          "Przedział budżetu na prezent w walucie użytkownika (min/max).",
        properties: {
          min: { type: "number", minimum: 0 },
          max: { type: "number", exclusiveMinimum: 0 },
          currency: {
            type: "string",
            minLength: 3,
            maxLength: 3,
            description: "Kod waluty ISO 4217, np. 'USD' lub 'RUB'."
          }
        },
        required: ["min", "max", "currency"]
      },
      locale: {
        type: "string",
        description: "Lokalizacja użytkownika, np. 'ru-RU' lub 'en-US'."
      },
      occasion: {
        type: "string",
        description:
          "Okazja na prezent: 'birthday', 'anniversary', 'new_year' itp."
      }
    },
    required: ["segments", "budget", "locale", "occasion"]
  }
};

Jest tu kilka niuansów.

Wyraźnie wiążemy zachowanie narzędzia z system‑prompt: fraza „stosuj się do instrukcji system‑prompt” przypomni modelowi, że „pusty wynik = uczciwa rozmowa, a nie kreatywność”.

Ustalamy warunki negatywne („nie używaj…”, „nie wymyślaj…”), co w praktyce bywa równie ważne, jak pozytywne opisy.

Robimy inputSchema semantycznie bogatym: sensowne opisy i ograniczenia pomagają modelowi poprawnie mapować zapytania na pola i mniej „mylą się” już przed wywołaniem narzędzia.

Wzorce follow‑up w kodzie widżetu i formacie odpowiedzi

Poza instrukcjami tekstowymi mamy jeszcze jedną dźwignię — sam format odpowiedzi narzędzia. Przez niego również możemy podpowiedzieć modelowi, co się wydarzyło, i zawęzić pole do fantazji.

Formalnie follow‑upy definiuje się tekstem w system‑prompt, ale w Waszym widżecie Next.js możecie dodatkowo znormalizować ToolOutput, aby ułatwić życie modelowi i zmniejszyć pole do domysłów.

Na przykład, uzgodnijmy, że backend dla recommend_gifts zawsze zwraca:

// typ odpowiedzi narzędzia po stronie backendu
export type RecommendGiftsResult = {
  items: Array<{
    id: string;
    title: string;
    price: number;
    currency: "USD" | "EUR" | "RUB";
    tags: ("digital" | "physical" | "education" | "fitness" | "tech")[];
  }>;
  // pole, które backend wypełnia, aby wprost zakomunikować modelowi, co zaszło
  status: "ok" | "empty" | "error";
  errorMessage?: string;
};

Widżet może to ładnie wyświetlać, a model — opierać się na status przy formułowaniu odpowiedzi. W Apps SDK często zwracacie ToolOutput do modelu jako obiekt JSON i on widzi to pole.

W system‑prompt można dodać mały blok:

# Interpretacja statusu narzędzia

- Jeśli status = "empty": patrz sekcja "Dialog po pracy z narzędziami" i
  nie wymyślaj prezentów.
- Jeśli status = "error": poinformuj o błędzie technicznym i nie próbuj zgadywać
  zawartości katalogu.

Tak, model i bez tego mógłby się domyślić, ale jawna instrukcja zmniejsza szansę „zgadywań” na podstawie domysłów.

7. Praktyka: dopracowujemy własny App

Żeby nie było wrażenia „ładnie na slajdach”, opiszmy konkretne ćwiczenie, które możecie zrobić z Waszą obecną aplikacją (w naszym przypadku — GiftGenius). Zrobimy mały refaktoring w trzech warstwach ochrony: system‑prompt, opisy narzędzi i obsługa wyników.

Po pierwsze, na poziomie system‑prompt, weźcie swój system‑prompt z pierwszej lekcji i znajdźcie w nim miejsce, gdzie opisany jest zakres odpowiedzialności i praca z narzędziami. Dodajcie tam:

  • zakaz wymyślania bytów poza katalogiem/bazą (prezentów, SKU, cen);
  • zasady na wypadek pustego wyniku i błędu narzędzia;
  • sekcję „Dialog po pracy z narzędziami” z 2–3 scenariuszami (pusto, za dużo, błąd).

Po drugie, na poziomie opisów narzędzi, otwórzcie opis kluczowego narzędzia (u Was to może być recommend_gifts, search_gifts, search_tariffs, calculate_quote, bez znaczenia). Przepiszcie description tak, aby:

  • wyraźnie mówił, że narzędzie działa tylko na Waszym źródle danych (katalog prezentów, taryf itp.);
  • wyjaśniał, kiedy jest potrzebne, a kiedy nie;
  • zawierał jasne ograniczenia negatywne: „nie wymyślaj…”, „nie używaj do…”.

Po trzecie, na poziomie struktury odpowiedzi narzędzia, jeśli w odpowiedzi narzędzia nie ma jeszcze pola, które jawnie opisuje status (status, resultType, hasMore) — dodajcie je w typie na backendzie i w ToolOutput. Następnie w system‑prompt dopiszcie, jak model ma interpretować ten status w dialogu.

Na koniec przepuśćcie kilka zapytań w Dev Mode, w tym takie, gdzie wyniki są z góry puste lub graniczne. Zwróćcie uwagę, czy model przestał wymyślać byty i na ile uczciwie informuje użytkownika o ograniczeniach.

W następnej lekcji sformalizujecie takie zapytania w tzw. golden prompt set i zamienicie to w powtarzalny artefakt testowy, ale na razie ważne jest, byście własnoręcznie poczuli różnicę.

8. Typowe błędy w walce z halucynacjami poprzez prompty i narzędzia

Błąd nr 1: jedno magiczne zdanie „nie halucynuj” w system‑prompt.
Programista pisze na końcu promptu: „Nie wymyślaj informacji” — i uważa zadanie za załatwione. W praktyce model dalej zmyśla, ponieważ opisy narzędzi i follow‑upy nie wyznaczają mu alternatywnego zachowania. Bez konkretnych reguł „co robić zamiast wymyślania” (przyznać pusty wynik, zaproponować zmianę filtrów, poinformować o błędzie) takie zdanie niewiele pomaga.

Błąd nr 2: sprzeczność między system‑prompt a description narzędzia.
W system‑prompt mówisz: „Nie wymyślaj prezentów, których nie ma w katalogu”, a w opisie narzędzia — „Dobiera odpowiednie prezenty, a jeśli nie znalazł — może zaproponować podobne opcje według własnego uznania”. W efekcie model miota się między dwoma źródłami prawdy, a wygrywa, niestety, bardziej konkretny (zwykle description narzędzia). Trzeba, aby obie warstwy mówiły to samo: jeśli podobne opcje są dopuszczalne, to też należy to sformalizować (i koniecznie wyjaśniać użytkownikowi, że to nie jest dokładne dopasowanie).

Błąd nr 3: zbyt mgliste opisy narzędzi.
Opis w stylu „Narzędzie, które pomaga użytkownikowi rozwiązywać zadania” nie daje modelowi prawie żadnych informacji o granicach narzędzia. W takim przypadku albo w ogóle nie będzie go używać, albo będzie wywoływać przy każdej okazji — a potem „dopowiadać” dane, gdy narzędzie zwróciło mało albo nic. Dobry description powinien być rozróżniający: mówić wprost, co robi narzędzie i kiedy nie należy go wywoływać.

Błąd nr 4: brak strategii dla pustych i błędnych wyników.
Programista troskliwie pisze backend, który zwraca { items: [], status: "empty" }, ale nigdzie nie wyjaśnia modelowi, co to znaczy. W rezultacie model widzi pustą tablicę i stwierdza: „no to chyba trzeba zaproponować coś z ogólnej wiedzy”. W system‑prompt brakuje sekcji wyjaśniającej, jak interpretować takie statusy i co mówić użytkownikowi. Dodanie kilku jasnych zasad dla pustego/błędnego wyniku daje ogromny wzrost jakości.

Błąd nr 5: próba „leczenia” halucynacji wyłącznie na poziomie kodu widżetu.
Bywa, że chce się wszystko przerzucić na frontend: „jeśli lista jest pusta — pokażę placeholder i nie pozwolę użytkownikowi zobaczyć tekstowej odpowiedzi modelu”. To może nieco złagodzić UX, ale sam model nadal wierzy w wymyślone byty i tak samo zachowuje się w kolejnych wypowiedziach. Właściwe podejście — najpierw zmienić instrukcje (system‑prompt, opisy narzędzi, follow‑upy), a dopiero potem uzupełniać to zabezpieczeniami w UI.

Błąd nr 6: ignorowanie wpływu metadanych i schematów na zachowanie modelu.
Niektórzy programiści postrzegają JSON Schema i opisy pól jako „tylko do formularza i walidacji”. W rzeczywistości, dla ChatGPT to ważna część promptu: na podstawie tych opisów model rozumie, jakie parametry musi wydobyć z prośby i jak wygląda poprawna odpowiedź. Słabe lub niespójne opisy pól (description, enum) zwiększają szansę błędów i pośrednio prowokują halucynacje.

Błąd nr 7: zbyt twarde zakazy bez alternatyw.
Zdarza się, że prompt zamienia się w listę „nie rób tego, nie rób tamtego”, ale nigdzie nie napisano, co robić w trudnych przypadkach. Na przykład zakazaliśmy wymyślania prezentów i ograniczyliśmy się tylko do katalogu, ale nic nie powiedzieliśmy o pytaniach teoretycznych. W efekcie model czasem po prostu odpowiada „nie wiem”, chociaż mógłby sensownie wyjaśnić ogólne zasady wyboru prezentów. Zawsze staraj się nie tylko zakazywać, ale też wskazywać dozwolone ścieżki: „jeśli nie możesz znaleźć prezentu w katalogu — powiedz o tym uczciwie i zaproponuj zmienić zapytanie” albo „jeśli pytanie jest teoretyczne — odpowiedz sam, bez narzędzi”.

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