CodeGym /Kursy /ChatGPT Apps /Wprowadzenie do agentów: role, cykl run, determinizm, ide...

Wprowadzenie do agentów: role, cykl run, determinizm, idempotencja

ChatGPT Apps
Poziom 12 , Lekcja 0
Dostępny

1. Czym właściwie jest agent i po co jest potrzebny

Agenci nie są obowiązkową częścią ChatGPT App: można stworzyć dowolnie wiele świetnych aplikacji bez używania agentów LLM. Mam jednak trzy mocne powody, by o nich opowiedzieć.

Agenci to świetny sposób na dodanie inteligencji do backendu twojej aplikacji. Inteligentny dobór prezentów, analiza tekstowych próśb użytkownika. Złożone scenariusze wyszukiwania, analizy, przetwarzania i podsumowania — to wszystko bardzo łatwo zrobić przy pomocy agentów LLM.

ChatGPT wydał własne AgentsSDK w TS i Pythonie. Jest bardzo dobre. Orkiestracja agentów działa od razu po wyjęciu z pudełka. Nad jednym złożonym zadaniem może pracować nie jeden agent, lecz cały zespół. To bardzo obiecujący kierunek.

Jest też cel edukacyjny. ChatGPT wywołuje mcp-tools dokładnie tak samo, jak agenci LLM wywołują swoje tools. Gdy tylko zrozumiesz, jak działają agenci LLM, będzie ci jasne, jak np. zrobić maszynę stanów po stronie modelu w aplikacji. Dodatkowo, studiowanie AgentsSDK daje wyobrażenie o tym, jak w przyszłości będzie działać ChatGPT SDK.

Zaczynajmy.

Czym jest agent LLM

Jeśli ChatGPT App to ładny i wygodny „front” twojej usługi wewnątrz ChatGPT, a serwer MCP to „silnik” z narzędziami i logiką biznesową, to agent jest czymś w rodzaju sprytnego dyspozytora, który potrafi:

  • przeczytać cel;
  • samodzielnie zdecydować, jakie narzędzia wywołać i w jakiej kolejności;
  • w razie potrzeby poprosić o dodatkowe dane;
  • powtórzyć kroki, jeśli wystąpiły błędy;
  • dojść do sensownego, końcowego wyniku.

W sformułowaniu zbliżonym do oficjalnej dokumentacji Agents SDK agent to program. Mając dostęp do LLM i zestawu narzędzi, potrafi samodzielnie planować kroki w celu osiągnięcia celu i realizować je poprzez wywołania narzędzi (tool‑calls).

Jeśli porównać to z tym, co już masz:

  • W zwykłym ChatGPT App model ChatGPT orkiestruje wywołania twoich narzędzi MCP bezpośrednio.
  • Agent LLM na backendzie również ma opis zadania, zestaw narzędzi i sam decyduje, jakie tools wywołać, ile kroków wykonać, kiedy się zatrzymać i jaki wynik zwrócić.

W kontekście naszego GiftGenius może to wyglądać tak:

  • Aplikacja bez agenta: model bezpośrednio wywołuje searchGifts, potem filterByBudget, potem getDetails — za każdym razem „myśląc od nowa”;
  • Aplikacja z agentem: ChatGPT wywołuje mcp‑tool, a backend stawia agentowi zadanie: „Znajdź top‑5 prezentów dla takiego‑a‑takiego profilu”. Agent wykonuje kilka kroków: zbiera dodatkowe informacje, wywołuje różne narzędzia wyszukiwania, filtruje, sortuje, buduje finalne karty i zwraca już gotową ustrukturyzowaną odpowiedź.

ChatGPT i agent LLM na backendzie — to jak dyrektor firmy i pracownik. ChatGPT ma znacznie większą swobodę: komunikuje się z użytkownikiem i decyduje, jakie strategiczne zadania uruchomić (wywołać mcp tools). Agent LLM działa tylko na backendzie, nie wchodzi w interakcję z użytkownikiem, ale też „myśli” i może wywoływać swoje tool. Taki sobie ChatGPT w wersji „lite”.

2. Z czego składa się agent: LLM, instrukcje, narzędzia i stan

Wygodnie jest myśleć o agencie jako o kilku poziomach.

Po pierwsze, pod maską mamy wciąż to samo LLM. Może to być GPT‑5.1 lub inny model używany przez Agents SDK. Generuje tekst, planuje kroki, wybiera narzędzia — krótko mówiąc, zajmuje się „myśleniem”, ale w twoim kontekście orkiestracji.

