Warunki zakończenia — kiedy agent ma przestać

przez Łukasz | sie 24, 2026

W poprzednim artykule w kodzie pętli siedziała linia obrot < 8. Wpisana na sztywno, bez komentarza, żeby przykład się nie rozjechał.

Ta jedna linia jest tematem całego tego tekstu. Bez niej pętla kręci się dopóty, dopóki model prosi o narzędzia — a model potrafi prosić bardzo długo, powtarzając w kółko ten sam krok.

Cztery rodzaje warunków

Naturalny. Model zwrócił odpowiedź zamiast prośby o wywołanie narzędzia. To jedyny warunek wynikający z zachowania modelu i jedyny, na którym nie wolno poprzestać.

Limit kroków. Twarda górna granica liczby obrotów. To zabezpieczenie, nie mechanizm sterowania — jeśli agent regularnie w nie uderza, problem leży gdzie indziej.

Limit budżetu. Przerwanie po przekroczeniu zadanej liczby tokenów albo kosztu. W praktyce ważniejszy od limitu kroków, bo obroty mają bardzo różną cenę: krok, w którym narzędzie zwróciło pięćdziesiąt rekordów, kosztuje wielokrotnie więcej niż krok z trzema polami. Dziesięć kroków może oznaczać złotówkę albo dwadzieścia.

Limit czasu. Przerwanie po przekroczeniu czasu wykonania. Istotne wszędzie tam, gdzie na odpowiedź czeka człowiek albo system nadrzędny z własnym limitem.

Sensowna pętla ma wszystkie cztery. Trzy ostatnie nie są nadmiarowe — każdy łapie inny rodzaj wykolejenia.

Dlaczego model nie zatrzyma się sam

To wynika wprost z tego, co ustaliliśmy w Anatomii LLM: model nie ma dostępu do pętli i nie wie, ile kroków już wykonano, poza tym, co widzi w kontekście. Nie ma też pojęcia „poddaję się” — zawsze istnieje rozkład prawdopodobieństwa, a w nim kolejna prośba o narzędzie jest zwykle sensowną kontynuacją.

Model nie jest więc wykonawcą, który wie, kiedy skończyć. Jest funkcją, którą trzeba przestać wywoływać.

Zapętlenie i jego trzy postacie

Zapętlenie to najczęstsza awaria produkcyjna agentów i przybiera trzy formy.

Powtórzenie. To samo narzędzie, te same argumenty, raz za razem. Najłatwiejsze do wykrycia.

Oscylacja. Agent wywołuje na przemian dwa narzędzia, wracając wciąż w to samo miejsce. Sprawdzanie dwóch kolejnych kroków tego nie złapie, bo żadne dwa pod rząd nie są identyczne.

Dryf argumentów. Model wywołuje wciąż to samo narzędzie, minimalnie zmieniając zapytanie — raz z myślnikiem, raz bez, raz po polsku, raz po angielsku. Wygląda na postęp, nim nie jest.

Skąd się to bierze

Przyczyna jest mechaniczna. Model nie pamięta poprzedniego kroku — widzi tylko kontekst. Jeśli wynik narzędzia nie wniósł informacji, którą model szukał, sytuacja przy kolejnym obrocie wygląda niemal identycznie jak poprzednio. A niemal identyczne wejście daje niemal identyczną decyzję.

Najczęstszym wyzwalaczem jest narzędzie zwracające pustkę. Puste [] albo null to dla modelu sygnał nierozróżnialny: czy nic nie znaleziono, czy zapytanie było złe? Skoro nie wie, próbuje jeszcze raz.

To jest najtańsza naprawa w całym temacie i zarazem najczęściej pomijana: niech narzędzie mówi, co się stało. Nie [], tylko „nie znaleziono zamówień dla klienta 4471 w zakresie dat 2026-01-01 do 2026-06-30″. Model dostaje wtedy informację, która realnie zmienia kontekst, i może wybrać inną drogę zamiast powtarzać.

Jak wykrywać

Prosta wersja porównuje nazwę narzędzia i argumenty dwóch kolejnych wywołań. Łapie powtórzenie i nie łapie reszty.

Wersja użyteczna trzyma okno kilku ostatnich wywołań i liczy, ile razy powtórzyła się dana sygnatura. To wychwytuje także oscylację. Na dryf argumentów pomaga normalizacja przed porównaniem — sprowadzenie do małych liter, usunięcie spacji, uporządkowanie kluczy.

