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

przez Łukasz | sie 24, 2026

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. Odpowiedzi mają inną długość, inny styl, więcej zastrzeżeń. Coś, co parsowałeś regularnie, przestaje się parsować.

Trajektoria. Ta sama sprawa idzie teraz inną drogą — więcej kroków, inne narzędzia, inny wynik.

Kluczowy sygnał: nikt niczego nie wdrażał. Albo wdrożył coś, co z tym zachowaniem nie miało nic wspólnego.

Mechanizm w trzech zdaniach

Wagi modelu są zamrożone, ale model dostępny przez API to usługa, nie plik — dostawca aktualizuje go, a ty zwykle wskazujesz alias, który po cichu przełącza się na nową wersję. Kolejne wydania przechodzą nowe dostrajanie, więc zmienia się ton, długość i skłonność do odmowy, choć „wiedza” pozostaje podobna.

Do tego dochodzi wszystko poza modelem: twoje API, twoje dane, cudze serwery, wersje bibliotek.

Zanim zaczniesz: czy to na pewno regresja

To pytanie trzeba zadać najpierw, bo w jednym przypadku na kilka odpowiedź brzmi „nie”.

Agent mógł nigdy nie działać dobrze. Wdrożenie sprawdzono na kilkunastu przypadkach, wszystkie wypadły poprawnie, więc uznano temat za zamknięty. Tymczasem odpowiedzi są niedeterministyczne — jeden udany przebieg nie mówi nic o dziewięciu kolejnych.

Test: weź pięć przypadków, które „przestały działać”, i uruchom każdy dziesięć razy. Jeśli wynik jest niestabilny również teraz, nie masz regresji — masz agenta, który zawsze działał w siedemdziesięciu procentach, tylko nikt tego nie zmierzył.

To rozróżnienie zmienia całą dalszą drogę: w pierwszym przypadku szukasz zmiany, w drugim budujesz ewaluację.

Diagnostyka różnicowa

1. Aktualizacja modelu po stronie dostawcy

Jak rozpoznać: data pogorszenia jest ostra — jeden dzień, czasem godzina. Zmienił się głównie styl i długość odpowiedzi.

Test: sprawdź, czy w śladzie zapisujesz pełną wersję modelu, a nie alias. Jeśli tak, porównaj wersję sprzed i po. Jeśli nie — to jest pierwsza rzecz do naprawienia, bo bez tego przyczyna jest niediagnozowalna.

2. Zmiana w danych wejściowych

Jak rozpoznać: pogorszenie dotyczy tylko części przypadków, powiązanych z jednym źródłem.

Dlaczego to boli: API dodało pole, zmieniło format daty, zaczęło zwracać null tam, gdzie wcześniej był pusty ciąg. Model dostaje inny tekst, więc podejmuje inne decyzje — mimo że twój kod się nie zmienił.

Test: porównaj surowe wyniki narzędzi ze śladów sprzed i po. Szukasz różnicy w strukturze, nie w wartościach.

3. Zmiana w zewnętrznym serwerze MCP

Jak rozpoznać: pogorszenie dotyczy narzędzi z konkretnego serwera. Twój kod ich nie definiuje.

Dlaczego to boli: katalog pobierany zdalnie może zmienić opisy narzędzi między jednym pobraniem a drugim. Zwykle to zwykła aktualizacja, czasem podmiana po zatwierdzeniu.

Test: zapisz skrót całego katalogu narzędzi w śladzie przebiegu. Różnica skrótów między datami jest rozpoznaniem w jednej linijce.

4. Dryf danych po twojej stronie

Jak rozpoznać: pogorszenie jest stopniowe, nie skokowe. Dotyczy wyszukiwania i odpowiedzi opartych na bazie wiedzy.

Dlaczego to boli: baza urosła, więc retrieval zwraca teraz inne fragmenty. Nic się nie zepsuło — zmieniła się konkurencja w rankingu.

Test: uruchom te same zapytania na wyszukiwaniu i porównaj zwrócone fragmenty ze śladem archiwalnym.

5. Zmiana w twoim kodzie, której nie uznałeś za zmianę promptu

Jak rozpoznać: wdrożenie było, ale dotyczyło „czegoś innego”.

Dlaczego to boli: dopisanie pola do wyniku narzędzia, zmiana kolejności kluczy, inny komunikat błędu, przesunięcie fragmentu w prompcie — wszystko to zmienia tekst trafiający do modelu. Każda zmiana czegokolwiek, co ląduje w kontekście, jest zmianą promptu.

Test: przejrzyj wdrożenia z okresu regresji pod kątem tego kryterium, a nie pod kątem „czy dotyczyło agenta”.

6. Zmiana wersji biblioteki lub frameworka

Jak rozpoznać: data pogorszenia zbiega się z aktualizacją zależności.

Dlaczego to boli: zmienione wartości domyślne — temperatura, limit tokenów, liczba ponowień, sposób składania historii. Nic w twoim kodzie nie musiało się zmienić.

Test: wypisz faktycznie wysyłane parametry wywołania, zamiast ufać, że są takie jak w konfiguracji.

7. Przeciążenie po stronie dostawcy

Jak rozpoznać: wzrost liczby ponowień i czasu odpowiedzi, jakość gorsza nierównomiernie.