Po drugie, ponad modelem są instrukcje. To systemowy prompt agenta, który definiuje jego rolę, granice, styl komunikacji, sposoby używania narzędzi. Robiłeś już coś podobnego dla ChatGPT App, ale teraz dotyczy to osobnego agenta.

Po trzecie, jest zestaw narzędzi agenta. Mogą to być:

  • funkcje w TypeScript (klasyczny function calling);
  • narzędzia HTTP/REST;
  • nakładki nad twoimi MCP‑tools, aby agent mógł korzystać z tego samego backendu co ChatGPT App;
  • wbudowane „hosted” narzędzia OpenAI (np. web‑search, jeśli je podłączasz).

I wreszcie, są zasady pracy ze stanem i krokami: jak przechowywany jest stan sesji (session state), jak zapisywane są wyniki pośrednie, jak ograniczane są pętle. Głębiej porozmawiamy o tym w kolejnym wykładzie o pamięci i stanie, ale już teraz warto mieć w głowie, że agent to nie „jednorazowe zapytanie”, lecz potencjalnie długi proces z zapisywaniem postępów.

Jeśli spojrzysz na to oczami dewelopera TypeScript, to mentalnie powstaje obiekt mniej więcej takiego typu (pseudokod zbliżony do Agents SDK TS):

const giftAgent = new Agent({
  model: "gpt-5.1",
  systemPrompt: giftAgentPrompt,
  tools: { searchGifts, filterGifts, checkoutDraft },
  // tutaj — ustawienia pamięci, limitów kroków itd.
});

Nie zagłębiamy się teraz w dokładne API, ważny jest obraz: model, instrukcje, narzędzia i ustawienia zachowania w jednym miejscu.

3. Role wiadomości: system / user / assistant / tool w świecie agenta

Znasz już klasyczne role system, user, assistant i tool z Chat Completions. W Agents SDK pozostają one, ale zyskują nieco bardziej aplikacyjny sens.

Rola system definiuje osobowość i misję agenta. Na przykład, dla agenta GiftGenius może to być: „Jesteś agentem doboru prezentów. Twoim zadaniem jest w minimalnej liczbie kroków dobrać 3–7 trafnych propozycji prezentów na podstawie profilu odbiorcy i budżetu, a następnie przygotować ustrukturyzowany JSON dla widżetu”. Tutaj też opisujesz ograniczenia: czego nie powinien robić (np. nie wykonywać prawdziwych zakupów bez osobnego kroku) i jak należy pracować z narzędziami.

Rola user w kontekście agenta to niekoniecznie „żywy człowiek”. Najczęściej to „zadanie” dla agenta: cel sformułowany przez twoje App, usługę lub innego agenta. Przykładowo, ChatGPT App może wywołać agenta z wiadomością user: „Dobierz 5 propozycji prezentów dla kolegi‑programisty, budżet 50 dolarów, okazja — urodziny”.

Rola assistant — to to, co „mówi” sam model wewnątrz agenta. Mogą się tu pojawiać zarówno rozważania pośrednie i plany, jak i odpowiedź końcowa. Twoim zadaniem jest tak skonfigurować systemowy prompt, aby te wiadomości były użyteczne i w razie potrzeby logowane.

Rola tool (lub jej odpowiedniki w konkretnym SDK) opisuje wyniki wywołań narzędzi: „przez MCP znaleziono 50 produktów”, „API zwróciło błąd timeoutu”, „DB zwróciła profil użytkownika”. Te wiadomości wraz z wiadomościami assistant tworzą historię cyklu run agenta.

Wygodnie zebrać to w małej tabeli:

Rola Kto mówi Przykład w kontekście GiftGenius
system
Ty (jako twórca agenta) „Jesteś agentem doboru prezentów…”
user
Wywołanie zewnętrzne (App, inny agent) „Dobierz 5 prezentów do 50 $…”
assistant
Model wewnątrz agenta „Plan: 1) dopytać o szczegóły…”
tool
Wynik wywołanego narzędzia „searchGifts zwrócił 20 wariantów…”

Ta struktura jest ważna, bo właśnie na jej podstawie buduje się cykl run — główny bohater dzisiejszego wykładu.

4. Jak LLM wywołuje funkcje w twoim backendzie

