Logo
Zbudowałam system multi-agentowy, który naprawia niestabilne testy

Zbudowałam system multi-agentowy, który naprawia niestabilne testy

CI to jak pudełko czekoladek — nigdy nie wiadomo, co się trafi.

Ten czerwony pipeline? Może to prawdziwy błąd. A może ten sam niestabilny test, który nie przeszedł wczoraj, przeszedł dziś rano i jutro znowu poleci z niewyjaśnionego powodu. Uruchamiasz job ponownie. Przechodzi. Idziesz dalej. I za tydzień to samo — u kogoś innego, kto spędza kolejne 30 minut na tym samym problemie.

Niestabilne testy nie są dramatyczne. Nie crashują produkcji, nie budzą nikogo o 3 w nocy. To wolny wyciek — zbyt mały, żeby go naprawić, wystarczająco duży, żeby narastać. Z miesiąca na miesiąc niszczy zaufanie do pipeline'u. Przestajemy wierzyć, że czerwony oznacza zepsuty. Review'y stają w miejscu, czekając na "jeszcze jeden zielony run". Nowi ludzie w zespole, bez plemiennej wiedzy o tym, które błędy "można zignorować", blokują się całkowicie.

Spędziłam lata pracując w środowiskach z intensywnym CI — budując infrastrukturę testową, debugując błędy pipeline'u, onboardując zespoły do frameworków testowych, których nie pisały. Robiłam na ten temat prezentacje na konferencjach, prowadziłam konsultacje dla firm. Ten sam pattern wszędzie: niestabilne testy to problem wszystkich i priorytet nikogo.

Zbudowałam więc system, który czyni je swoim priorytetem.

Pomysł

Pomyślałam, że mogłabym przenieść moją wiedzę i doświadczenie o tym, jak naprawianie takich testów wygląda — na AI. Zrobić kopię mojego workflow.

Rezultatem jest multi-agentowy orkiestrator, który pobiera artefakty CI z nieudanego pipeline'u, uruchamia równolegle agentów-śledczych analizujących błąd z różnych kątów, wybiera najbardziej wiarygodną przyczynę, stosuje ukierunkowaną poprawkę, sprawdza czy poprawka się kompiluje, opcjonalnie przepuszcza przez AI reviewer, pushuje na branch, uruchamia pipeline weryfikacyjny i powtarza aż poprawka się utrzyma — albo eskaluje do człowieka, gdy nie może.

Działa autonomicznie. Uruchamiam go, idę robić coś innego i wracam do merge requesta z poprawką, wyjaśnieniem przyczyny i historią wszystkiego, czego agenci próbowali.

Jak działają agenci

System używa kilkunastu wyspecjalizowanych agentów AI, każdy z ograniczonymi uprawnieniami i konkretną rolą. Żaden agent nie ma pełnego dostępu do wszystkiego. Śledczy mogą tylko czytać. Naprawiacz może edytować pliki, ale tylko w obrębie allowlisty. Reviewer może tylko czytać diff. To obrona warstwowa — orkiestrator kontroluje, co każdy agent może widzieć i robić. To nie tylko kwestia bezpieczeństwa, ale również oszczędności kontekstu. Gdy agent miał za dużo do zrobienia lub zbyt dużą rolę — zaczynały się timeouty.

Faza 0: Rozeznanie

Zanim zaczną się jakiekolwiek analizy, system pobiera z pipeline'u odpowiedni job, wyciąga logi i zbiera podstawowy kontekst: który job failuje, jakie testy nie przeszły, co mówią raporty. To etap czystego zbierania danych — bez hipotez, bez poprawek. Dopiero z tym kontekstem śledczy mogą zacząć pracę.

Faza 1: Dochodzenie (równolegle)

Trzej śledczy działają jednocześnie, każdy podchodzi do błędu z innej strony:

  • Pierwszy skupia się na izolacji testów: warunki wstępne, setup/teardown, wyciek stanu między testami
  • Drugi skupia się na współdzielonej infrastrukturze: stan między testami, zasoby singletonowe, globalna konfiguracja
  • Trzeci skupia się na timingu: operacje asynchroniczne, race conditions, opóźnienia zależne od środowiska

Każdy śledczy czyta logi joba, raporty testów i właściwy kod źródłowy testów. Każdy produkuje analizę przyczyny głównej z proponowaną poprawką i poziomem pewności. Trzy równoległe to trzy niezależne hipotezy.

Faza 2: Weryfikacja (równolegle)

Trzej weryfikatorzy sprawdzają wzajemnie hipotezy każdego śledczego:

  • Czy plik, do którego śledczy się odwołuje, naprawdę istnieje?
  • Czy proponowana zmiana jest składniowo poprawna?
  • Czy rozumowanie trzyma się w kontekście otaczającego kodu?