Dlaczego to boli: timeouty prowadzą do ponowień, ponowienia do innych trajektorii, a przy braku idempotencji także do powtórzonych operacji.

Test: porównaj liczbę ponowień na przebieg przed i po.

8. Zmiana modelu embeddingowego

Jak rozpoznać: wyłącznie wyszukiwanie semantyczne działa gorzej, reszta bez zmian.

Dlaczego to boli: wektory z różnych modeli są nieporównywalne. Częściowe przeliczenie bazy daje wyniki, które wyglądają wiarygodnie i są bezużyteczne.

Test: sprawdź, czy wszystkie wektory w bazie pochodzą z jednego modelu i jednej wersji.

Postępowanie

Natychmiast: przypnij konkretną wersję modelu zamiast aliasu, jeśli dostawca to umożliwia. To zatrzymuje dalszy dryf i pozwala pracować na stabilnym podłożu.

Potem: uruchom zestaw testowy z archiwalnych przypadków. Jeśli go nie masz — zbuduj z logów sprzed regresji, bo to najlepszy dostępny materiał. Bez punktu odniesienia każda dalsza zmiana jest zgadywaniem.

Następnie bisekcja przez daty. Wypisz wszystko, co zmieniło się w oknie regresji: wersja modelu, wdrożenia, aktualizacje zależności, skrót katalogu narzędzi, rozmiar bazy wiedzy. Zwykle kandydat jest jeden.

Nie przepisuj promptu jako pierwszego ruchu. To najczęstszy odruch, kosztuje najwięcej czasu i przy siedmiu z ośmiu przyczyn nie ma nic do rzeczy.

Profilaktyka

Zapisuj w śladzie przebiegu: pełną wersję modelu, skrót katalogu narzędzi, wersje kluczowych bibliotek i faktycznie użyte parametry wywołania. Cztery pola, które zamieniają dochodzenie z dni na minuty.

Uruchamiaj zestaw testowy cyklicznie, nie tylko przy zmianie kodu. To jest kluczowa różnica wobec klasycznego CI: tutaj podłoże zmienia się bez twojego udziału, więc test odpalany wyłącznie przy wdrożeniu nie wykryje niczego. Wystarczy raz dziennie.

Jak zdiagnozować u siebie

Prosty sygnalizator dryfu: uruchamia zestaw kontrolny, porównuje z zapisanym punktem odniesienia i wypisuje, co się zmieniło w otoczeniu.

python
import hashlib, json, datetime

def odcisk_katalogu(narzedzia):
    tresc = json.dumps(narzedzia, sort_keys=True, ensure_ascii=False)
    return hashlib.sha256(tresc.encode()).hexdigest()[:12]

def kontrola(zestaw, uruchom, narzedzia, model, punkt_odniesienia=None):
    """zestaw: [(pytanie, oczekiwane)]; uruchom: funkcja -> (wynik, meta)"""
    wyniki, trafienia = [], 0
    for pytanie, oczekiwane in zestaw:
        wynik, meta = uruchom(pytanie)
        ok = oczekiwane in str(wynik)
        trafienia += ok
        wyniki.append({"pytanie": pytanie, "ok": ok,
                       "kroki": meta.get("kroki"), "model": meta.get("model")})

    raport = {
        "data": datetime.date.today().isoformat(),
        "model": model,
        "odcisk_katalogu": odcisk_katalogu(narzedzia),
        "trafnosc": round(trafienia / len(zestaw), 3),
        "srednie_kroki": round(sum(w["kroki"] or 0 for w in wyniki) / len(wyniki), 1),
    }

    if punkt_odniesienia:
        print(f"trafność {punkt_odniesienia['trafnosc']:.0%} → {raport['trafnosc']:.0%}")
        if raport["model"] != punkt_odniesienia["model"]:
            print(f"  ! ZMIANA MODELU: {punkt_odniesienia['model']} "
                  f"→ {raport['model']} (przyczyna 1)")
        if raport["odcisk_katalogu"] != punkt_odniesienia["odcisk_katalogu"]:
            print(f"  ! ZMIANA KATALOGU NARZĘDZI (przyczyna 3 lub 5)")
        if raport["srednie_kroki"] > punkt_odniesienia["srednie_kroki"] * 1.3:
            print(f"  ! DŁUŻSZE TRAJEKTORIE: "
                  f"{punkt_odniesienia['srednie_kroki']} → {raport['srednie_kroki']}")
    return raport

Odcisk katalogu narzędzi jest tu najważniejszym pojedynczym polem. Wykrywa naraz dwie przyczyny — cudzą zmianę na serwerze MCP i twoją własną zmianę, o której nie pomyślałeś jako o zmianie promptu — a kosztuje jedną linijkę.

Ćwiczenie: zapisz punkt odniesienia dzisiaj, uruchamiaj kontrolę raz dziennie przez tydzień i zobacz, ile z tych pól zmieni się bez twojego udziału. Przy większości wdrożeń odpowiedź zaskakuje.

Powiązane

Co logować w pętli, żeby dało się cokolwiek zdiagnozować: Maszyneria agenta. Skąd bierze się charakter modelu i dlaczego zmienia się między wersjami: Anatomia LLM.

Następny syndrom: Agent podaje dane, których nie ma.

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:...

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...

Co logować w pętli agenta

Co logować w pętli agenta

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...