Gdy przywykniesz do trybu „pytanie–odpowiedź”, może się wydawać, że LLM działa według prostej schemy: przyszedł tekst → model odpowiedział tekstem. W rzeczywistości pod maską wszystko jest nieco bardziej złożone i właśnie dlatego działa function calling.

Model nie otrzymuje jednego pytania, tylko listę wiadomości — historię dialogu. Leżą tam już wszystkie wcześniejsze wypowiedzi: instrukcje systemowe („kim jesteś i co wolno/nie wolno”), twoje wiadomości, poprzednie odpowiedzi modelu, wyniki narzędzi. Na każdym kroku model patrzy na całą tę taśmę jak na log czatu i decyduje: „Jaką następną wiadomość trzeba dodać na koniec?”.

To kluczowa myśl: LLM zawsze robi jeden krok — dopisuje następną wiadomość na końcu historii. Nie „zmienia przeszłości” ani nie edytuje starych wiadomości, tylko kontynuuje listę. Piszesz pytanie — model odpowiada. Dopisujesz drugie pytanie — model znów odpowiada, ale z uwzględnieniem całej historii dialogu (wszystkich wiadomości).

Function calling działa na tej samej zasadzie. Zamiast bezpośrednio „uruchamiać funkcję”, model robi następujące:

  • widzi listę dostępnych narzędzi/tools i ich opisy razem z historią dialogu;
  • decyduje: „Teraz sensowniej nie tylko odpowiedzieć tekstem, ale najpierw wywołać takie‑a‑takie narzędzie”;
  • i jako następną wiadomość dopisuje do historii nie zwykłą tekstową odpowiedź, lecz specjalną wiadomość w formacie „chcę wywołać taki‑a‑taki tool z takimi‑a‑takimi argumentami”.

Dalej już nie model, lecz twój backend czyta tę nową wiadomość na końcu historii, rozumie, że to żądanie wywołania funkcji, i wywołuje potrzebne narzędzie. Następnie dodaje do historii jeszcze jedną wiadomość — z wynikiem narzędzia, i znowu wysyła pełną listę wiadomości modelowi. Model ponownie patrzy na całą taśmę i dopisuje kolejny krok: albo kolejne wywołanie, albo już finalną, zrozumiałą dla człowieka odpowiedź.

Czyli:

  • dla zwykłego Q&A: „następna wiadomość” = odpowiedź tekstowa;
  • dla function calling: „następna wiadomość” = instrukcja wywołania funkcji lub odpowiedź po użyciu funkcji.

Nie ma żadnej osobnej magicznej „komendy wywołania funkcji” — to po prostu szczególny rodzaj następnej wiadomości, którą model dodaje na końcu łańcucha.

Model nie wywołuje funkcji twojego backendu przez publiczne API. On po prostu „pisze na czacie”, że chce wywołać funkcję z parametrami. A już twój backend wywołuje lokalną funkcję, a jej odpowiedź dopisuje do czatu. I wszystko zaczyna się od nowa.

5. Cykl run agenta: jak „myśli” krok po kroku

W istocie agent LLM to pewien obiekt/algorytm na twoim serwerze, który uruchamia agentowy cykl run — to rozszerzony cykl „pytanie → pomyśl → być może wykonaj działanie → znów pomyśl → … → odpowiedź końcowa”. W dokumentacji OpenAI bywa to nazywane agent loop albo wzorcem ReAct (Reason + Act + Observe).

Na poziomie koncepcyjnym jeden run agenta wygląda tak:

  1. Agent otrzymuje wejście: instrukcje systemowe, zadanie (wiadomość user), ewentualnie — bieżący stan.
  2. Model generuje krok: albo odpowiedź tekstową, albo plan i decyzję o wywołaniu jednego lub kilku narzędzi.
  3. Jeśli model wybrał tool‑call, agent wywołuje odpowiadające narzędzie w kodzie (może to być lokalna funkcja, MCP‑tool, żądanie HTTP, dostęp do DB itd.).
  4. Wyniki narzędzi dodawane są do historii jako wiadomości tool.
  5. Cykl wraca do modelu z nowym kontekstem. Model decyduje, co dalej: kontynuować planowanie, wywołać inne narzędzie czy zakończyć zadanie odpowiedzią końcową.
  6. Gdy model jawnie lub według warunków zatrzymania kończy run, agent zwraca wynik końcowy stronie wywołującej.

W formie małego diagramu można to pokazać tak:

