Przejdź do treści
Vibe engineering

Vibe engineering · · 9 min czytania

Architektura systemu z agentami AI

Jak sterować agentami AI na poziomie systemu: kontrakty modułów, granice i to, co zostaje u człowieka.

Większość zespołów używa agentów AI na poziomie pliku: „dopisz endpoint”, „napraw ten test”, „dodaj walidację”. To działa, dopóki zmiana mieści się w jednym pliku. Problem zaczyna się wyżej — tam, gdzie decyzje dotyczą granic modułów, kontraktów między nimi i tego, kto za co odpowiada. Sterowanie agentem na poziomie systemu to inna dyscyplina niż sterowanie na poziomie pliku. Wymaga, żeby człowiek — senior, tech lead, architekt — utrzymał kontrolę nad rzeczami, których agent nie wymyśli sam: modelem danych, granicami i wymaganiami niefunkcjonalnymi.

Moduł i kontrakt jako jednostka pracy agenta

Plik jest złą jednostką pracy dla agenta, bo nie ma własnej semantyki. Dobrą jednostką jestmoduł z jawnym kontraktem: jasno opisanym wejściem, wyjściem, błędami i inwariantami. Kiedy mówisz agentowi „zaimplementuj moduł rozliczeń zgodnie z tym kontraktem”, dajesz mu zamkniętą przestrzeń, w której może być kreatywny bez rozlewania się po całym systemie.

W praktyce kontrakt to typy na brzegu modułu (interfejs publiczny), schemat danych wejściowych (np. Zod), zdefiniowane przypadki błędów i krótka lista inwariantów — rzeczy, które muszą być prawdziwe niezależnie od implementacji. Agent dostaje kontrakt jako wejście, a nie jako coś, co ma sam „dopowiedzieć”. To przesuwa najważniejszą decyzję — kształt granicy — z modelu na człowieka.

Jest jeszcze jedna korzyść, którą docenisz dopiero przy skali. Kiedy moduł ma zamknięty kontrakt, możesz zlecić jego implementację jednemu agentowi, a sąsiedni moduł drugiemu — równolegle, bez kolizji. Granica kontraktu jest jednocześnie granicą zrównoleglenia. Bez niej dwa agenty pracujące na tym samym obszarze zaczynają sobie nawzajem nadpisywać założenia, a ty spędzasz wieczór na scalaniu sprzecznych wizji tego samego serwisu. Kontrakt to nie biurokracja — to protokół, dzięki któremu wiele agentów (i ludzi) może pracować obok siebie bez deptania sobie po palcach.

Dlaczego jasne interfejsy ważą więcej, gdy kod pisze agent

Człowiek piszący kod nosi w głowie niewypowiedziany kontekst: wie, że ten serwis „tak naprawdę” nie powinien wołać bazy bezpośrednio, że tamten cache jest tymczasowy. Agent tego kontekstu nie ma. Czyta to, co jest zapisane w typach i sygnaturach — i traktuje to jako pełną prawdę. Jeśli interfejs jest nieostry, agent wypełni lukę najprostszym założeniem, które przejdzie kompilację, a niekoniecznie tym, które jest poprawne domenowo.

Stąd reguła: im więcej kodu pisze agent, tym ostrzejsze muszą być interfejsy. Wąskie, jawne sygnatury, brak „magicznych” parametrów typu options: any, brak ukrytych efektów ubocznych. Dobry interfejs jest dla agenta tym, czym dla człowieka dobra dokumentacja: mówi, co wolno, a czego nie. Zła granica nie spowalnia agenta — przeciwnie, agent ochoczo przekroczy ją w dziesięciu plikach naraz, zanim zauważysz.

Praktyczna konsekwencja: opłaca się robić niedozwolone stany niereprezentowalnymi w samym typie. Jeśli funkcja przyjmuje status: string, agent prędzej czy później przekaże tam „peding” z literówką i kod się skompiluje. Jeśli przyjmuje wąską unię literałów, błąd wyłapie kompilator, zanim trafi do review. Każda taka decyzja typowa to bramka, której agent nie przeskoczy — działa zawsze, bez czujności człowieka. To tańsza forma kontroli niż łapanie tego samego błędu okiem w dziesiątym PR-ze tygodnia.

Co musi pozostać w rękach człowieka

Są trzy obszary, których nie oddajesz agentowi — nie dlatego, że agent ich nie napisze, tylko dlatego, że to są decyzje, nie generacje.

  • Model danych. Kształt encji, relacje, klucze, granice spójności. To fundament, na którym stoi wszystko inne — agent może zaproponować schemat, ale akceptacja i konsekwencje migracji należą do ciebie.
  • Granice modułów. Gdzie kończy się jedna odpowiedzialność, a zaczyna druga. To decyzja o tym, co będzie tanie do zmiany, a co drogie. Agent nie widzi twojej trajektorii produktu na rok do przodu.
  • Wymagania niefunkcjonalne. Latencja, koszt LLM na żądanie, budżet pamięci, ścieżka rollback, model bezpieczeństwa. Agent domyślnie optymalizuje „działa”, nie „działa w 50 ms i kosztuje grosze”. Te ograniczenia musisz podać wprost.

Wspólny mianownik: to są rzeczy, których koszt błędu rośnie wykładniczo z czasem. Źle postawiona granica modułu w tygodniu pierwszym to refaktor jednego dnia. Ta sama granica po pół roku to przepisanie ćwiartki systemu.

Małe, recenzowalne zmiany na poziomie architektury

