W drugim artykule padło, że pętla powinna zwracać nie samą odpowiedź, lecz odpowiedź razem z powodem zakończenia, liczbą kroków i zużytym budżetem. To był zalążek. Tu jest pełna wersja.
Typowy dziennik agenta zapisuje pytanie i odpowiedź. To akurat najmniej użyteczna część — odpowiedź widzi użytkownik, a pytanie i tak znasz. Wartość diagnostyczna leży w tym, co działo się pomiędzy.
Ślad przebiegu
Jeden rekord na całe zadanie, z polami, które pozwalają odpowiedzieć na pytanie „co się właściwie stało” bez zaglądania w szczegóły.
Identyfikator przebiegu. Co go wyzwoliło — użytkownik, zdarzenie, cykl. Model i jego wersja. Powód zakończenia — czy model odpowiedział, czy wyczerpał limit, czy wykryto zapętlenie. Liczba kroków. Tokeny wejściowe i wyjściowe osobno, plus koszt. Czas trwania. Liczba wykonanych operacji nieodwracalnych.
Zapis wersji modelu wygląda na formalność, a jest jedynym sposobem powiązania spadku jakości z aktualizacją po stronie dostawcy. Bez niego cicha regresja jest niewykrywalna — masz gorsze wyniki i żadnego kandydata na przyczynę.
Ślad kroku
Jeden rekord na obrót pętli, dowiązany do przebiegu.
Numer kroku. Wywołane narzędzie i argumenty. Wynik — w skrócie albo jako uchwyt, zgodnie z tym, co ustaliliśmy w artykule o stanie. Czas wykonania. Tokeny tego obrotu. Liczba ponowień. Odrzucenia wraz z powodem.
To ostatnie jest najczęściej pomijane i najbardziej wartościowe. Dzienniki zapisują, co się wykonało; odrzucenia znikają, bo formalnie nic się nie stało. A rosnąca liczba odrzuceń tego samego typu oznacza jedną z trzech rzeczy: myląca definicja narzędzia, zbyt ostra walidacja albo ktoś próbuje czegoś, czego nie powinien.
Czego nie logować
Log jest miejscem wycieku równie realnym jak kontekst modelu — z tą różnicą, że zostaje na dłużej i bywa dostępny szerszemu gronu.
Nie zapisuj pełnych kontekstów z danymi osobowymi, poświadczeń, treści dokumentów objętych poufnością ani surowych śladów wyjątków ze ścieżkami i nazwami hostów.
Zasada praktyczna: redakcja przy zapisie, nie przy odczycie. Jeśli dane wrażliwe trafiają do dziennika i są ukrywane dopiero w interfejsie, to one tam są — a założenie, że nikt nie zajrzy do surowych plików, nie jest zabezpieczeniem.
Wzorzec z artykułu siódmego działa tu bez zmian: w logu zostaje uchwyt, wartość zostaje w miejscu, do którego dostęp jest kontrolowany osobno.
Pięć sygnałów
Same dane niczego nie mówią. Poniższe pięć wykresów wychwytuje praktycznie wszystkie wykolejenia opisane w tej serii.
Rozkład liczby kroków. Nie średnia — rozkład. Wykolejenia siedzą w prawym ogonie: przebiegi, które zamiast trzech kroków wzięły dwanaście. Średnia je ukrywa.
Udział zakończeń wymuszonych. Ile przebiegów kończy się limitem albo wykrytym zapętleniem zamiast odpowiedzią modelu. To jest miara zdrowia agenta i najszybszy wskaźnik, że coś się zmieniło.
Koszt na przebieg w percentylach. Znowu nie średnia. Interesuje cię, ile kosztuje najdroższe pięć procent przebiegów, bo to one robią rachunek.
Odrzucenia według typu. Sygnał opisany wyżej.
Ponowienia według narzędzia. Wskazuje niestabilne zależności, zanim zaczną psuć wyniki. Warto pamiętać, że ponowienia bywają rozliczane, a w typowym logu nie zostawiają śladu.
Jeden identyfikator przez wszystkie warstwy
Identyfikator przebiegu powinien przechodzić dalej niż twój kod: do wywołań API, do systemów docelowych, do klucza idempotencji.
Dzięki temu odpowiedź na pytanie „skąd wziął się ten mail w skrzynce klienta” jest do znalezienia w jednym zapytaniu, a nie przez zestawianie znaczników czasu. Przy operacjach nieodwracalnych to nie wygoda, tylko warunek prowadzenia jakiegokolwiek dochodzenia.
Log jako materiał do ewaluacji
To jest zwrot, dla którego warto zbierać ślady nawet wtedy, gdy nic się nie psuje.
Przebiegi z produkcji są najlepszym możliwym zestawem testowym. Realne pytania, realne dane, realne przypadki graniczne — czyli dokładnie to, czego nie da się wymyślić przy biurku i czego nie dostarczy żaden publiczny benchmark.
Wystarczy wybrać kilkadziesiąt przebiegów, dopisać oczekiwany wynik i masz zestaw, którego szukaliśmy w Anatomii LLM przy okazji kontaminacji benchmarków: własny, niepublikowany, mierzący dokładnie twoje zadanie. Ten sam materiał zasila ewaluację trajektorii — bo ślad kroków to zapis drogi, nie tylko wyniku.
Jak to sprawdzić u siebie
Prosty zbieracz śladu, który da się nałożyć na pętlę z pierwszego artykułu.
import json, time, uuid, statistics
class Slad:
def __init__(self, wyzwalacz, model):
self.rekord = {
"id": str(uuid.uuid4())[:8], "wyzwalacz": wyzwalacz,
"model": model, "start": time.time(), "kroki": [],
"tokeny_we": 0, "tokeny_wy": 0,
"nieodwracalne": 0, "odrzucenia": 0, "powod": None,
}
def krok(self, nr, narzedzie, argumenty, tokeny_we, tokeny_wy,
ponowienia=0, odrzucenie=None, skutek=False):
self.rekord["kroki"].append({
"nr": nr, "narzedzie": narzedzie,
"argumenty": redaguj(argumenty),
"tokeny_we": tokeny_we, "ponowienia": ponowienia,
"odrzucenie": odrzucenie,
})
self.rekord["tokeny_we"] += tokeny_we
self.rekord["tokeny_wy"] += tokeny_wy
if odrzucenie: self.rekord["odrzucenia"] += 1
if skutek and not odrzucenie: self.rekord["nieodwracalne"] += 1
def zamknij(self, powod):
self.rekord["powod"] = powod
self.rekord["czas_s"] = round(time.time() - self.rekord["start"], 2)
self.rekord["liczba_krokow"] = len(self.rekord["kroki"])
return self.rekord
WRAZLIWE = {"adres", "email", "pesel", "karta", "token"}
def redaguj(d):
return {k: ("***" if k.lower() in WRAZLIWE else v) for k, v in d.items()}
# --- przykład użycia ---
s = Slad("uzytkownik", "gpt-4o-mini@2026-05")
s.krok(1, "status_zamowienia", {"numer_zamowienia": "ZAM-9999"},
1200, 40, odrzucenie="zamówienie nie istnieje")
s.krok(2, "szukaj_zamowien", {"numer_klienta": "KL-0012"}, 1800, 60, ponowienia=1)
s.krok(3, "wyslij_mail", {"adres": "kontakt@firma.pl"}, 2400, 80, skutek=True)
print(json.dumps(s.zamknij("model odpowiedział"), ensure_ascii=False, indent=2))
Zwróć uwagę na trzy rzeczy. Argumenty przechodzą przez redaguj przy zapisie — adres nigdy nie trafia do rekordu. Odrzucenie z pierwszego kroku jest zapisane jako pełnoprawne zdarzenie, nie pominięte. A tokeny wejściowe rosną z każdym krokiem, co widać w rekordzie gołym okiem.
Ćwiczenie: zbierz ślady z dwudziestu przebiegów i policz dwie rzeczy — medianę oraz maksimum liczby kroków, a potem koszt w percentylu dziewięćdziesiątym. Jeśli maksimum jest trzykrotnie wyższe od mediany, masz zapętlenia. Jeśli percentyl dziewięćdziesiąty jest wielokrotnie wyższy od mediany kosztu, masz kilka przebiegów robiących cały rachunek.
Drugie ćwiczenie: weź dziesięć śladów z realnych przebiegów, dopisz do każdego oczekiwany wynik i zapisz jako plik. Właśnie zbudowałeś zestaw testowy, którym sprawdzisz następną zmianę modelu.
Koniec serii
Dziesięć artykułów o warstwie, która nie jest ani modelem, ani promptem — o kodzie między jednym wywołaniem a drugim.
Wracając do zdania, od którego zaczynaliśmy: model niczego nie wykonuje, wykonuje pętla. Wszystko, co przez tę serię przewijało się jako problem — koszt kumulacyjny, zapętlenie, przeciążenie narzędziami, awarie, uprawnienia, wstrzyknięcia — mieszka po tej samej stronie granicy. I dlatego wszystko da się tam rozwiązać.
Jeśli interesuje cię, co jest pod tą warstwą — dlaczego model halucynuje, skąd bierze się koszt kontekstu, czym różni się model rozumujący — to opisuje Anatomia LLM. Jeśli interesuje cię, z czego agent jest zbudowany jako całość, razem z pamięcią, RAG-iem i wdrożeniem — Anatomia agenta AI.
A jeśli po tej lekturze wydaje ci się, że w twoim przypadku wystarczyłby zwykły skrypt — to prawdopodobnie dobry wniosek i też ma swój tekst: Kiedy nie budować agenta.





















