Debugowanie kodu z AI: jak znaleźć błąd, zamiast prosić o kolejną poprawkę

Mateusz Bogolubow

02-11-2026

(aktualizacja: 02-11-2026)

Jak debugować kod napisany przez AI, gdy kolejne poprawki od modelu tylko przesuwają błąd? Przykład w Pythonie z pdb, szablon zgłoszenia błędu i reguła, kiedy odłożyć czat.

Debugowanie kodu z AI: jak znaleźć błąd, zamiast prosić o kolejną poprawkę
Błąd w kodzie napisanym przez AI debugujesz tak samo jak każdy inny: najpierw go odtwarzasz w teście, potem sprawdzasz w debuggerze, jakie wartości faktycznie mają zmienne, i dopiero wtedy poprawiasz. Zasada, którą AI łatwo łamie: poprawiasz przyczynę, nie objaw, więc kolejnych łatek od modelu nie przyjmujesz bez zrozumienia, dlaczego działają. Gdy po dwóch próbach błąd wciąż wraca, odłóż czat i otwórz debugger.

Kod od AI zwykle działa za pierwszym razem na przykładzie z promptu. Problem zaczyna się, gdy dostaje inne dane. Wtedy wklejasz błąd z powrotem do czatu, dostajesz poprawkę, a po chwili wyskakuje następny błąd w tym samym miejscu. Ten artykuł pokazuje, jak z tej pętli wyjść: na prostej funkcji w Pythonie, z uruchomionymi testami i debuggerem.

Czym debugowanie kodu z AI różni się od zwykłego debugowania?

Sama metoda jest ta sama. Różni się to, co sprawia, że w pętli „wklej błąd, dostań poprawkę” tak łatwo utknąć:

Zwykły kodKod od AI
Znasz intencję, bo sam ją zapisałeśIntencja jest w prompcie, a kod ją tylko przybliża
Błąd zwykle jest w miejscu, które ostatnio zmieniałeśBłąd może siedzieć w części, której nie czytałeś
Poprawka wynika z Twojego zrozumieniaPoprawka wynika z tego, co model uzna za prawdopodobne
Piszesz test, bo chceszKod „działa”, więc testu nie ma

Ostatni wiersz jest najważniejszy. Bez testu nie masz niczego, co powie, czy poprawka rzeczywiście coś naprawiła, więc oceniasz ją na oko. Tak działa pętla, w której model kolejno „naprawia” własne łatki.

Od czego zacząć debugowanie, gdy kod od AI nie działa?

Od odtworzenia błędu, a nie od poprawki. Zapisz najmniejszy przypadek, który go wywołuje (po angielsku minimal reproducible example), jako test. To, co masz w głowie, nie wystarczy: test jest jedyną rzeczą, która po poprawce powie Ci prawdę.

Przykład. Poprosiłeś model o funkcję liczącą średnią ocenę produktu i dostałeś ją w tej postaci:

def average_rating(reviews):
    ratings = [review["rating"] for review in reviews]
    total = sum(rating for rating in ratings if rating is not None)
    return round(total / len(ratings), 2)

Wygląda rozsądnie, a nawet radzi sobie z recenzjami bez oceny (None). Zanim cokolwiek poprawisz, zapisz oczekiwane zachowanie w trzech testach. Test, który opisuje zachowanie przed implementacją, to ta sama idea co w TDD i BDD na przykładzie w pytest:

from ratings import average_rating


def test_average_of_rated_reviews():
    reviews = [{"rating": 5}, {"rating": 3}]
    assert average_rating(reviews) == 4.0


def test_unrated_reviews_are_ignored():
    reviews = [{"rating": 5}, {"rating": None}, {"rating": 3}]
    assert average_rating(reviews) == 4.0


def test_no_reviews_returns_none():
    assert average_rating([]) is None

Uruchamiasz pytest i widzisz dwa różne błędy:

FAILED test_ratings.py::test_unrated_reviews_are_ignored - AssertionError: assert 2.67 == 4.0
FAILED test_ratings.py::test_no_reviews_returns_none - ZeroDivisionError: division by zero
2 failed, 1 passed

Jeden jest głośny (wyjątek), drugi cichy (zły wynik bez żadnego komunikatu). Od tego rozróżnienia zależy dalsza praca.

Jak czytać traceback, żeby wiedzieć, gdzie szukać?