flowchart TD
    A[Start run: cel + system] --> B[Wywołanie modelu]
    B --> C{Model chce
odpowiedzieć tekstem
czy wywołać tool?} C --> D["Odpowiedź tekstowa
(assistant)"] D --> E{Zadanie zakończone?} E -->|Tak| F[Wynik końcowy] E -->|Nie| B C --> G["Tool-call
(opis wywołania)"] G --> H[Wywołanie funkcji / MCP / HTTP] H --> I["Wynik narzędzia
(tool message)"] I --> B

Jeśli przełożyć to na uproszczony pseudokod TypeScript (daleki od realnego API, ale logicznie poprawny), wyjdzie coś w rodzaju:

async function runAgent(goal: string) {
  let context = buildInitialContext(goal);

  while (!isFinished(context)) {
    const decision = await callLLM(context); // krok agenta

    if (decision.type === "tool_call") {		// wywołać funkcję?
      const toolResult = await callTool(decision.tool, decision.args);	// wywołujemy lokalną funkcję
      context = appendToolResult(context, toolResult);		// dodajemy wynik na koniec listy
    } else {
      context = appendAssistantMessage(context, decision.message); 
    }

    enforceLimits(context); // limity kroków/czasu/pętli
  }

  return extractFinalResult(context);
}

Agents SDK bierze na siebie dużą część tej rutyny: przechowywanie historii, marshalling tool‑calls, logikę powtórzeń itd. Tobie zostaje konfiguracja i implementacja samych narzędzi.

Run vs step

Warto rozróżniać dwa pojęcia:

  • run — jedno uruchomienie agenta dla określonego celu: „dobierz prezenty na tę okazję”;
  • step — jeden krok cyklu run: konkretne wywołanie modelu, które może prowadzić do odpowiedzi tekstowej lub do tool‑call.

W monitoringu będziesz widzieć właśnie wiele kroków w ramach jednego run, a limity bezpieczeństwa i kosztów często ustala się albo „na run”, albo „na krok”.

Teraz, gdy wiadomo, jak agent żyje w ramach jednego runu i kroczy po cyklu run, zobaczmy, gdzie w ogóle ma sens to wszystko budować, a gdzie wystarczą proste tools.

5. Gdzie w GiftGenius agent jest potrzebny, a gdzie zbędny

Zanim rzucisz się pisać agenta do wszystkiego, warto zadać sobie szczere pytanie: „Czy on w ogóle jest tu potrzebny?”.

Dobry scenariusz dla agenta to zadanie wieloetapowe z rozgałęzieniami, powtórkami i logiką, której niewygodnie trzymać wyłącznie w promptach.

W GiftGenius taką pracą może być „kreator inteligentnego doboru prezentów”, który:

  • potrafi dopytać o ważne szczegóły (płeć odbiorcy, hobby, stopień bliskości);
  • może zwrócić się do kilku źródeł produktów (różni dostawcy przez MCP‑tools);
  • filtruje i ranguje wyniki;
  • w przypadku błędów źródeł ponawia próbę lub idzie ścieżką awaryjną;
  • zwraca nie tylko listę tekstów, lecz ustrukturyzowaną listę kandydatów z objaśnieniami i linkami do SKU z feedu produktowego.

Tutaj agent będzie naprawdę użyteczny jako „orkiestrator”, zwłaszcza jeśli później zechcesz dodać do tego scenariusz voice/Realtime lub złożoną komercję (ACP).

A dla prostego wywołania getGiftDetails(giftId) agent nie jest potrzebny: zwykły MCP‑tool, wywoływany bezpośrednio z ChatGPT, w pełni pokrywa ten przypadek. To samo z banalnymi „jednoetapowymi” scenariuszami w rodzaju „opisz ten prezent na podstawie tekstu karty produktu”.

Zdrowe podejście jest takie: jeśli można opisać scenariusz jako „jedno sensowne narzędzie (tool)” — najpewniej agenta nie trzeba. Jeśli jednak zaczynasz wyraźnie rozpisywać wieloetapowy workflow z kontrolami i ponowieniami, z dużym prawdopodobieństwem agent cię ucieszy.

6. Determinizm: jak uczynić zachowanie agenta przewidywalnym

Determinizm w świecie agentów LLM to rzecz podstępna. Teoretycznie, przy identycznym wejściu i tych samych ustawieniach chcesz otrzymywać ten sam plan działań i tę samą sekwencję tool‑calls. W praktyce model pozostaje stochastyczny, ale masz kilka dźwigni sterowania przewidywalnością.

Po pierwsze, klasyka: temperatura i inne parametry generowania. Im niższa temperatura, tym mniej „kreatywności” i tym więcej „posłuszeństwa” modelu. Dla agenta doboru prezentów najpewniej zechcesz mieć nie zerowy, ale i nie zbyt wysoki poziom swobody, inaczej model będzie co rano wymyślał nowy sposób wywołania tego samego narzędzia.

Po drugie, jasne instrukcje systemowe. Jeśli nieprecyzyjnie opisujesz zachowanie typu „możesz wołać różne narzędzia i robić co chcesz”, nie dziw się, że agent będzie to skakał po API, to próbował odpowiadać „z sufitu”. Dużo lepiej wyraźnie opisywać, kiedy dokładnie należy wywoływać narzędzie, jakie parametry są dopuszczalne, jak interpretować błędy i w jakich przypadkach kończyć zadanie.

Na przykład systemowy prompt dla agenta GiftGenius może zawierać fragment:

Jeśli nie masz pełnego profilu odbiorcy (wiek, płeć, okazja, przybliżony budżet),
najpierw zadaj pytania uściślające przez kanał dla użytkownika i poczekaj na odpowiedzi.
Dopiero potem wywołuj narzędzie search_gifts z uzupełnionym profilem.
Nie wymyślaj produktów z sufitu, zawsze opieraj się na wynikach narzędzi.

Takie wskazówki zmniejszają zmienność decyzji i czynią zachowanie bardziej deterministycznym.

Po trzecie, projekt samych narzędzi. Jeśli masz trzy narzędzia, które „mniej więcej to samo” wyszukują prezenty, model nieuchronnie będzie czasem wybierał jedno, czasem drugie. Lepiej zaprojektować narzędzia z wyraźnymi, niepokrywającymi się zakresami odpowiedzialności i opisać to w ich opisach.

Wreszcie, możesz używać guardrails — zasad i schematów, które sprawdzają działania agenta i wyniki modelu. W Agents SDK jest wbudowane wsparcie dla weryfikacji i ograniczeń, w tym co do struktury danych wyjściowych. Jeśli model próbuje wygenerować coś niezgodnego ze schemą, możesz delikatnie go poprawić lub nawet powtórzyć krok.

Mini‑przykład: ustalamy format wyniku

Załóżmy, że potrzebujesz, aby agent zawsze zwracał JSON z polem gifts, a w środku obiekty z id, title i score. Możesz:

  • opisać ten schemat na poziomie agenta;
  • wskazać, że finalny output ma mu odpowiadać;
  • w przypadku naruszenia — powtórzyć krok lub zwrócić bezpieczny błąd.

Pseudokod:

const giftResultSchema = z.object({
  gifts: z.array(z.object({
    id: z.string(),
    title: z.string(),
    score: z.number().min(0).max(1),
  }))
});

// W konfiguracji agenta
const agent = new Agent({
  /* ... */
  outputSchema: giftResultSchema,
});

Gdy model spróbuje zwrócić coś dziwnego, runner zgłosi błąd walidacji, a ty będziesz mógł albo poprosić model ponownie, albo zalogować incydent.

7. Idempotencja: dlaczego agent może wywołać twoje API dwa razy

Jeśli determinizm to „ten sam plan przy tych samych wejściach”, to idempotencja dotyczy bezpieczeństwa powtórek. W kontekście agentów jest krytyczna z dwóch powodów.

Po pierwsze, dochodzi kolejny poziom ponowień: nie tylko klienci HTTP i load balancery, ale i sam agent może zdecydować, by powtórzyć wywołanie narzędzia, jeśli dostał błąd lub niepełny wynik. Po drugie, w realnych scenariuszach produkcyjnych dochodzą webhooki, kolejki, kanały strumieniowe — i możesz przypadkowo obsłużyć ten sam logiczny krok kilka razy.

Rozmawialiśmy już o idempotencji na poziomie MCP‑tools: nie wykonywać podwójnych płatności, nie tworzyć dwa razy tego samego zamówienia, używać idempotency keys w żądaniach. Teraz to samo, ale spotęgowane wieloetapową naturą agenta.

Wyobraźmy sobie, że w GiftGenius pojawiło się narzędzie create_checkout_session, które na podstawie listy wybranych prezentów tworzy draft checkoutu w ACP/Stripe. Jeśli agent zdecyduje się powtórzyć to wywołanie z powodu błędu sieci, bardzo nie chcesz mieć dwóch oddzielnych zamówień i dwóch obciążeń karty.

W takim razie trzeba:

  • wymyślać zewnętrzny idempotency key dla każdego logicznego działania (np. runId + stepIndex lub jawnie wygenerowany checkoutDraftId);
  • przekazywać go do twojego backendu/ACP‑endpointu;
  • po stronie backendu sprawdzać, czy nie obsłużyłeś już tego klucza, i zwracać zapisany wynik zamiast ponownie wykonywać działanie.

Pseudoprzykład w TypeScript:

async function createCheckoutDraft(runId: string, payload: DraftPayload) {
  const key = `gift-checkout-${runId}`;

  const existing = await findDraftByKey(key);
  if (existing) return existing;

  const draft = await stripe.checkout.sessions.create({
    /* ... */,
    idempotencyKey: key, // albo własna warstwa nad tym
  });

  await saveDraftWithKey(key, draft);
  return draft;
}

Teraz, nawet jeśli agent z jakiegoś powodu wywoła to narzędzie dwa razy z tym samym runId, twój kod pozostanie idempotentny: ten sam logiczny krok → ten sam faktyczny wynik.

„Najpierw sprawdź, potem wykonuj”

Drugi rozpowszechniony wzorzec idempotencji — najpierw sprawdzać stan, potem działać. Na przykład zanim utworzysz zamówienie, sprawdzić, czy nie istnieje już zamówienie z takim clientReferenceId albo tym samym zestawem parametrów. To szczególnie wygodne w długich workflow, gdzie agent może „zapomnieć”, że coś już zrobił w poprzednim kroku.

Safe‑mode/Fake‑mode

Na etapie rozwoju warto mieć „tryb bezpieczny” dla niebezpiecznych narzędzi: zamiast rzeczywistego działania tylko logują, co byłoby zrobione, i zwracają pseudo‑wynik. Dla agentów to wygodny sposób, by przepuścić cykl run w produkcyjnym środowisku bez ryzyka dla pieniędzy czy danych.

8. Mini‑praktyka: opisujemy agenta GiftGenius ludzkim językiem

Mówiliśmy już o cyklu run, determinizmie i idempotencji narzędzi. Oderwijmy się na chwilę od kodu i sprawdźmy, jak to składa się w żywy scenariusz.

Teraz warto zrobić małe ćwiczenie na papierze (albo w głowie), bez kodu.

Wyobraź sobie, że opisujesz prostego agenta:

  • system
    : jesteś pomocnikiem doboru prezentów; zawsze uściślasz ważne szczegóły, nie wymyślasz produktów z głowy, korzystasz tylko z wyników narzędzi.
  • user
    : chcę prezent dla kolegi do 50 $.

Opisz słowami, jakie kroki taki agent powinien wykonać.

Typowy scenariusz może wyglądać tak.

  1. Najpierw agent sprawdza, czy informacji jest dość. Jeśli nie, zadaje pytania uściślające: czym kolega się zajmuje (projektant, programista, menedżer), czy są jakieś tabu (alkohol, prezenty żartobliwe), czy są ograniczenia dotyczące dostawy. Odpowiedzi trafiają albo do stanu sesji, albo do parametrów wywołania narzędzia.
  2. Następnie agent wywołuje narzędzie search_gifts z uzupełnionym profilem: „kolega‑programista, budżet 50, kategoria — gadżety i biuro”. Narzędzie zwraca listę kandydatów z cenami, kategoriami i ID produktów.
  3. Dalej agent może wywołać dodatkowe narzędzie filter_gifts_by_constraints, jeśli okazało się, że części produktów nie można dostarczyć do potrzebnego regionu, albo przefiltrować ręcznie w swoim promcie. Po tym sortuje warianty według trafności i ceny, ewentualnie dodając własne komentarze („pasuje, jeśli kolega lubi kawę”, „dobry wariant dla pracy zdalnej”).
  4. Na koniec agent przygotowuje finalną ustrukturyzowaną odpowiedź dla ChatGPT App: listę 5–7 prezentów z krótkimi opisami, podpowiedziami użycia i linkami do Checkout (albo do następnego kroku — utworzenia draft checkoutu).

Gdzie są tu potrzebne tool‑calls? Oczywiście w wyszukiwaniu i filtracji produktów, w sprawdzaniu dostępności i w tworzeniu draft checkoutu. Jakie kroki powinny być idempotentne? W pierwszej kolejności wszystkie operacje związane z zamówieniami i pieniędzmi — tworzenie draft checkoutu, ewentualnie zapis historii w DB.

9. Typowe błędy przy pierwszych krokach z agentami

Błąd nr 1: agent jako „drugi ChatGPT bez ograniczeń”.
Czasem kusi, by po prostu dać modelowi kolejny prompt i nazwać to „agentem”. W rezultacie powstaje coś, co generuje dużo tekstu, chaotycznie wywołuje narzędzia i trudno poddaje się kontroli. Aby tego uniknąć, ważne jest jasno opisywać rolę agenta w system, ograniczać listę narzędzi i myśleć o nim jako o orkiestratorze z konkretną misją, a nie jako o „drugim wszechświecie generacji tekstu”.

Błąd nr 2: brak idempotencji w narzędziach.
Deweloperzy nierzadko przenoszą swoje stare HTTP‑handlery pod agenta „jak są”, nie uwzględniając, że teraz runner może automatycznie powtarzać wywołania. W przypadku płatności i zamówień może to prowadzić do bardzo przykrych konsekwencji. Właściwe podejście — od razu projektować narzędzia tak, aby ponowne wywołanie z tym samym logicznym kluczem nie prowadziło do powtórnego działania.

Błąd nr 3: zbyt kreatywne ustawienia modelu.
Wysoka temperatura świetnie nadaje się do wymyślania toastów i wierszy, ale dla agenta, który ma niezawodnie orkiestrwać procesy wieloetapowe, to droga do nieprzewidywalnego zachowania: model będzie za każdym razem wybierał inne narzędzia, generował różne plany i czasem w ogóle zapominał, że ma tools. Traktuj agentów jako byty „służbowe” i trzymaj je w bardziej rygorystycznym reżimie.

Błąd nr 4: narzędzie „na wszystkie przypadki”.
Czasem kusi, by zrobić jedno uniwersalne narzędzie w rodzaju execute_any_sql lub do_anything_with_orders, a potem oddać je w ręce agenta. W połączeniu z kreatywnością LLM to niemal gwarantowane zagrożenie bezpieczeństwa. Znacznie lepiej mieć kilka wąsko wyspecjalizowanych narzędzi z jasnymi kontraktami i uprawnieniami niż jedno „boskie” z pełnymi prawami do wszystkiego.

Błąd nr 5: brak jawnych kryteriów zakończenia run.
Jeśli agentowi nie wyjaśnisz, kiedy ma się zatrzymać, może zacząć wpadać w nieskończone lub pół‑nieskończone pętle: jeszcze raz sprawdzić wyniki, jeszcze raz dopytać użytkownika, jeszcze raz spróbować wywołać narzędzie przy tym samym błędzie. Często ujawnia się to dopiero pod obciążeniem, gdy jedna z zależności jest niestabilna. Właściwy sposób — ustawiać limity na liczbę kroków, czas run i liczbę powtórek przy takim samym błędzie, a także opisywać w system, że agent powinien „uczciwie się poddać”, gdy wyczerpał rozsądne opcje.

Błąd nr 6: przechowywanie wszystkiego naraz w stanie agenta.
Ponieważ Agents SDK upraszcza pracę ze stanem sesji, kusi, by wrzucać tam wszystko: duże dokumenty, surowe logi, wrażliwe dane. To rozdyma kontekst, zwiększa koszt i stwarza ryzyka bezpieczeństwa. Stan agenta powinien przechowywać tylko to, co naprawdę potrzebne do kontynuowania pracy; reszta — w DB, logach i innych warstwach, z uwzględnieniem prywatności.

Błąd nr 7: próba użycia agenta tam, gdzie wystarczy prosty MCP‑tool.
Czasem deweloperzy zaczynają od agenta, nawet jeśli zadanie to po prostu wywołać jedną funkcję i zwrócić wynik. To dodaje złożoność tam, gdzie nie jest potrzebna: pojawia się cykl run, stan, dodatkowe logi i potencjalne punkty awarii. Jeśli scenariusz mieści się w jednym tool‑call bez złożonego workflow, lepiej pozostawić go takim i podłączać agenta dopiero, gdy pojawia się realna wieloetapowość.

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