To wyłapuje halucynowane ścieżki plików, niemożliwe edycje i analizy, które brzmią sensownie, ale nie odpowiadają rzeczywistości. Tania brama sanitarna przed zatwierdzeniem poprawki.

Faza 3: Selekcja

Agent selekcji przegląda wszystkie trzy analizy i wybiera najbardziej wiarygodną. Preferuje analizy odwołujące się do konkretnych plików, z wyższym poziomem pewności i proponujące konkretne zmiany zamiast niejasnych sugestii. Jeśli wszystkie trzy się zgadzają — używa konsensusu. Jeśli tylko jedna analiza się udała — używa tej.

Ta faza była najtrudniejsza do zaimplementowania. Agent próbował powtarzać analizę poprzednich śledczych — w efekcie traciłam dużo czasu na tej fazie i nie uzyskiwałam rezultatu, bo okno kontekstowe zostało przekroczone. Rozwiązaniem było ścisłe ograniczenie roli tego agenta: tylko wybiera, nie analizuje od nowa.

Faza 4: Implementacja

Naprawiacz otrzymuje wybraną analizę i implementuje zmianę. Może czytać pliki, edytować je i tworzyć nowe — ale tylko w ścieżkach zdefiniowanych w allowliście projektu. To daje mu autonomiczność, a równocześnie pilnuje, żeby nie zepsuł czegoś w projekcie. Po wprowadzeniu zmian orkiestrator uruchamia sprawdzenie kompilacji (TypeScript typecheck, Kotlin build, cokolwiek projekt potrzebuje), żeby wychwycić błędy składni przed pushem.

Faza 5: Review (warunkowy)

Jeśli diff jest większy niż konfigurowalny próg, agent reviewer ocenia zmianę względem kryteriów review specyficznych dla projektu. Jeśli odrzuca poprawkę, naprawiacz dostaje feedback i próbuje ponownie — do trzech rund. Małe, chirurgiczne poprawki omijają review całkowicie i trafiają bezpośrednio do CI.

Agenci pracujący równolegle

Pętla

Po zacommitowaniu i zpushowaniu poprawki orkiestrator uruchamia pipeline weryfikacyjny. Jeśli przechodzi, uruchamia ponownie — i znowu — aż osiągnie wymaganą liczbę kolejnych zielonych przebiegów (zazwyczaj trzy). Dopiero wtedy ogłasza poprawkę zweryfikowaną.

Jeśli pipeline nie przejdzie, system porównuje nowe błędy z baseline'em. Jeśli nowy test się posypał (regresja) — flaguje to. Potem pobiera nowe artefakty i zaczyna nowy cykl dochodzenia, niosąc kontekst z poprzednich iteracji — co próbowano, co nie wyszło, jakie były błędy kompilacji. Naprawiacz widzi pełną historię i unika powtarzania tego samego błędu.

Jeśli naprawiacz nie może zaproponować żadnych zmian dwie iteracje z rzędu, system kończy ze statusem "eskalowane". Nie forsuje złych poprawek. Lepiej oddać to z powrotem człowiekowi z szczegółową analizą niż wypychać coś złego.

Ta pętla jest nadal w trakcie developmentu — i sami możecie się domyślić, ile scenariuszy może się tu pojawić.

Interfejs

Lekki dashboard webowy streamuje logi w czasie rzeczywistym. Widać, w której fazie jest system, które agenty są aktywne, na czym skupia się każdy śledczy i podsumowanie zmian naprawiacza. Jest przycisk kill, jeśli trzeba przerwać. To miły dodatek, który umożliwia szybką kontrolę tego, co się dzieje — bez grzebania w logach. Gdy system skończy — odzywa się dźwiękiem.

Model bezpieczeństwa

Dawanie agentom AI dostępu do zapisu w codebasie wymaga zabezpieczeń. System wymusza kilka warstw:

  • Zakres narzędzi — śledczy i reviewer'zy są read-only. Naprawiacz może edytować pliki, ale tylko w obrębie allowlisty. Żaden agent nie może uruchamiać dowolnych komend shell, chyba że projekt wyraźnie na to pozwoli.
  • Ochrona branchy — orkiestrator weryfikuje aktywny branch przed każdym commitem. Jeśli naprawiacz wyląduje na złym branchu, system twardо wychodzi.
  • Allowlisting ścieżek — każdy zmodyfikowany plik jest sprawdzany względem allowlisty projektu. Zmiany poza dozwolonymi katalogami są automatycznie cofane.
  • Limity zakresu API — integracja CI jest ograniczona do konkretnych operacji: wyzwalanie pipeline'ów, pobieranie logów, pobieranie artefaktów, tworzenie merge requestów. Usuwanie branchy, zarządzanie członkami i ustawienia projektu są zablokowane.
  • Timeouty — każde wywołanie agenta ma twardy timeout, żeby zapobiec niekończącym się sesjom.
  • Hooki blokujące usuwanie plików — agent nie może usunąć żadnego pliku z repozytorium, nawet jeśli uzna to za zasadne.
  • Service account — agenci działają pod dedykowanym kontem, dzięki czemu w historii GitLab od razu widać, które zmiany zostały wprowadzone przez system, a które przez człowieka.