Komunikat o błędzie z listą wywołań (po angielsku traceback albo stack trace) czytasz od dołu. Ostatnia linia mówi, co poszło nie tak, a linia nad nią, gdzie:

    def average_rating(reviews):
        ratings = [review["rating"] for review in reviews]
        total = sum(rating for rating in ratings if rating is not None)
>       return round(total / len(ratings), 2)
E       ZeroDivisionError: division by zero
ratings.py:4: ZeroDivisionError

Tu odpowiedź jest prosta: len(ratings) wynosi zero, bo lista recenzji jest pusta. Zwróć uwagę, że traceback wskazuje miejsce, w którym błąd wyszedł na jaw. Nie zawsze jest to miejsce, w którym powstał.

Dlaczego kolejne poprawki od AI przesuwają błąd zamiast go naprawić?

Bo model dostaje komunikat o błędzie i szuka najprostszej zmiany, która sprawi, że ten komunikat zniknie. Dla ZeroDivisionError poprawka jest oczywista (if not reviews: return None). Dla cichego błędu z wynikiem 2.67 nie ma komunikatu, więc model zgaduje. Typowa odpowiedź wygląda tak:

def average_rating(reviews):
    if not reviews:
        return None
    ratings = [review["rating"] for review in reviews]
    total = sum(rating for rating in ratings if rating is not None)
    if None in ratings:
        return round(total / (len(ratings) - 1), 2)
    return round(total / len(ratings), 2)

Test z jedną recenzją bez oceny przechodzi, więc poprawka wygląda na udaną. Dodaj jednak czwarty test z dwiema recenzjami bez oceny:

def test_two_unrated_reviews():
    reviews = [{"rating": 5}, {"rating": None}, {"rating": None}, {"rating": 3}]
    assert average_rating(reviews) == 4.0
FAILED test_ratings.py::test_two_unrated_reviews - AssertionError: assert 2.67 == 4.0
1 failed, 3 passed

len(ratings) - 1 odejmuje dokładnie jedną recenzję, niezależnie od tego, ile ich jest bez oceny. To jest poprawka na objaw: dopasowana do jednego przypadku testowego, a nie do przyczyny. Przyjęcie jej bez zrozumienia kończy się kolejnym błędem za tydzień, już na produkcji.

Jak sprawdzić w debuggerze, co naprawdę dzieje się w kodzie?

Zamiast zgadywać, zatrzymaj program w podejrzanym miejscu i sprawdź wartości. W Pythonie wystarczy wbudowany debugger pdb. Uruchamiasz z jego pomocą skrypt run_dbg.py, który wywołuje funkcję na przykładowych danych, ustawiasz punkt zatrzymania (breakpoint) w linii z dzieleniem i wypisujesz zmienne:

python -m pdb run_dbg.py
(Pdb) b ratings.py:4
(Pdb) c
(Pdb) p ratings
[5, None, 3]
(Pdb) p total
8
(Pdb) p len(ratings)
3
(Pdb) p [r for r in ratings if r is not None]
[5, 3]

Teraz widać przyczynę. Suma (8) jest policzona tylko z ocen, a dzielnik (3) liczy wszystkie recenzje, także te bez oceny. Licznik i mianownik pochodzą z dwóch różnych zbiorów. Żadna łatka na len(ratings) - 1 tego nie naprawi, bo problem nie jest w liczbie, tylko w tym, co liczymy.

Najczęstsze polecenia pdb, które wystarczą na początek:

PolecenieCo robi
b plik.py:4ustawia breakpoint w linii 4
cwykonuje program do następnego breakpointu
nwykonuje bieżącą linię i przechodzi do następnej
swchodzi do wywoływanej funkcji
p zmiennawypisuje wartość zmiennej lub wyrażenia
qkończy debugowanie

Jeśli wolisz zatrzymywać się dopiero wtedy, gdy test padnie, pytest --pdb otwiera ten sam debugger w miejscu błędu. Wszystkie polecenia pdb, breakpoint() i pytest --pdb opisuje artykuł o debugowaniu w Pythonie.

Jak poprawić błąd u źródła i upewnić się, że wrócił do zera?

Poprawiasz przyczynę: filtrujesz recenzje bez oceny przed liczeniem, żeby suma i liczba pochodziły z tego samego zbioru. Przy okazji obsługujesz przypadek, w którym nie zostaje żadna ocena:

def average_rating(reviews):
    ratings = [review["rating"] for review in reviews if review["rating"] is not None]
    if not ratings:
        return None
    return round(sum(ratings) / len(ratings), 2)
4 passed