Pokusa przy agentach jest taka, żeby zlecić wielką zmianę — „przepisz warstwę persystencji na nowy ORM” — i dostać sto plików do review. To antywzorzec. Człowiek nie zrecenzuje stu plików rzetelnie; zrecenzuje pierwsze pięć i zaufa reszcie. A właśnie w reszcie siedzi błąd.

Zmiany architektoniczne tnij na sekwencję małych, odwracalnych kroków, z których każdy zostawia system w działającym stanie. Najpierw wprowadź nowy interfejs obok starego. Potem przełącz jeden moduł. Potem kolejny. Każdy krok to osobny PR, osobny zielony build, osobny punkt rollbacku. Agent świetnie wykonuje takie kroki — pod warunkiem że to ty narysowałeś sekwencję, a nie on. Plan migracji jest decyzją architektoniczną; wykonanie pojedynczego kroku jest pracą, którą można delegować.

Dokumentacja, którą konsumuje agent

Dokumentacja w świecie agentów przestaje być „dla nowych w zespole”. Staje się wejściem do modelu — kontekstem, na którym agent buduje decyzje. To zmienia jej formę.

  • ADR (Architecture Decision Records). Krótkie zapisy decyzji: co zdecydowano, w jakim kontekście, jakie były alternatywy i konsekwencje. Agent czytający ADR rozumie, dlaczego granica jest tu, a nie tam — i nie „poprawi” jej wbrew intencji.
  • Specyfikacje modułów. Kontrakt, inwarianty, przypadki brzegowe, jawne „out of scope”. To jest dokument, który dajesz agentowi przed implementacją, nie po.
  • Mapa granic. Jedna strona: jakie są moduły, kto z kim rozmawia, czego nie wolno wołać bezpośrednio. To kompas, który trzyma agenta w ryzach na poziomie systemu.

Dokumentacja, której agent nie czyta, jest martwa. Dokumentacja, którą podajesz mu w kontekście, staje się aktywnym mechanizmem sterowania — tańszym i szybszym niż wyłapywanie błędów na review.

Tryby awarii: agent dotyka architektury bez nadzoru

Kiedy pozwolisz agentowi podejmować decyzje architektoniczne samodzielnie, awarie mają powtarzalny kształt. Warto je znać, bo każda jest przewidywalna.

  • Erozja granic. Agent „dla wygody” woła wewnętrzny moduł z drugiego końca systemu, bo tak jest najkrócej. Po dwudziestu takich skrótach moduły są splątane, a kontrakt jest fikcją.
  • Dryf modelu danych. Każda zmiana dokłada pole „na wszelki wypadek”. Encje puchną, semantyka się rozmywa, nikt nie wie, które pole jest źródłem prawdy.
  • Niewidoczna duplikacja. Agent nie znajduje istniejącej abstrakcji, więc pisze trzecią wersję tej samej logiki. Lokalnie wygląda dobrze; globalnie masz trzy rozjeżdżające się implementacje.
  • Cicha regresja niefunkcjonalna. Kod działa, testy przechodzą, ale latencja urosła dwukrotnie albo koszt LLM na żądanie wystrzelił. Nikt nie zauważy, bo nikt nie zmierzy tego w bramce.

Wspólna przyczyna wszystkich czterech: agent optymalizuje lokalnie i krótkoterminowo, bo tyle widzi w kontekście. Architektura to optymalizacja globalna i długoterminowa — i to jest dokładnie ta część, której nie delegujesz.

Co ważne, te tryby awarii nie krzyczą. Żaden z nich nie wywróci buildu ani nie zapali czerwonej lampki w CI — bo lokalnie wszystko jest poprawne. Ujawniają się dopiero jako narastające tarcie: każda kolejna zmiana jest odrobinę droższa, każdy onboarding trochę dłuższy, każdy incydent trudniejszy do zdiagnozowania. To jest dług architektoniczny i, w odróżnieniu od długu w pojedynczym pliku, prawie nigdy nie spłaca go agent — spłaca go zespół, ręcznie, miesiącami. Dlatego nadzór nad architekturą nie jest opcją do dodania „później”; jest warunkiem tego, żeby prędkość, którą daje agent, w ogóle się utrzymała.

Jak to poukładać w praktyce

  1. Człowiek definiuje model danych i granice modułów — spisane w ADR i mapie granic.
  2. Dla każdego modułu powstaje kontrakt (typy, schemat, inwarianty, out of scope).
  3. Agent implementuje wewnątrz jednego modułu, mając kontrakt jako wejście.
  4. Zmiany architektoniczne idą jako sekwencja małych, odwracalnych PR-ów.
  5. Wymagania niefunkcjonalne mają swoje bramki (latencja, koszt, bundle) — nie tylko testy.
  6. Cross-module review robi człowiek; agent może recenzować wewnątrz modułu.

TL;DR

Sterowanie agentem na poziomie systemu różni się od poziomu pliku. Jednostką pracy jest moduł z jawnym kontraktem, nie plik. Im więcej kodu pisze agent, tym ostrzejsze muszą być interfejsy, bo agent czyta tylko to, co zapisane. Człowiek zachowuje model danych, granice modułów i wymagania niefunkcjonalne — to decyzje, nie generacje. Zmiany architektoniczne tnij na małe, odwracalne kroki, a dokumentację (ADR, specyfikacje, mapa granic) traktuj jako wejście do modelu, nie archiwum. Bez nadzoru agent eroduje granice, rozdyma model danych, duplikuje logikę i cicho psuje niefunkcjonalność — bo optymalizuje lokalnie, a architektura jest globalna.

Architektura systemu z agentami AI | vibecoding.pl