Zapytaj, dlaczego wdrożenia AI się nie udają - odpowiedź rzadko jest głośna
Obraz nieudanego systemu AI, który wszyscy nosimy w głowie, jest dramatyczny: halucynuje coś absurdalnego, ktoś robi zrzut ekranu, projekt umiera. Prawdziwe awarie na produkcji są cichsze. Model produkuje coś wiarygodnego, człowiek przelatuje to wzrokiem, wynik idzie dalej, a szkoda wychodzi dwa tygodnie później w kolejce supportu albo na zepsutej stronie, której nikt nie powiązał ze startem.
Jest powód, dla którego silniejsze modele pogarszają to, a nie poprawiają. Jak ujmuje to Atlan w swoim tekście o tym samym problemie, słaby model produkuje oczywiste błędy, a silny produkuje przekonujące. Zły wynik, który widać, jest tani do wyłapania. Zły wynik przebrany za dobry to ten drogi rodzaj, bo przechodzi review na samej płynności.
Warto oddzielić to od drugiej warstwy przyczyn. Artur Jabłoński w zestawieniu dziesięciu błędów wdrożeń AI w firmie trafnie zauważa, że AI nie naprawia złych procesów i że przedwczesna automatyzacja to częsty błąd - to problemy organizacyjne. Ten artykuł jest o czymś innym: o awarii technicznej, która przeżywa nawet dobrze poprowadzone wdrożenie. Prawie każda awaria poniżej ma ten sam korzeń. Zespół zaufał wynikowi, bo dobrze się czytał, zamiast osadzić go w realnych danych i sprawdzić, zanim wyszedł. Poprawka nigdy nie jest sprytniejszym promptem. Jest osadzeniem plus bramką.
Wiarygodny kod, który przechodzi demo i wykłada się na review
Plik Figmy to nie specyfikacja. To zbiór wizualnych wskazówek: przyciski bez nazw semantycznych, design tokens w osobnej bibliotece, reguły auto-layoutu, które znaczą co innego w różnych klatkach. Skieruj na tę niejednoznaczność zwykły model, a wypełni luki zgadywaniem. W pipeline design-to-code, który prowadzimy, wersja generyczna halucynowała nazwy klas i wymyślała propy, których nie było - a wynik wyglądał na tyle wiarygodnie, że przechodził na pierwszy rzut oka.
I to jest pułapka. Kod, który wygląda dobrze i renderuje się źle przy pierwszym uruchomieniu, jest gorszy niż brak kodu, bo ktoś musi najpierw zauważyć, że jest zepsuty, zanim go naprawi - a wtedy siedzi już w gałęzi. Zagnieżdżone komponenty zaostrzały problem: gdy karta zawiera badge, który zawiera ikonę, okno kontekstu wypełnia się szumem komponentu-rodzica, a model zaczyna wymyślać nazwy propów, żeby połączyć to, czego już wyraźnie nie widzi.
Naprawiły to dwie bramki i żadna nie jest błyskotliwa. Pierwsza to kontrola strukturalna przez Pydantic: każda nazwa komponentu, prop i import muszą się resolvować, zanim powstanie choć jeden plik, więc strukturalnie zepsuty kod nigdy nie opuszcza pipeline'u. Druga jest wizualna: skrajne przypadki auto-layoutu rozjeżdżały mniej więcej 15% klatek, więc wygenerowany kod jest renderowany ponownie headlessowo i porównywany piksel po pikselu z oryginalną klatką. Niepowodzenia wracają do kolejki z dołączonym diffem, więc model poprawia się względem konkretnej rozbieżności, a nie zgaduje ponownie. Demo nie potrzebowało nic z tego. Produkcja potrzebowała wszystkiego.
Dane były prawdziwe minutę temu - i to jest problem
Część awarii nie dotyczy błędnych faktów, tylko starych. Zbudowaliśmy głosowego inżyniera wyścigowego do sesji iRacing na żywo, który musi odpowiedzieć kierowcy w niecałe dwie sekundy, czytając telemetrię na żywo przez 19 narzędzi. Oczywisty projekt trzyma cały łańcuch wywołań narzędzi w rozmowie, tak jak chatbot trzyma swój transkrypt. Ten projekt zatruwa kolejną odpowiedź.
Czas okrążenia zebrany 30 sekund temu jest już błędny, gdy nadchodzi kolejne pytanie, ale model nie odróżni nieaktualnej liczby od świeżej - dla niego to wszystko po prostu tekst w kontekście. Więc rozumuje, pewnie, na danych, które wygasły w połowie rozmowy. Nieaktualne dane niesione dalej są gorsze niż brak danych, bo brak danych przynajmniej wymusza świeży odczyt.
Rozwiązaniem było przestać traktować historię jak magazyn danych. Utrwalane są tylko pytanie kierowcy i finalna odpowiedź - okno czterech wiadomości - a każde pośrednie wywołanie narzędzia uruchamia się na świeżo w każdej turze i jest potem odrzucane. Stan na żywo żyje całkowicie poza modelem: worker odpytuje symulator co 100 ms i zapisuje ukończone okrążenia do bazy, którą agent odpytuje na żądanie. Reguła, która uogólnia się poza wyścigi: wszystko, co wrażliwe na czas, pobiera się w momencie pytania, nigdy nie pamięta z wcześniejszej tury.
Model zmyśla liczbę, a ona wygląda prawdziwie
Najgroźniejsza rzecz, jaką produkuje model językowy, to konkretny, pewny fakt wyciągnięty z pamięci zamiast z twoich danych. W asystencie RAG nad bazą wiedzy z 500 tys. rekordów awaria była dokładna: zapytaj o drop rate, a model podawał precyzyjny procent - pewnie i precyzyjnie błędny. Ogólnikowa odpowiedź zaprasza do sprawdzenia. Precyzyjna nie zaprasza, i to właśnie czyni ją gorszą.
Poprawką jest walidacja każdej deklaracji. Każda liczba w odpowiedzi - procent, licznik czasu, wymagany poziom - jest sprawdzana względem źródłowego chunku, z którego rzekomo pochodzi, zanim odpowiedź trafi do użytkownika. Deklaracja bez możliwego do pobrania źródła jest odrzucana albo oznaczana jako niezweryfikowana, nigdy wygładzana w płynną prozę. To nie problem gier. Każda firma siedząca na dużej, ustrukturyzowanej bazie - katalogu produktów, arkuszu specyfikacji, regulaminie - spotyka tę samą awarię w chwili, gdy postawi przed nią okienko czatu: płynne odpowiedzi subtelnie błędne dokładnie na tych liczbach, na których ludzie zadziałają. W kontekście RODO czy AI Act pewny siebie, błędny numer w dokumencie to nie tylko wstyd, to problem zgodności.
Ten sam odruch zmyśla odwołania, nie tylko liczby. W pipelinie treści, który publikuje artykuły SEO, model pisał wiarygodne linki wewnętrzne do stron, które nie istniały. Bez kontroli zapełnia to serwis martwymi linkami i po cichu podkopuje zarówno SEO, jak i zaufanie czytelnika - awaria niewidoczna przy sprawdzeniu trzech artykułów i oczywista przy trzystu. Bramka działa w dwóch miejscach: przed generowaniem pobierana jest żywa sitemapa i realne adresy są wstrzykiwane do promptu, więc model linkuje do stron, które istnieją, zamiast zgadywać; po generowaniu każdy link dostaje żywe zapytanie HTTP, a wszystko, co zwraca 404 albo wskazuje poza znaną sitemapę, zostaje rozpakowane - tekst zakotwiczenia zostaje, uszkodzony link znika. Wzorzec pod oboma przypadkami to jedno zdanie. Jeśli model może coś zadeklarować, każ mu to udowodnić względem źródła.
Działa przy średnim obciążeniu i składa się w szczycie
Ostatni tryb awarii jest najbardziej podstępny, bo to ten, którego demo strukturalnie nie pokaże. System może czuć się dobrze przy średnim obciążeniu i rozpaść się w szczycie, a szczyt to zwykle jedyny moment, który się liczy. Widzieliśmy to na backendzie platformy OTT: startuje mecz na żywo i dziesiątki tysięcy widzów naciska play w tej samej minucie. Tytuł VOD może zaciąć się na sekundę i nikt nie zauważy. Mecz piłki nożnej na żywo - nie.
Naiwne rozwiązanie wykłada się jako kaskada, a nie pojedynczy punkt. Autoryzacja przelatuje aż do bazy, baza się nasyca, timeouty cofają się do serwisów, a spowolnienie rozlewa się, aż cała ścieżka play degraduje się naraz - dokładnie w trakcie wydarzenia, dla którego wszyscy się włączyli. Poprawka jest architektoniczna: Go na ścieżkach krytycznych dla latencji, Redis przed najgorętszymi danymi, żeby autoryzacja nigdy nie trafiała do bazy pod obciążeniem, i strumienie zdarzeń zamiast blokujących wywołań, żeby jeden wolny konsument w dole nie zatrzymał żądania, które widz ma przed sobą.
Skala chowa się tak samo w mniejszych systemach. Bot feedowy na Telegramie, który prowadzimy, wygląda trywialnie - odpytaj kanał, prześlij nowe pozycje - dopóki jedno ogłoszenie nie pasuje do setek użytkowników naraz i jedno zadanie nie staje się setkami wiadomości, wszystkimi do wysłania w kilka sekund i wszystkimi pod limitem tempa. Odpowiedzią była kolejka, która pochłania skok, i workery, które drenują ją w kontrolowanym tempie, ponownie kolejkując przy 429 zamiast gubić wiadomość. Miliony wiadomości dziennie, i nic z tego nie widać w teście z trzema użytkownikami. Obciążenie istnieje tylko na produkcji, więc obciążenie trzeba przetestować przed produkcją.
Co ukrywa demo, zebrane w jednej tabeli
Pięć awarii, jeden kształt. Każda wyglądała dobrze w kontrolowanym demie i pękła w kontakcie z realnymi warunkami: prawdziwą niejednoznacznością, upływającym czasem, danymi na żywo i realnym skokiem obciążenia. Zestawione obok siebie, kolumna z bramką jest tą przydatną: nic w niej nie jest lepszym modelem, wszystko jest osadzeniem i bramkowaniem wokół modelu.
| Tryb awarii | Co pokazuje demo | Co pęka na produkcji | Bramka |
|---|---|---|---|
| Wiarygodny, ale błędny wynik (design-to-code) | Czysto wyglądający kod na prostej klatce | Zmyślone propy; ~15% klatek rozjechanych | Bramka strukturalna + pętla pixel-diff |
| Nieaktualne dane w kontekście (telemetria) | Szybkie odpowiedzi na statycznym snapshocie | Rozumowanie na czasie okrążenia sprzed 30 s | Sliding window; świeży pobór w każdej turze |
| Pewna, błędna liczba (RAG nad 500 tys. rekordów) | Precyzyjna liczba do zacytowania | Liczba z pamięci, nie ze źródła | Walidacja każdej deklaracji względem źródła |
| Zmyślone odwołania (pipeline treści) | Ładnie zalinkowany artykuł | Linki wskazują na strony, które nie istnieją | Wstrzyknięcie sitemapy + sprawdzenie linków |
| Składa się w szczycie (streaming, bot feedowy) | Żwawo przy średnim obciążeniu | Kaskadowa awaria w szczycie na żywo | Cache gorącej ścieżki; kolejka i drenaż skoku |
Kolumna, która nigdy się nie zmienia, to ta środkowa. Produkcja to miejsce, gdzie niejednoznaczność, czas, realne dane i obciążenie przychodzą naraz, a demo usuwa całą czwórkę z założenia. To właśnie dlatego zaliczone demo tak mało przewiduje.
Checklista przed wdrożeniem, którą zrobisz w tydzień
Nie potrzebujesz nas ani frameworka, żeby przycisnąć wdrożenie przed startem, i nie ma znaczenia, czy zbudowałeś system, czy go kupiłeś - kupione narzędzie halucynuje tak samo jak własne. Przepuść je przez checklistę poniżej. Każdy wiersz to jedna z awarii wyżej, zamieniona w pytanie, na które odpowiesz małym eksperymentem, a nie opinią.
| Kontrola | Jak ją zrobić | Awaria, którą łapie |
|---|---|---|
| Osadzenie | Poproś o fakt, który umiesz sprawdzić, potem zapytaj, skąd pochodzi. Brak cytowalnego źródła znaczy, że zgaduje. | Pewne, błędne fakty |
| Walidacja każdej deklaracji | Podrzuć zapytanie o znanej ci odpowiedzi, z wiarygodną błędną wartością obok w danych. Zobacz, którą zwróci. | Precyzyjnie błędne liczby, podane z pełną pewnością |
| Bramka strukturalna wyniku | Podaj zniekształcone albo brzegowe wejście i potwierdź, że zepsuty wynik jest odrzucany, nie publikowany. | Wiarygodnie zepsuty wynik idący dalej |
| Weryfikacja wyniku | Dla wszystkiego, na co system się powołuje - link, ID, plik - potwierdź, że cel istnieje po generacji. | Zmyślone odwołania |
| Obsługa nieaktualnych danych | Zadaj dwa pytania z rzędu, gdzie odpowiedź zmienia się między nimi. Potwierdź, że drugie nie rozumuje z pierwszego. | Stare dane niesione dalej |
| Test szczytu obciążenia | Odtwórz swoją realną najgorszą minutę, nie średnią - skok na starcie, wydarzenie na żywo, wsad na koniec miesiąca. | Systemy, które składają się w szczycie |
Zrób to przed startem, a większość awarii z tego tekstu nigdy nie dotrze do użytkownika. Pomiń, a dostajesz ten cichy rodzaj awarii, który wychodzi czysto i wypływa tygodnie później. (To robota, którą robimy na co dzień, tak swoją drogą - ale checklista broni się sama, kto by jej nie zrobił.)
Jeśli chcesz drugą parę oczu na to, gdzie konkretne wdrożenie najpewniej pęknie, krótki audyt to niskostawkowy sposób, żeby ją mieć: wychodzisz z dwoma czy trzema trybami awarii, które najpewniej ugryzą twój system, przypiętymi do kontroli wyżej.
FAQ
Dlaczego agenty AI przechodzą demo, a potem wykładają się na produkcji?
Demo działa na dobranych wejściach, statycznym snapshocie i jednym użytkowniku. Produkcja dokłada realną niejednoznaczność, dane na żywo, które się starzeją, fakty, które model woli zmyślić, i skok obciążenia - a żadnego z tego demo nie ćwiczy. Wynik, który na scenie wyglądał płynnie i poprawnie, to ten sam płynny wynik, który teraz jest pewny siebie i błędny, tylko nikt nie zbudował bramki, żeby go złapać.
Czy większy albo nowszy model to rozwiązanie?
Rzadko. Silniejszy model zwykle robi błędne odpowiedzi bardziej przekonującymi, a nie rzadszymi, więc łatwiej przechodzą przez review człowieka. Trwałe poprawki są architektoniczne: osadź odpowiedź w pobranych danych, zweryfikuj każdą deklarację względem źródła i odrzuć wynik, który się nie resolvuje. To trzyma się niezależnie od tego, jaki model stoi z tyłu.
Którą bramkę dodać jako pierwszą, jeśli mamy dodać jedną?
Osadzanie każdej deklaracji, jeśli twój system podaje fakty albo liczby. Zmuś każdą deklarację, żeby prowadziła do pobranego źródła, i odrzuć albo oznacz to, co nie prowadzi. Usuwa to najdroższą awarię - pewną, precyzyjną, błędną odpowiedź - i przy okazji jest twoim śladem audytowym, gdy ktoś zapyta, skąd wzięła się liczba.
Jak przetestować awarię szczytu obciążenia, zanim się wydarzy?
Odtwórz swoją realną najgorszą minutę, nie syntetyczną średnią. Weź zeszłomiesięczny skok na starcie, okno wydarzenia na żywo albo wsad na koniec miesiąca jako kształt testu. Awarie kaskadowe - autoryzacja przelatująca do bazy, cofające się timeouty - pojawiają się tylko blisko szczytu, więc test obciążenia zbudowany wokół średniego ruchu przejdzie, a produkcja i tak się złoży.
- 17 lipca 2026Publikacja.