Co zrobić po wykryciu

Trzy sensowne reakcje.

Przerwać i zwrócić to, co udało się ustalić. Najbezpieczniejsze. Częściowy wynik z informacją o przerwaniu jest lepszy niż wynik pełny, ale zmyślony.

Wstrzyknąć do kontekstu jawną informację. „Krok wyszukiwania po numerze klienta został już wykonany i zwrócił zero wyników. Spróbuj innego podejścia albo zakończ.” To zmienia sytuację, którą model widzi — a więc może zmienić decyzję.

Eskalować do człowieka. Sensowne przy zadaniach o wysokiej stawce, gdzie przerwanie jest kosztowne.

Czwarta reakcja, najczęściej spotykana w praktyce, to milczące kontynuowanie do wyczerpania limitu. Kosztuje i nie kończy się niczym.

Zatrzymanie się to nie sukces

To rozróżnienie ginie w większości implementacji, a jest ważne.

Pętla może zakończyć się na cztery różne sposoby: model odpowiedział, wyczerpano limit kroków, wyczerpano budżet, wykryto zapętlenie. Tylko pierwszy oznacza, że zadanie prawdopodobnie zostało wykonane.

Agent przerwany limitem też coś zwróci — najczęściej odpowiedź niepełną, sformułowaną równie pewnie jak każda inna. Jeśli pętla nie odróżnia zakończenia normalnego od wymuszonego i nie przekazuje tego dalej, system nadrzędny potraktuje jedno i drugie tak samo.

Zwracaj więc nie samą odpowiedź, lecz odpowiedź razem z powodem zakończenia, liczbą kroków i zużytym budżetem. Bez tego nie da się ani sensownie monitorować, ani ocenić przebiegu — a ewaluacja trajektorii potrzebuje dokładnie tych danych.

Jak to sprawdzić u siebie

Pętla z poprzedniego artykułu, rozbudowana o cztery limity i wykrywanie zapętlenia. Narzędzie celowo zwraca pustkę, żeby zobaczyć mechanizm w działaniu.

python
import json, time
from openai import OpenAI

client = OpenAI()

def szukaj_zamowien(numer_klienta):
    return []                              # celowo: pustka bez wyjaśnienia

narzedzia = [{
    "type": "function",
    "function": {
        "name": "szukaj_zamowien",
        "description": "Wyszukuje zamówienia klienta po numerze",
        "parameters": {
            "type": "object",
            "properties": {"numer_klienta": {"type": "string"}},
            "required": ["numer_klienta"],
        },
    },
}]

MAX_KROKOW, MAX_TOKENOW, MAX_SEKUND, OKNO = 10, 20000, 30, 4

wiadomosci = [{"role": "user",
               "content": "Znajdź zamówienia klienta 4471 i podsumuj je."}]
historia, krok, tokeny, start = [], 0, 0, time.time()
powod = None

while True:
    if krok >= MAX_KROKOW:            powod = "limit kroków";  break
    if tokeny >= MAX_TOKENOW:         powod = "limit budżetu"; break
    if time.time() - start > MAX_SEKUND: powod = "limit czasu"; break

    krok += 1
    odp = client.chat.completions.create(
        model="gpt-4o-mini", messages=wiadomosci, tools=narzedzia
    )
    tokeny += odp.usage.total_tokens
    komunikat = odp.choices[0].message
    wiadomosci.append(komunikat)

    if not komunikat.tool_calls:
        powod = "model odpowiedział"
        break

    for w in komunikat.tool_calls:
        sygnatura = (w.function.name, w.function.arguments.strip().lower())
        historia.append(sygnatura)
        if historia[-OKNO:].count(sygnatura) >= 3:
            powod = "wykryto zapętlenie"

        wynik = szukaj_zamowien(**json.loads(w.function.arguments))
        wiadomosci.append({"role": "tool", "tool_call_id": w.id,
                           "content": json.dumps(wynik)})
    if powod:
        break

print(f"powód: {powod} | kroki: {krok} | tokeny: {tokeny}")

Uruchom i zobacz, ile obrotów agent wykonuje, zanim coś go zatrzyma. Prawdopodobnie kilka — i żaden nie przybliży go do celu.

Teraz najważniejsza część ćwiczenia. Zamień pustą listę na komunikat:

python
def szukaj_zamowien(numer_klienta):
    return {"znaleziono": 0,
            "komunikat": f"Brak zamówień dla klienta {numer_klienta}. "
                         f"Numer nie występuje w bazie."}

Uruchom ponownie. W większości przypadków agent zakończy po jednym wywołaniu, odpowiadając, że takiego klienta nie ma. Zmieniło się jedno zdanie w narzędziu — nie prompt, nie model, nie parametry.

To jest cała lekcja tego artykułu: zapętlenie zwykle nie jest problemem modelu, tylko projektu narzędzia.

Co dalej

Wszystko powyżej zakłada agenta, który dostaje zadanie i ma je skończyć. Istnieje jednak druga kategoria: agenci, którzy nie kończą wcale — działają w tle, budzą się co jakiś czas i sami decydują, czy jest coś do zrobienia.

Warunek zakończenia przestaje wtedy dotyczyć agenta, a zaczyna dotyczyć pojedynczego cyklu. Zmienia się też sposób liczenia budżetu i model nadzoru.

O tym jest artykuł trzeci: Agent wyzwalany kontra agent długo żyjący.

Agent kończy, ale zadania nie wykonał

Agent kończy, ale zadania nie wykonał

Objawy Agent odpowiada: „Przygotowałem i wysłałem potwierdzenie do klienta." Potwierdzenie nie zostało wysłane. Albo: „Zaktualizowałem wszystkie rekordy" — zaktualizował trzy z dwunastu. Albo: „Nie znalazłem żadnych zamówień" — bo narzędzie zwróciło błąd, którego nikt...

Agent zrobił coś, o co nikt nie prosił

Agent zrobił coś, o co nikt nie prosił

Objawy Operacja, której nikt nie zlecił. Dane wysłane pod adres, którego nie ma w żadnej konfiguracji. Rekord zmieniony poza zakresem zadania. Odpowiedź, z której wynika, że agent dostał instrukcje od kogoś innego niż ty. Cecha wspólna: technicznie wszystko zadziałało...

Agent zrobił to samo dwa razy

Agent zrobił to samo dwa razy

Objawy Ten syndrom różni się od pozostałych jedną rzeczą: objaw widzi klient, nie ty. Trzy identyczne wiadomości w skrzynce. Dwa zamówienia zamiast jednego. Podwójne obciążenie. Duplikaty rekordów, które ktoś zauważa tydzień później. W twoim dzienniku wszystko wygląda...

Agent gubi wątek w długim zadaniu

Agent gubi wątek w długim zadaniu

Objawy Pierwsze kroki idą wzorowo. Po kilkunastu agent zaczyna się rozjeżdżać. Przestaje przestrzegać reguły z promptu systemowego, której trzymał się na początku. Zmienia format odpowiedzi w połowie zadania. Wraca do czegoś, co już ustalił, i ustala to inaczej....

Agent podaje dane, których nie ma

Agent podaje dane, których nie ma

Objawy Numer zamówienia w idealnym formacie, którego nie ma w bazie. Kwota, której w dokumencie nie ma. Nazwa pola API, które nigdy nie istniało. Cytat z regulaminu, brzmiący dokładnie jak reszta regulaminu i w nim nieobecny. Cecha wspólna wszystkich tych przypadków:...

Działał wczoraj, dziś nie działa

Działał wczoraj, dziś nie działa

Objawy Nic się nie wywala. Nie ma wyjątków, nie ma timeoutów, dziennik wygląda tak samo jak zawsze. Po prostu odpowiedzi są gorsze. Postacie, w jakich to się objawia: Jakość. Agent zaczyna pomijać kroki, które wcześniej wykonywał, albo odpowiada ogólniej. Format....

Agent wybiera złe narzędzie

Agent wybiera złe narzędzie

Objawy Agent odpowiada nie na to pytanie. Pobiera listę zamówień, gdy pytano o jedno konkretne. Odpowiada z pamięci, choć miał sprawdzić w bazie. Albo sięga po właściwe narzędzie i wpisuje w argumenty coś, czego nie da się użyć. Sygnał, który odróżnia ten syndrom od...

Agent kręci się w kółko

Agent kręci się w kółko

Objawy Agent wykonuje kolejne obroty pętli, nie zbliżając się do zakończenia zadania. W dzienniku widać jedną z trzech postaci. Powtórzenie. To samo narzędzie, te same argumenty, raz za razem — czasem dziesiątki razy pod rząd. Oscylacja. Agent wywołuje na przemian dwa...