System jest zaprojektowany tak, żeby najgorszy scenariusz błędu to "nic nie naprawił" — nie "coś zepsuł".

Czego nauczyłam się budując to

Multi-agent bije single-agent przy dochodzeniu. Jeden agent zapytany "dlaczego ten test nie przeszedł?" często fiksuje się na jednej hipotezie. Trzej agenci z różnymi analitycznymi soczewkami konsekwentnie produkują lepsze analizy przyczyny głównej. Ta struktura — zbadaj, potem zweryfikuj, potem wybierz — filtruje false positives zanim dotrą do naprawiacza.

Iteracyjność bije jednorazowość przy naprawianiu. Pierwsza próba naprawy nie zawsze działa. Ale system uczy się przez iteracje. Przy drugiej czy trzeciej próbie naprawiacz widział błędy kompilacji, feedback reviewera i świeże wyniki pipeline'u. Ten narastający kontekst sprawia, że każda kolejna próba jest bardziej ukierunkowana. Czasem pierwsza iteracja poprawia, druga usuwa, a dopiero trzecia — mając kontekst obu poprzednich — trafia na właściwy efekt. Zupełnie jak w życiu.

Read-only jest niedoceniane. Większość agentów w tym systemie nic nie może zapisywać. To ograniczenie zmusza je do starannego przemyślenia rekomendacji, wiedząc, że ktoś inny to zaimplementuje. Oznacza też, że halucynujący śledczy nie może wyrządzić żadnych szkód — najgorsze, co może zrobić, to zmarnować cykl.

Kilka praktycznych szczegółów

Każdy run produkuje pełne logi z każdego etapu — zarówno prompt wysłany do agenta, jak i wynik jego pracy. Oprócz tego system zapisuje kilka plików kontekstowych: wybrane poprawki, efekty kolejnych pipeline'ów, skrócony opis przyczyn. To wszystko służy kilku celom: ułatwia debugowanie gdy coś idzie nie tak, zasila opis MR — żeby człowiek przeglądający merge request rozumiał, co zostało zmienione i dlaczego — a jeśli system nie dobrnął do końca, można te logi wrzucić bezpośrednio do Claude i ręcznie dokończyć pracę. Kontekst jest już zebrany, analiza częściowo zrobiona — wystarczy przejąć pałeczkę.

Prompty dla śledczych są zapisane jako osobne pliki .md — jeden na agenta. Łatwo je edytować, wersjonować i poprawiać. I ewoluują — za każdym razem, gdy system trafi na nowy typ błędu, który można było przewidzieć, uzupełniam prompt o tę wiedzę. To właśnie w tych plikach siedzi "kopia mojego workflow".

MR-y po weryfikacji nie są auto-mergowane — czekają na zatwierdzenie przez człowieka. Na tym etapie każdy merge to też okazja do obserwacji: czy poprawka jest sensowna, czy agent nie poszedł na skróty, czy prompt wymaga korekty. To nadal proces uczenia się systemu, nie tylko naprawiania testów.

Ograniczenia dostępu dla poszczególnych agentów — co mogą czytać, co edytować, jakie narzędzia mają dostępne — są konfigurowane bezpośrednio w ustawieniach Claude. To sprawia, że nie trzeba tego pilnować w kodzie orkiestratora — model sam respektuje przypisany mu zakres.

Chciałam używać Claude w wersji subskrypcyjnej zamiast API. Przy kilkunastu agentach odpalanych w jednej sesji — najlepiej w najwyższej dostępnej wersji modelu — koszt per naprawka przy płatności za tokeny potrafi zaskoczyć. Subskrypcja z nielimitowanym dostępem zmienia rachunek ekonomiczny całkowicie.

Stan obecny

System działa codziennie na prawdziwych pipeline'ach. Produkuje merge requesty z prawdziwymi poprawkami, które są przeglądane i mergowane przez ludzi.

Nie jest idealny. Eskaluje gdy powinien, czasem potrzebuje trzech iteracji tam gdzie człowiek potrzebuje jednej i nie może naprawić testów wymagających rozumienia logiki biznesowej, której go nie nauczono. Ale obsługuje mechaniczną, czasochłonną kategorię dochodzenia w sprawie niestabilnych testów, której nikt nie chce robić — i robi to konsekwentnie, dokładnie i bez narzekania.

Niestabilne testy to problem wszystkich. Teraz jest system, który sprawia, że nie są niczyim ciężarem.