Zwróć uwagę na dwie rzeczy. Po pierwsze, ta wersja jest krótsza od łatki, bo usunęła przyczynę zamiast dokładać obejście. Po drugie, test z dwiema recenzjami bez oceny zostaje w zestawie na stałe: następnym razem, gdy ktoś (lub model) dotknie tej funkcji, ten błąd wróci do Ciebie od razu, a nie od klienta.

Jak opisać błąd modelowi, żeby dostać użyteczną odpowiedź?

Gdy już wiesz, że chcesz poprosić model o pomoc, nie wklejaj samego komunikatu. Daj mu to, co masz w debuggerze.

Dobre zgłoszenie ma pięć elementów:

  1. Co miało się stać (jedno zdanie, np. „średnia z ocen, bez recenzji bez oceny”).
  2. Co się stało (konkretny wynik: „2.67 zamiast 4.0”).
  3. Najmniejszy przypadek, który to wywołuje (test z danymi, a nie opis słowami).
  4. Co już sprawdziłeś (np. „w pdb total = 8, len(ratings) = 3”).
  5. Czego nie wolno zmieniać (np. „nie zmieniaj sygnatury funkcji ani innych testów”).

Przykład gotowego zgłoszenia:

Funkcja average_rating ma zwracać średnią z ocen, pomijając recenzje bez oceny (None).
Test test_unrated_reviews_are_ignored zwraca 2.67 zamiast 4.0 dla [5, None, 3].
W debuggerze: total = 8 (z samych ocen), len(ratings) = 3 (razem z None).
Wygląda, że licznik i mianownik liczą z różnych zbiorów. Wyjaśnij przyczynę
i pokaż minimalną poprawkę. Nie zmieniaj sygnatury ani istniejących testów.

Piąty punkt jest tym, który najczęściej pomija się, a najbardziej chroni: bez niego model chętnie „przy okazji” przepisuje test, który mu przeszkadza. Jak opisywać nie tylko błędy, ale i same zadania, pokazuje artykuł o promptach do kodu.

Kiedy przestać pytać AI i otworzyć debugger samodzielnie?

Odłóż czat i weź się za debugger, gdy zajdzie którakolwiek z trzech rzeczy:

  • Po dwóch poprawkach ten sam test nadal pada (albo pada inny w tym samym miejscu). Trzecia próba zwykle jest wariacją pierwszych dwóch.
  • Poprawka zmienia więcej niż miejsce błędu. Gdy model dotyka innych funkcji albo testów „przy okazji”, nie wiesz już, co przeglądasz.
  • Nie potrafisz własnymi słowami wyjaśnić, dlaczego poprawka działa. Jeśli nie umiesz, nie będziesz wiedzieć, kiedy przestanie.

To samo dotyczy przeglądu kodu przed scaleniem: jak go robić, opisuje code review kodu napisanego przez AI.

Co zrobić, gdy błąd wygląda na nieistniejącą funkcję albo bibliotekę?

To inny przypadek niż w tym artykule. Jeśli kod w ogóle się nie uruchamia, bo model użył metody albo pakietu, które nie istnieją, nie debugujesz logiki, tylko sprawdzasz źródło. Jak to rozpoznać, opisuje artykuł o halucynacjach AI w kodzie. Tutaj chodzi o kod, który się uruchamia i liczy źle, a to trudniejsze, bo nic nie zgłasza błędu.

Podsumowanie: szybka lista kroków

  1. Odtwórz błąd najmniejszym możliwym testem.
  2. Odróżnij błąd głośny (wyjątek) od cichego (zły wynik).
  3. Przeczytaj traceback od dołu, pamiętając, że wskazuje, gdzie błąd wyszedł, a niekoniecznie gdzie powstał.
  4. Zatrzymaj program w debuggerze i sprawdź wartości, zamiast zgadywać.
  5. Poprawiaj przyczynę, a nie objaw, i zostaw test, który odtworzył błąd.
  6. Do modelu idź z konkretami (oczekiwanie, wynik, przypadek, co sprawdzone, czego nie ruszać).
  7. Po dwóch nieudanych próbach odłóż czat.

Chcesz uczyć się budować backend z AI, z testami od pierwszego dnia i code review swojego kodu? Zobacz ścieżkę AI Engineer.

Konsultacja i warsztaty devmentor.pl

Masz pytania?

Partnerzy

Firmy, z którymi współpracujemy

To partnerzy odpowiedzialni za staże po naszych kursach. Organizują je dla najlepszych absolwentów.

Zobacz wszystkich partnerów →
  • VR Medical
  • Sophona
  • Robot Force