Jak tworzymy oprogramowanie z agentami AI (i gdzie nas to zawiodło)
Jak realnie używamy agentów AI przy tworzeniu oprogramowania — orkiestrator + workerzy, reference discovery, workflow spec-driven. I war story o tym, jak popularna abstrakcja .NET po cichu wyłączyła prompt caching, a my płaciliśmy bez potrzeby.
Rok temu byłem sceptyczny. Nie wobec samej AI — wobec tego, jak się o niej mówiło. „AI zastąpi programistów". „Wystarczy prompt". „Copilot napisze to za ciebie". Potem zacząłem realnie używać agentów AI we własnych projektach, obserwować, co działa, a co nie, i obraz się skomplikował.
Ten artykuł nie jest o tym, jaka AI jest wspaniała. Jest o tym, jak konkretnie wpinamy ją w tworzenie oprogramowania, oraz o jednym incydencie, który kosztował nas więcej pieniędzy, niż było trzeba — i nauczył nas inaczej testować abstrakcje.
Architektura, której używamy: orkiestrator + workerzy
Nasze workflow AI ma dwie warstwy.
Silny model (orkiestrator) trzyma całościowy kontekst. Planuje. Robi reference discovery — przechodzi kod i znajduje WSZYSTKIE dotknięte miejsca przed każdą zmianą. Recenzuje wyniki pozostałych agentów. Decyduje, co jest edge case, a co halucynacją. Ta praca kosztuje tokeny, ale się opłaca — silny model robi tu dokładnie to, w czym jest dobry.
Słabsze, tańsze modele (workerzy) dostają wyizolowane, dobrze zdefiniowane kroki. Dodaj import do 15 plików. Wygeneruj test scaffold według tego wzoru. Zescrapuj wyniki builda. Przepisz DTO według nowej struktury. Te kroki są dobrze specyfikowalne, nie wymagają cross-file reasoning i można je uruchomić równolegle. Workerzy są tani i szybcy — a jeśli któryś popełni błąd, orkiestrator wyłapie go w review.
To ta sama zasada co w zespołach ludzkich: senior trzyma architekturę i decyzje, reszta wykonuje dobrze zdefiniowane kawałki. Różnica jest w tempie — równolegli workerzy przetwarzają w minutę pracę, która jednemu człowiekowi zajęłaby godzinę.
Ale działa to tylko wtedy, gdy praca jest naprawdę dobrze wyspecyfikowana. Niejasne zadanie daje niejasny wynik. To dotyczy juniora i dotyczy też AI.
Reference discovery: nauka z realnego incydentu
Jedna z pierwszych lekcji, których nauczyli nas agenci AI, nie była o AI. Była o dyscyplinie.
Przy podłączaniu handlera SignalR do frontendu pozwoliliśmy agentowi wykonać zmianę. Zmiana wyglądała poprawnie. Testy przeszły. Dopiero po trzech dniach okazało się, że wiring działa w trzech z ośmiu miejsc — resztę agent po cichu pominął, bo nie była w tym samym pliku.
Od tamtej pory obowiązuje zasada: przed każdą zmianą znajdujemy WSZYSTKIE miejsca, których ona dotyczy. Dopiero potem zmieniamy. Agenci potrafią wyszukać referencje szybko i dokładnie — ale musimy im to wprost zlecić. Bez tego optymalizują pod to, co widzą, a nie pod to, co istnieje.
Reference discovery to nuda. To praca, która nie kręci się w demo video. Ale to różnica między zmianą, która działa, a zmianą, która wygląda, że działa.
Spec przed kodem, nie po
Drugie przesunięcie było w kolejności pracy. Wcześniej pisaliśmy specyfikację jako dokumentację po fakcie — o ile w ogóle. Agenci AI to pogorszyli: z AI da się wygenerować kod szybko, ale szybko wygenerowany kod bez specyfikacji to szybko wygenerowany problem.
Teraz obowiązuje zasada: spec to umowa przed startem, a nie opis po zakończeniu. Spec mówi, co musi być spełnione, dlaczego i jakie decyzje za tym stoją. Kod to tylko implementacja uzgodnionego zachowania.
Korzyść: gdy agent zaproponuje rozwiązanie, które pasuje do spec, to dobra propozycja. Gdy nie, wiemy dlaczego nie. Bez spec każdą halucynację AI trzeba tłumaczyć całym kontekstem — a to podnosi koszt każdej rundy review.
War story: jak Microsoft.Extensions.AI po cichu okradło nas z pieniędzy
To centralna historia artykułu, bo ilustruje kategorię błędów, na którą abstrakcje AI nie uważają.
Pracujemy z wieloma dostawcami LLM jednocześnie — Claude, OpenAI, Gemini, Grok — pod jedną abstrakcją. Microsoft.Extensions.AI to rozsądny wybór dla ujednoliconego interfejsu: jeden interface dla wielu providerów, wymiana konfiguracji, brak vendor lock-in na poziomie kodu.
Tyle że zauważyliśmy, że koszty tokenów nie spadają tak, jak powinny. Mieliśmy ustawiony prompt caching dla system message — co przy Claude Sonnet i Opus oszczędza do 90 % ceny przy powtarzanych wywołaniach. Model pracuje z długimi promptami systemowymi, więc caching to u nas materialna różnica.
Tyle że caching nie działał. Nie wprost — żadnego komunikatu o błędzie, żadnego warninga. Tokeny po prostu liczyły się po pełnej cenie.
Przez chwilę szukaliśmy konfiguracji. Potem napisaliśmy test integracyjny, który porównał oczekiwane cache hity z realnymi — i test się wywalił. Żadnych cache hitów. Przy żadnym wywołaniu.
Przyczyna: Microsoft.Extensions.AI w tej wersji po cichu dropowało parametr cache_control, którego Anthropic API wymaga do oznaczenia tokenów do cache'owania. Abstrakcja go ignorowała — albo go nie wspierała, albo nie propagowała do raw requestu. Z punktu widzenia kodu wszystko wyglądało poprawnie. Z punktu widzenia provider API wysyłaliśmy requesty bez instrukcji cachowania.
Rozwiązanie było proste: u tego klienta obeszliśmy abstrakcję i poszliśmy na raw endpoint dostawcy z fine-grained kontrolą. Prompt caching ma model-specyficzne minimalne progi — około 1024 tokenów dla Sonnet i Opus, 2048 dla Haiku. Musisz oznaczyć właściwe bloki, we właściwej kolejności, z właściwą minimalną długością. Abstrakcja ten detal ukrywała — i ukryła go źle.
Lekcja: Abstrakcja może po cichu ukraść ci zachowanie. Nie wystarczy przetestować happy path — trzeba testować granicę. Przy krytycznej integracji (cena, bezpieczeństwo, stan) chcesz wiedzieć, co realnie przechodzi przez drut, a nie co mówi high-level API.
Dziś mamy test integracyjny, który u każdego providera weryfikuje, że cache hity odpowiadają oczekiwaniom. Nie wystarczy, że wywołanie nie pada. Musi przebiec tak, jak chcieliśmy.
Co AI realnie przyspiesza — a czego nie
Po roku pracy z agentami AI mam dość danych na trzeźwe porównanie.
AI przyspiesza:
- Dobrze wyspecyfikowaną, powtarzalną pracę. Boilerplate, mappery, testy według wzoru, rename w wielu plikach.
- Eksplorację kodu. Znaleźć, gdzie co jest zdefiniowane, jakie są wszystkie referencje, jaki wzorzec jest używany gdzie indziej w projekcie.
- Pierwszy draft. README, spec, opis PR, migration script. Wynik wymaga review, ale punkt startowy jest szybszy.
AI nie przyspiesza:
- Architektury. Która abstrakcja jest właściwa, jak rozdzielić odpowiedzialności, gdzie będzie bottleneck przy skalowaniu — to wymaga osądu, którego AI nie ma.
- Integracji. Gdzie dokładnie stykają się dwa systemy, co się stanie przy stanie błędu, jakie jest realne zachowanie strony trzeciej w przeciwieństwie do tego, co mówi dokumentacja — AI halucynuje, nie mierzy.
- Diagnostyki. Dlaczego produkt nie zawiera cache hitów? Dlaczego latencja jest wyraźnie wyższa niż w dev? AI zgaduje. Senior inżynier trasuje.
Najgroźniejsza kombinacja to junior programista i AI bez senior review. Wynik wygląda poprawnie. Ma typy, ma testy, przechodzi PR. I zawiera bug, który ujawni się dopiero na produkcji, bo model nie wiedział, czego nie wie — a junior tego nie wyłapał, bo nie wyłapie tego AI.
Senior review to ten mnożnik. Nie kontrola z braku zaufania — to warstwa osądu, której AI nie dostarczy.
Gdzie jesteśmy dziś
W projektach działamy z wieloma dostawcami LLM pod jedną abstrakcją i z własnymi serwerami MCP, które wystawiają nasze systemy agentom AI — katalogi, workflow, dane biznesowe. Każde wywołanie LLM ma budget gate i audit log. Koszty są przewidywalne i mierzalne, a nie wynikiem promptu bez zastanowienia.
Ale najcenniejsza rzecz, której się nauczyliśmy, nie jest techniczna. To dyscyplina: reference discovery przed każdą zmianą, spec przed kodem, test integracyjny dla każdej krytycznej abstrakcji. Z AI albo bez niej.
FAQ
Jakiego modelu używacie jako orkiestratora?
Aktualnie Claude Opus lub Sonnet do orkiestracji (duży kontekst, dobra reference discovery), tańsze modele do wyizolowanych kroków. Konkretny wybór zmienia się z każdą nową wersją — testujemy i przewartościowujemy.
Jak testujecie, że prompt caching realnie działa?
Test integracyjny, który robi dwa identyczne wywołania po sobie, porównuje cache_read_input_tokens w metadanych response z input_tokens i weryfikuje, że drugie wywołanie miało cache hit. Jeśli nie, test pada — i wiemy o tym wcześniej, niż kosztuje nas to pieniądze.
Kiedy odradzacie agentów AI?
Przy pracy, gdzie kluczowy jest osąd w kontekście, którego agent nie ma. Architektura bezpieczeństwa, migracja bazy danych z produkcją na drucie, strojenie realnego wydajności — tu AI jest najwyżej pomocnikiem do eksploracji, a nie decydentem. I zawsze, gdy wynik idzie wprost na produkcję bez senior review.