{"id":35091,"date":"2026-11-10T05:00:00","date_gmt":"2026-11-10T04:00:00","guid":{"rendered":"https:\/\/sii.pl\/blog\/?p=35091"},"modified":"2026-11-09T16:20:41","modified_gmt":"2026-11-09T15:20:41","slug":"deterministyczne-testy-aplikacji-z-llm-jak-kasety-vcr-ograniczaja-koszty-i-niestabilnosc-ci","status":"publish","type":"post","link":"https:\/\/sii.pl\/blog\/deterministyczne-testy-aplikacji-z-llm-jak-kasety-vcr-ograniczaja-koszty-i-niestabilnosc-ci\/","title":{"rendered":"Deterministyczne testy aplikacji z LLM: Jak kasety VCR ograniczaj\u0105 koszty i niestabilno\u015b\u0107 CI"},"content":{"rendered":"\n<p>Coraz wi\u0119cej odpowiedzialno\u015bci za wyszukiwanie i interpretowanie informacji powierzamy sztucznej inteligencji. Przez dobrze ju\u017c znane okienko ulubionego czatu pytamy o coraz bardziej skomplikowane tematy: analiz\u0119 prawn\u0105, kwestie ksi\u0119gowe czy porad\u0119, co mo\u017ce oznacza\u0107 dziwny stukot w samochodzie.<\/p>\n\n\n\n<p>Modele j\u0119zykowe posiadaj\u0105 ogromn\u0105 ilo\u015b\u0107 wiedzy zdobytej podczas treningu, ale nie s\u0105 przecie\u017c encyklopedi\u0105, w kt\u00f3rej ka\u017cda informacja zosta\u0142a zapisana w oryginalnej postaci. Wiedza jest rozproszona w parametrach modelu, a jej odtworzenie nie zawsze jest dok\u0142adne.<\/p>\n\n\n\n<p>Dlatego wsp\u00f3\u0142czesne aplikacje AI coraz cz\u0119\u015bciej korzystaj\u0105 r\u00f3wnie\u017c z wyszukiwarek internetowych, zewn\u0119trznych narz\u0119dzi i baz wiedzy, dostarczaj\u0105c modelowi informacje bezpo\u015brednio w trakcie rozmowy. Ten sprytny mechanizm nadal mo\u017ce jednak nie wystarczy\u0107, poniewa\u017c dane w sieci bywaj\u0105 b\u0142\u0119dne, nieaktualne lub przedstawione w spos\u00f3b, kt\u00f3rego model nie zinterpretuje poprawnie.<\/p>\n\n\n\n<p>Nic dziwnego \u2013 wi\u0119kszo\u015b\u0107 tre\u015bci powstawa\u0142a z my\u015bl\u0105 o ludziach, a nie o maszynach. Dlatego nawet bardzo zaawansowany model, kt\u00f3ry \u015bwietnie radzi sobie z programowaniem czy z\u0142o\u017conymi problemami matematycznymi, mo\u017ce pomyli\u0107 si\u0119 przy odczytywaniu planu lekcji z tabeli na stronie pobliskiego liceum. Tw\u00f3rcy modeli stale poprawiaj\u0105 ich zdolno\u015b\u0107 rozumienia kontekstu, rozpoznawania wiarygodnych informacji i radzenia sobie z niejednoznacznym tekstem.<\/p>\n\n\n\n<p>Mimo to nadal zdarza si\u0119, \u017ce model, maj\u0105cy wszystkie potrzebne dane pod r\u0119k\u0105, wyci\u0105ga z nich b\u0142\u0119dne wnioski, pomija drobny warunek albo po prostu wymy\u015bla odpowied\u017a.<\/p>\n\n\n\n<p>Problem zauwa\u017cy\u0142 ju\u017c pewnie ka\u017cdy, kto korzysta\u0142 z AI nieco d\u0142u\u017cej. Dlatego przy wdra\u017caniu firmowych baz wiedzy zwykle zachowujemy ostro\u017cno\u015b\u0107 i wymagamy, aby istotne informacje otrzymane od modelu by\u0142y dodatkowo weryfikowane. W przeciwnym razie system pozostaje przede wszystkim pomocnikiem dostarczaj\u0105cym wst\u0119pny research, a do obiecywanych w reklamach wirtualnych pracownik\u00f3w i wszechwiedz\u0105cych asystent\u00f3w nadal jest mu daleko.<\/p>\n\n\n\n<p>Czy to oznacza, \u017ce AI nigdy nie zast\u0105pi cz\u0142owieka? Tego nie wiem. Mam natomiast pomys\u0142, co mo\u017cemy zrobi\u0107, aby sprawdzi\u0107, czy nasz system poprawnie interpretuje firmowe procedury, wykrywa\u0107 problemy jeszcze przed wdro\u017ceniem i ograniczy\u0107 liczb\u0119 niespodzianek po kolejnej aktualizacji dokumentacji.<\/p>\n\n\n\n<p>Mo\u017cemy do tego wykorzysta\u0107 dobrze znane z testowania API kasety VCR, kt\u00f3re pozwalaj\u0105 nagrywa\u0107 odpowiedzi modeli i odtwarza\u0107 je podczas kolejnych test\u00f3w. Podczas nagrywania sprawdzimy, czy model poprawnie interpretuje procedury, a p\u00f3\u017aniej wykorzystamy zaakceptowane odpowiedzi do regresji naszego kodu.<\/p>\n\n\n\n<p>Co wi\u0119cej, mo\u017cemy zrobi\u0107 to stosunkowo tanio, bez odpytywania modeli przy ka\u017cdym uruchomieniu CI\/CD i bez przechowywania kluczy API w zwyk\u0142ych testach regresyjnych.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>Za\u0142o\u017cenie<\/strong><\/h2>\n\n\n\n<p>Za\u0142\u00f3\u017cmy, \u017ce przygotowujemy bota obs\u0142uguj\u0105cego wewn\u0119trzny helpdesk. Firma posiada procedury dotycz\u0105ce resetowania hase\u0142, konfiguracji VPN, drukowania dokument\u00f3w i zg\u0142aszania problem\u00f3w ze sprz\u0119tem. Chcemy, aby pracownik m\u00f3g\u0142 opisa\u0107 sw\u00f3j problem w\u0142asnymi s\u0142owami, a bot znalaz\u0142 odpowiedni\u0105 procedur\u0119 i wskaza\u0142 mu, co powinien zrobi\u0107. Je\u017celi sprawa wymaga interwencji administratora, bot powinien przekaza\u0107 zg\u0142oszenie do w\u0142a\u015bciwej osoby.<\/p>\n\n\n\n<p>W teorii wystarczy udost\u0119pni\u0107 modelowi dokumentacj\u0119 i przygotowa\u0107 odpowiedni prompt systemowy. W praktyce musimy jeszcze wiedzie\u0107, czy bot prawid\u0142owo rozumie zasady, czy potrafi wybra\u0107 w\u0142a\u015bciw\u0105 procedur\u0119 i czy po zmianie jednego dokumentu nie zacznie udziela\u0107 b\u0142\u0119dnych odpowiedzi na pytania, z kt\u00f3rymi wcze\u015bniej radzi\u0142 sobie bez problemu.<\/p>\n\n\n\n<p>B\u0119dziemy wi\u0119c potrzebowali dw\u00f3ch uzupe\u0142niaj\u0105cych si\u0119 sposob\u00f3w testowania:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>rzeczywistych wywo\u0142a\u0144 modelu, pozwalaj\u0105cych ocenia\u0107 jako\u015b\u0107 jego odpowiedzi<\/li>\n\n\n\n<li>oraz powtarzalnych test\u00f3w aplikacji na zapisanych odpowiedziach.<\/li>\n<\/ul>\n\n\n\n<p>Do tego drugiego zadania wykorzystamy VCR.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>Przypadki testowe<\/strong><\/h2>\n\n\n\n<p>Do naszej bazy wiedzy przygotujmy przypadki testowe. Mo\u017cemy wygenerowa\u0107 je na podstawie dokumentacji za pomoc\u0105 AI, ale niekoniecznie b\u0119d\u0105 one odpowiada\u0142y temu, o co rzeczywi\u015bcie pytaj\u0105 u\u017cytkownicy. Model prawdopodobnie dobrze poradzi sobie z przygotowaniem pyta\u0144 dotycz\u0105cych informacji, kt\u00f3re znalaz\u0142 w procedurach.<\/p>\n\n\n\n<p>Znacznie trudniej przewidzie\u0107 pytania zawieraj\u0105ce niedopowiedzenia, liter\u00f3wki, potoczne okre\u015blenia albo kilka problem\u00f3w opisanych jednocze\u015bnie.<\/p>\n\n\n\n<p>Pracownik rzadko napisze przecie\u017c: \u201eProsz\u0119 o przedstawienie procedury diagnostycznej dotycz\u0105cej lokalnej kolejki wydruku i komunikacji ze wsp\u00f3\u0142dzielon\u0105 drukark\u0105\u201d.<\/p>\n\n\n\n<p>Znacznie bardziej prawdopodobne, \u017ce zapyta: \u201eDrukarka drukuje stron\u0119 testow\u0105 z panelu, ale dokumenty z laptopa stoj\u0105 w kolejce. Co mam zrobi\u0107?\u201d.<\/p>\n\n\n\n<p>Cz\u0142owiek znaj\u0105cy firmowe procedury zauwa\u017cy, \u017ce skoro urz\u0105dzenie potrafi samodzielnie wydrukowa\u0107 stron\u0119 testow\u0105, a dokumenty zatrzymuj\u0105 si\u0119 na komputerze, warto rozpocz\u0105\u0107 diagnostyk\u0119 od kolejki drukowania, sterownika albo po\u0142\u0105czenia z drukark\u0105. Model musi zauwa\u017cy\u0107 dok\u0142adnie ten sam szczeg\u00f3\u0142, zamiast automatycznie uzna\u0107, \u017ce urz\u0105dzenie jest zepsute i zaleci\u0107 jego ponowne uruchomienie. Taka odpowied\u017a mog\u0142aby wygl\u0105da\u0107 rozs\u0105dnie, ale nie by\u0142aby zgodna z kolejno\u015bci\u0105 diagnostyki opisan\u0105 w naszej procedurze.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\"><strong>Dobre praktyki<\/strong><\/h3>\n\n\n\n<p>Dlatego dobr\u0105 praktyk\u0105 jest zbieranie rzeczywistych pyta\u0144 u\u017cytkownik\u00f3w, na przyk\u0142ad z istniej\u0105cego systemu zg\u0142oszeniowego, komunikatora lub historii wyszukiwania w firmowej bazie wiedzy. Mo\u017cemy te\u017c zapisywa\u0107 pytania kierowane do nowego bota, oczywi\u015bcie po odpowiedniej anonimizacji i zgodnie z zasadami przetwarzania danych w organizacji.<\/p>\n\n\n\n<p>Z czasem uzyskamy zbi\u00f3r pokazuj\u0105cy nie tylko to, co znajduje si\u0119 w dokumentacji, ale przede wszystkim to, jak pracownicy pr\u00f3buj\u0105 z niej korzysta\u0107. Do takich pyta\u0144 warto do\u0142o\u017cy\u0107 przypadki wygenerowane przez model:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>inne sposoby sformu\u0142owania tego samego problemu,<\/li>\n\n\n\n<li>pytania z brakuj\u0105cymi informacjami,<\/li>\n\n\n\n<li>sytuacje wymagaj\u0105ce po\u0142\u0105czenia kilku procedur,<\/li>\n\n\n\n<li>przypadki, w kt\u00f3rych bot powinien poprosi\u0107 o doprecyzowanie albo przekaza\u0107 spraw\u0119 cz\u0142owiekowi.<\/li>\n<\/ul>\n\n\n\n<p>Wygenerowane scenariusze s\u0105 jednak propozycjami do sprawdzenia, a nie automatycznie poprawnymi testami. Model mo\u017ce nie zauwa\u017cy\u0107 przypadk\u00f3w, kt\u00f3rych nie opisano w dokumentacji, dlatego rzeczywiste zg\u0142oszenia i wiedza pracownik\u00f3w helpdesku nadal s\u0105 potrzebne.<\/p>\n\n\n\n<p>Ka\u017cdy przypadek testowy powinien mie\u0107 pytanie, zestaw informacji dost\u0119pnych dla modelu oraz oczekiwany rezultat. Nie zawsze musimy por\u00f3wnywa\u0107 dos\u0142own\u0105 tre\u015b\u0107 odpowiedzi, poniewa\u017c model mo\u017ce przekaza\u0107 t\u0119 sam\u0105 informacj\u0119 na wiele sposob\u00f3w.<\/p>\n\n\n\n<p>Znacznie lepiej sprawdza\u0107 konkretne decyzje, kt\u00f3re podejmuje nasza aplikacja. W przypadku drukarki, kt\u00f3ra wykonuje wydruk testowy z w\u0142asnego panelu, ale nie drukuje dokument\u00f3w wysy\u0142anych z laptopa, oczekujemy rozpoznania problemu po stronie komputera lub komunikacji z urz\u0105dzeniem i rozpocz\u0119cia diagnostyki od kolejki drukowania. Nie ma znaczenia, czy model napisze \u201esprawd\u017a kolejk\u0119 wydruku\u201d, czy \u201eotw\u00f3rz list\u0119 dokument\u00f3w oczekuj\u0105cych na wydruk\u201d.<\/p>\n\n\n\n<p>Mo\u017cemy wymaga\u0107 od modelu ustrukturyzowanej odpowiedzi, zawieraj\u0105cej kategori\u0119 zg\u0142oszenia i proponowan\u0105 akcj\u0119. Wtedy test nie musi rozstrzyga\u0107, kt\u00f3re zdanie brzmi \u0142adniej. Sprawdza, czy system podj\u0105\u0142 w\u0142a\u015bciw\u0105 decyzj\u0119.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\"><strong>Przyk\u0142adowy scenariusz<\/strong><\/h3>\n\n\n\n<p>Przyk\u0142adowy scenariusz mo\u017cemy zapisa\u0107 w pliku <code>PRINT-003.yaml<\/code>. Dla uproszczenia umie\u015bcimy w nim zar\u00f3wno pytanie, jak i niewielki fragment firmowej procedury. W wi\u0119kszej aplikacji dokumentacja prawdopodobnie b\u0119dzie przechowywana osobno, dlatego scenariusze warto wi\u0105za\u0107 z identyfikatorami i wersjami wykorzystywanych dokument\u00f3w.<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: xml; title: ; notranslate\" title=\"\">\nid: PRINT-003\n\nprocedure:\n  id: printing\n  version: 3\n  text: &gt;\n    Je\u015bli drukarka drukuje stron\u0119 testow\u0105 ze swojego panelu,\n    ale dokumenty z komputera pozostaj\u0105 w kolejce,\n    najpierw sprawd\u017a kolejk\u0119 wydruku. Je\u015bli to nie pomo\u017ce,\n    sprawd\u017a po\u0142\u0105czenie komputera z drukark\u0105.\n    Je\u015bli drukarka nie drukuje nawet strony testowej,\n    zacznij diagnostyk\u0119 od samego urz\u0105dzenia.\n\nquestion: &gt;\n  Drukarka drukuje stron\u0119 testow\u0105 z panelu,\n  ale dokumenty z laptopa stoj\u0105 w kolejce.\n  Co mam zrobi\u0107?\n\nexpected:\n  category: client_printing_issue\n  action: check_print_queue\n<\/pre><\/div>\n\n\n<p>Przygotowuj\u0105c takie przypadki, mo\u017cemy odkry\u0107 jeszcze jeden problem.<\/p>\n\n\n\n<p>Firmowe procedury, nawet je\u015bli s\u0105 poprawne z punktu widzenia ich autor\u00f3w, nie zawsze s\u0105 jednoznaczne. Jedna mo\u017ce m\u00f3wi\u0107, aby przy problemach z drukowaniem ponownie uruchomi\u0107 drukark\u0119, a druga zaleca\u0107 sprawdzenie kolejki i sterownik\u00f3w, je\u017celi urz\u0105dzenie potrafi wykona\u0107 wydruk testowy.<\/p>\n\n\n\n<p>Pracownik helpdesku prawdopodobnie domy\u015bli si\u0119, \u017ce w opisanym przypadku nale\u017cy zacz\u0105\u0107 od drugiej procedury. Model nie musi tego wiedzie\u0107, je\u017celi nigdzie nie zapisali\u015bmy takiej zale\u017cno\u015bci.<\/p>\n\n\n\n<p>Testy pomagaj\u0105 wi\u0119c nie tylko sprawdzi\u0107 jako\u015b\u0107 odpowiedzi, ale r\u00f3wnie\u017c znale\u017a\u0107 miejsca, w kt\u00f3rych sama baza wiedzy wymaga doprecyzowania. Gdy zauwa\u017cymy, \u017ce r\u00f3\u017cne modele inaczej interpretuj\u0105 ten sam przypadek, otrzymujemy dodatkowy sygna\u0142 do przeanalizowania procedury. Mo\u017cemy w\u00f3wczas sprawdzi\u0107, czy problem wynika z niejednoznacznego dokumentu, sposobu wyszukiwania informacji, promptu systemowego czy mo\u017cliwo\u015bci konkretnego modelu.<\/p>\n\n\n\n<p>W ten spos\u00f3b poprawiamy nie tylko testy, ale r\u00f3wnie\u017c jako\u015b\u0107 wiedzy, na kt\u00f3rej b\u0119dzie pracowa\u0142 nasz asystent.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>Kasety VCR<\/strong><\/h2>\n\n\n\n<p>Je\u017celi VCR kojarzy si\u0119 z magnetowidem i kasetami VHS, to dobre skojarzenie. Skr\u00f3t pochodzi od <em>Video Cassette Recorder<\/em>, a w naszym przypadku kaseta b\u0119dzie zawiera\u0142a zapisan\u0105 komunikacj\u0119 z API. Tak jak kiedy\u015b mogli\u015bmy nagra\u0107 transmisj\u0119 telewizyjn\u0105 i obejrze\u0107 j\u0105 p\u00f3\u017aniej, tak samo mo\u017cemy zapisa\u0107 odpowied\u017a otrzyman\u0105 od modelu i odtworzy\u0107 j\u0105 podczas nast\u0119pnego testu.<\/p>\n\n\n\n<p>Pomys\u0142 nagrywania komunikacji z API nie jest niczym nowym. Istniej\u0105 biblioteki rozwijane od lat, kt\u00f3re pozwalaj\u0105 przechwyci\u0107 \u017c\u0105danie HTTP wraz z odpowiedzi\u0105, zapisa\u0107 je w pliku, a nast\u0119pnie odtworzy\u0107 podczas kolejnego uruchomienia testu. Skoro wi\u0119kszo\u015b\u0107 modeli udost\u0119pniana jest przez API, mo\u017cemy wykorzysta\u0107 dok\u0142adnie ten sam mechanizm. Nie potrzebujemy do tego specjalnego modelu ani dostawcy oferuj\u0105cego osobn\u0105 funkcj\u0119 testowania. Wystarczy, \u017ce potrafimy kontrolowa\u0107 komunikacj\u0119 HTTP wykonywan\u0105 przez nasz\u0105 aplikacj\u0119.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\"><strong>Dzia\u0142anie<\/strong><\/h3>\n\n\n\n<p>Podczas nagrywania test wysy\u0142a do API modelu pytanie u\u017cytkownika, odpowiednie fragmenty firmowej dokumentacji i parametry \u017c\u0105dania. Otrzymana odpowied\u017a trafia do kasety, a aplikacja przetwarza j\u0105 tak samo jak podczas normalnej pracy. Mo\u017cemy od razu sprawdzi\u0107, czy model poprawnie zinterpretowa\u0142 procedur\u0119, a zaakceptowan\u0105 odpowied\u017a zachowa\u0107 do kolejnych test\u00f3w. Przy p\u00f3\u017aniejszych uruchomieniach biblioteka rozpoznaje wcze\u015bniej zapisane \u017c\u0105danie i zwraca nagran\u0105 odpowied\u017a bez kontaktowania si\u0119 z dostawc\u0105.<\/p>\n\n\n\n<p>Mo\u017cemy wi\u0119c uruchamia\u0107 ten sam test wielokrotnie, nie p\u0142ac\u0105c ponownie za inferencj\u0119 i nie zastanawiaj\u0105c si\u0119, czy model tym razem u\u017cyje innego sformu\u0142owania. Powtarzalno\u015b\u0107 dotyczy tutaj odtworzonej komunikacji. Dzi\u0119ki niej sprawdzamy, czy nasz kod nadal prawid\u0142owo przetwarza wcze\u015bniej zweryfikowane odpowiedzi, podczas gdy osobne testy wykonywane na rzeczywistym API pozwalaj\u0105 ocenia\u0107 aktualne zachowanie modelu. Ten podzia\u0142 jest korzystny r\u00f3wnie\u017c podczas debugowania, poniewa\u017c \u0142atwiej ustali\u0107, czy problem powsta\u0142 w aplikacji, czy podczas generowania odpowiedzi.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\"><strong>Ustalenie dopasowania kasety<\/strong><\/h3>\n\n\n\n<p>Wa\u017cne jest r\u00f3wnie\u017c ustalenie, kiedy kaseta nadal pasuje do wykonywanego testu. Nie wystarczy zapami\u0119ta\u0107 adresu API, poniewa\u017c wszystkie pytania mog\u0105 by\u0107 wysy\u0142ane pod ten sam endpoint.<\/p>\n\n\n\n<p>Musimy uwzgl\u0119dni\u0107 przynajmniej tre\u015b\u0107 \u017c\u0105dania:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>prompt systemowy,<\/li>\n\n\n\n<li>pytanie u\u017cytkownika,<\/li>\n\n\n\n<li>fragmenty firmowych procedur,<\/li>\n\n\n\n<li>identyfikator modelu,<\/li>\n\n\n\n<li>parametry wp\u0142ywaj\u0105ce na generowanie odpowiedzi.<\/li>\n<\/ul>\n\n\n\n<p>W API typu Chat Completions s\u0105 one zwykle przesy\u0142ane razem w JSON-ie, lecz w niekt\u00f3rych integracjach cz\u0119\u015b\u0107 konfiguracji wynika tak\u017ce z adresu endpointu. Mo\u017cemy obliczy\u0107 skr\u00f3t, czyli fingerprint, z uporz\u0105dkowanej reprezentacji tych danych i wykorzysta\u0107 go do identyfikacji nagrania. Dzi\u0119ki temu zmiana pytania lub procedury nie zostanie przypadkowo obs\u0142u\u017cona odpowiedzi\u0105 przygotowan\u0105 dla poprzedniej wersji.<\/p>\n\n\n\n<p>W bardziej rozbudowanym rozwi\u0105zaniu warto zapisywa\u0107 r\u00f3wnie\u017c wersj\u0119 \u017ar\u00f3d\u0142a wiedzy, konfiguracj\u0119 modelu i informacje o tym, kto zatwierdzi\u0142 nagran\u0105 odpowied\u017a. Nie ka\u017cda techniczna r\u00f3\u017cnica w serializacji JSON powinna jednak oznacza\u0107 zmian\u0119 scenariusza. Biblioteki VCR mog\u0105 por\u00f3wnywa\u0107 przetworzon\u0105 tre\u015b\u0107 \u017c\u0105dania, a przy w\u0142asnym mechanizmie fingerprint\u00f3w mo\u017cemy dodatkowo zadba\u0107 o jej kanoniczn\u0105 reprezentacj\u0119. Istotne jest, aby nie ignorowa\u0107 parametr\u00f3w, kt\u00f3re rzeczywi\u015bcie wp\u0142ywaj\u0105 na dzia\u0142anie modelu.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\"><strong>Ponowne nagranie kasety<\/strong><\/h3>\n\n\n\n<p>Je\u017celi zmieni\u0142a si\u0119 tre\u015b\u0107 procedury albo pytanie w te\u015bcie, odpowiedni\u0105 kaset\u0119 trzeba nagra\u0107 ponownie. Nie robi\u0142bym tego jednak automatycznie podczas zwyk\u0142ego uruchomienia CI\/CD. Brak pasuj\u0105cej kasety powinien oznacza\u0107 b\u0142\u0105d testu, a nie niejawne wys\u0142anie zapytania do p\u0142atnego API.<\/p>\n\n\n\n<p>Nagrywanie uruchamiamy \u015bwiadomie, w osobnym procesie, w kt\u00f3rym mamy dost\u0119p do klucza API, mo\u017cemy obejrze\u0107 nowe odpowiedzi i zdecydowa\u0107, czy nadaj\u0105 si\u0119 do zatwierdzenia. Nowe kasety najlepiej zapisywa\u0107 najpierw w katalogu roboczym; je\u015bli model po zmianie procedury zacznie pope\u0142nia\u0107 b\u0142\u0119dy, poprzednie zatwierdzone nagrania pozostan\u0105 nienaruszone.<\/p>\n\n\n\n<p>Dla wa\u017cniejszych przypadk\u00f3w warto r\u00f3wnie\u017c wykona\u0107 kilka rzeczywistych pr\u00f3b i sprawdzi\u0107, jak cz\u0119sto model wybiera poprawn\u0105 decyzj\u0119. Jedna udana odpowied\u017a nie m\u00f3wi nam jeszcze, czy model r\u00f3wnie dobrze poradzi sobie z kolejnymi pytaniami tego samego rodzaju. Dopiero po sprawdzeniu wynik\u00f3w zapisujemy zatwierdzone kasety razem z testami.<\/p>\n\n\n\n<p>W codziennej pracy otrzymujemy w ten spos\u00f3b prosty proces:<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li>przygotowujemy scenariusz,<\/li>\n\n\n\n<li>wykonujemy nagranie,<\/li>\n\n\n\n<li>sprawdzamy odpowied\u017a,<\/li>\n\n\n\n<li>zatwierdzamy kaset\u0119 i wykorzystujemy j\u0105 podczas regresji.<\/li>\n<\/ol>\n\n\n\n<p>Gdy p\u00f3\u017aniej zmienimy prompt, model albo dokumentacj\u0119, brak dopasowania lub wykryta zmiana zale\u017cno\u015bci b\u0119dzie sygna\u0142em do przeprowadzenia nowej ewaluacji.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\"><strong>B\u0142\u0119dna interpretacja<\/strong><\/h3>\n\n\n\n<p>Je\u017celi po nagraniu odpowiedzi oka\u017ce si\u0119, \u017ce model \u017ale zinterpretowa\u0142 firmow\u0105 procedur\u0119, otrzymujemy informacj\u0119, kt\u00f3r\u0105 mo\u017cemy wykorzysta\u0107 podczas poprawiania systemu. By\u0107 mo\u017ce dokumentacja jest niejednoznaczna, by\u0107 mo\u017ce brakuje w niej istotnego warunku albo model nie otrzyma\u0142 odpowiedniego fragmentu. Mo\u017cemy poprawi\u0107 procedur\u0119, ponownie wykona\u0107 test i por\u00f3wna\u0107 wynik.<\/p>\n\n\n\n<p>Co ciekawe, taka praca mo\u017ce prowadzi\u0107 r\u00f3wnie\u017c do obni\u017cenia koszt\u00f3w dzia\u0142ania gotowego systemu. Dobrze przygotowana baza wiedzy, jasne regu\u0142y pierwsze\u0144stwa i odpowiednio ograniczone zadanie mog\u0105 pozwoli\u0107 ta\u0144szemu modelowi poradzi\u0107 sobie z pytaniami, dla kt\u00f3rych wcze\u015bniej potrzebowali\u015bmy znacznie dro\u017cszego rozwi\u0105zania.<\/p>\n\n\n\n<p>Mo\u017cemy to sprawdzi\u0107, uruchamiaj\u0105c wsp\u00f3lne scenariusze na kilku rodzinach modeli i por\u00f3wnuj\u0105c odsetek poprawnych odpowiedzi, liczb\u0119 b\u0142\u0119d\u00f3w oraz koszt wykonania zadania. Warto uwzgl\u0119dni\u0107 modele o r\u00f3\u017cnej cenie, wielko\u015bci i sposobie trenowania, poniewa\u017c ich zachowanie mo\u017ce by\u0107 odmienne nawet przy identycznych danych wej\u015bciowych. R\u00f3\u017cnice mog\u0105 dotyczy\u0107 rozumienia j\u0119zyka polskiego, lokalnych poj\u0119\u0107, interpretowania formalnych instrukcji czy obs\u0142ugi ustrukturyzowanych odpowiedzi.<\/p>\n\n\n\n<p>Nie zak\u0142ada\u0142bym z g\u00f3ry, kt\u00f3ry model poradzi sobie najlepiej z nasz\u0105 dokumentacj\u0105. Lepiej to po prostu zmierzy\u0107.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>Przyk\u0142ady bibliotek \u2013 Python i .NET<\/strong><\/h2>\n\n\n\n<p>Do przygotowania takiego rozwi\u0105zania nie musimy od razu budowa\u0107 w\u0142asnego frameworka.<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>W Pythonie mo\u017cemy wykorzysta\u0107 VCR.py, dost\u0119pne jako pakiet vcrpy, kt\u00f3re przechwytuje komunikacj\u0119 wykonywan\u0105 przez obs\u0142ugiwanych klient\u00f3w HTTP i zapisuje j\u0105 w plikach YAML. Dzia\u0142a niezale\u017cnie od frameworka testowego, wi\u0119c r\u00f3wnie dobrze mo\u017cemy u\u017cywa\u0107 go z pytest, jak i z innym narz\u0119dziem.<\/li>\n\n\n\n<li>W .NET podobny efekt uzyskamy za pomoc\u0105 WireMock.Net, uruchamiaj\u0105c lokalny serwer proxy.<\/li>\n<\/ul>\n\n\n\n<p>R\u00f3\u017cnica jest istotna: VCR.py przechwytuje \u017c\u0105danie naszego klienta, natomiast WireMock.Net odbiera je na osobnym adresie i podczas nagrywania przekazuje dalej do dostawcy API. W obu przypadkach podczas zwyk\u0142ej regresji aplikacja otrzymuje wcze\u015bniej zapisan\u0105 odpowied\u017a, bez kontaktowania si\u0119 z zewn\u0119trznym modelem.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\"><strong>Python<\/strong><\/h3>\n\n\n\n<p>Zacznijmy od Pythona. Dotychczasowe testy mogliby\u015bmy napisa\u0107 jako pojedyncz\u0105 funkcj\u0119 wykonuj\u0105c\u0105 request i sprawdzaj\u0105c\u0105 JSON, ale nie by\u0142aby to jeszcze sensowna demonstracja regresji aplikacji. Je\u017celi ca\u0142y prompt, parser i wywo\u0142anie HTTP znajd\u0105 si\u0119 w te\u015bcie, zmiana w\u0142a\u015bciwego kodu helpdesku pozostanie dla niego niewidoczna.<\/p>\n\n\n\n<p>Dlatego wydzielmy kr\u00f3tk\u0105 funkcj\u0119 aplikacyjn\u0105 <code>classify_ticket()<\/code> do pliku <code>helpdesk.py<\/code>. Odpowiada ona za zbudowanie \u017c\u0105dania z aktualnego promptu i podanej procedury, wykonanie po\u0142\u0105czenia i walidacj\u0119 odpowiedzi. Dopiero test wywo\u0142a t\u0119 funkcj\u0119 i por\u00f3wna wynik z oczekiwaniami zapisanymi w PRINT-003.yaml.<\/p>\n\n\n\n<p>W przyk\u0142adzie wykorzystujemy endpoint Chat Completions, a model domy\u015blnie ustawiamy na gpt-6-luna z jawnym <code>reasoning_effort=\"none\".<\/code> To przyk\u0142ad konkretnej konfiguracji, a nie obietnica, \u017ce alias modelu b\u0119dzie zawsze oznacza\u0142 t\u0119 sam\u0105 wersj\u0119.<\/p>\n\n\n\n<p>Plik <code>helpdesk.py<\/code>:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: xml; title: ; notranslate\" title=\"\">\n&quot;&quot;&quot;Minimalny fragment kodu aplikacji, niezale\u017cny od pytest i VCR.&quot;&quot;&quot;\nimport json\nimport os\n\nimport requests\nfrom jsonschema import validate\n\nAPI_URL = &quot;https:\/\/api.openai.com\/v1\/chat\/completions&quot;\nMODEL = os.getenv(&quot;HELPDESK_MODEL&quot;, &quot;gpt-6-luna&quot;)\n\nRESPONSE_SCHEMA = {\n    &quot;type&quot;: &quot;object&quot;,\n    &quot;properties&quot;: {\n        &quot;category&quot;: {\n            &quot;type&quot;: &quot;string&quot;,\n            &quot;enum&quot;: &#x5B;&quot;client_printing_issue&quot;, &quot;printer_hardware_issue&quot;, &quot;other&quot;],\n        },\n        &quot;action&quot;: {\n            &quot;type&quot;: &quot;string&quot;,\n            &quot;enum&quot;: &#x5B;&quot;check_print_queue&quot;, &quot;check_printer&quot;, &quot;ask_for_details&quot;],\n        },\n    },\n    &quot;required&quot;: &#x5B;&quot;category&quot;, &quot;action&quot;],\n    &quot;additionalProperties&quot;: False,\n}\n\nSYSTEM_PROMPT = (\n    &quot;Klasyfikuj problem na podstawie do\u0142\u0105czonej procedury. &quot;\n    &quot;Wybierz pierwszy w\u0142a\u015bciwy krok diagnostyczny. &quot;\n    &quot;Odpowiedz JSON-em zgodnym z otrzymanym schematem.&quot;\n)\n\n\ndef classify_ticket(question: str, procedure: dict) -&gt; dict:\n    &quot;&quot;&quot;Zwr\u00f3\u0107 decyzj\u0119 asystenta po wywo\u0142aniu rzeczywistego klienta HTTP.&quot;&quot;&quot;\n    prompt = (\n        f&quot;{SYSTEM_PROMPT}\\n&quot;\n        f&quot;Procedura: {procedure&#x5B;&#039;id&#039;]}, wersja {procedure&#x5B;&#039;version&#039;]}\\n&quot;\n        f&quot;{procedure&#x5B;&#039;text&#039;]}&quot;\n    )\n    response = requests.post(\n        API_URL,\n        headers={\n            &quot;Authorization&quot;: f&quot;Bearer {os.getenv(&#039;OPENAI_API_KEY&#039;) or &#039;VCR_REPLAY&#039;}&quot;\n        },\n        json={\n            &quot;model&quot;: MODEL,\n            &quot;reasoning_effort&quot;: &quot;none&quot;,\n            &quot;messages&quot;: &#x5B;\n                {&quot;role&quot;: &quot;system&quot;, &quot;content&quot;: prompt},\n                {&quot;role&quot;: &quot;user&quot;, &quot;content&quot;: question},\n            ],\n            &quot;response_format&quot;: {\n                &quot;type&quot;: &quot;json_schema&quot;,\n                &quot;json_schema&quot;: {\n                    &quot;name&quot;: &quot;helpdesk_decision&quot;,\n                    &quot;strict&quot;: True,\n                    &quot;schema&quot;: RESPONSE_SCHEMA,\n                },\n            },\n        },\n        timeout=30,\n    )\n    response.raise_for_status()\n    choice = response.json()&#x5B;&quot;choices&quot;]&#x5B;0]\n    if choice.get(&quot;finish_reason&quot;) != &quot;stop&quot;:\n        raise ValueError(&quot;Odpowied\u017a modelu jest niekompletna&quot;)\n    message = choice&#x5B;&quot;message&quot;]\n    if message.get(&quot;refusal&quot;):\n        raise ValueError(&quot;Model odm\u00f3wi\u0142 odpowiedzi&quot;)\n    result = json.loads(message&#x5B;&quot;content&quot;])\n    validate(instance=result, schema=RESPONSE_SCHEMA)\n    return result\n<\/pre><\/div>\n\n\n<p>Kluczowym szczeg\u00f3\u0142em jest to, \u017ce prompt oraz spos\u00f3b interpretowania odpowiedzi nale\u017c\u0105 do kodu aplikacji, a nie do testu. Je\u015bli programista przypadkowo zmieni <code>SYSTEM_PROMPT<\/code>, zapytanie HTTP b\u0119dzie inne i dotychczasowa kaseta przestanie pasowa\u0107. Je\u017celi zmieni spos\u00f3b odczytywania <code>choices[0]<\/code> lub walidowania wyniku, test wykona now\u0105 wersj\u0119 tego kodu na tej samej nagranej odpowiedzi i mo\u017ce wykry\u0107 regresj\u0119.<\/p>\n\n\n\n<p>W schemacie wymienili\u015bmy dozwolone kategorie i akcje, wi\u0119c model nie musi zgadywa\u0107 nazw naszych wewn\u0119trznych identyfikator\u00f3w. Structured Outputs pomaga uzyska\u0107 JSON zgodny z obs\u0142ugiwanym podzbiorem JSON Schema, ale pozostawiamy walidacj\u0119 r\u00f3wnie\u017c po stronie aplikacji, poniewa\u017c jest elementem jej kontraktu. Prawid\u0142owy format nadal nie gwarantuje w\u0142a\u015bciwego wyboru procedury.<\/p>\n\n\n\n<p>Drugi plik, <code>tests\/test_helpdesk.py<\/code>, zawiera sam scenariusz regresyjny i konfiguracj\u0119 kaset:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: xml; title: ; notranslate\" title=\"\">\n&quot;&quot;&quot;Test regresyjny: VCR zast\u0119puje API, nie testowany kod aplikacji.&quot;&quot;&quot;\nimport os\nfrom pathlib import Path\n\nimport vcr\nimport yaml\n\nfrom helpdesk import classify_ticket\n\nROOT = Path(__file__).resolve().parent\nCASE = yaml.safe_load((ROOT \/ &quot;scenarios\/PRINT-003.yaml&quot;).read_text(encoding=&quot;utf-8&quot;))\nRECORDING = os.getenv(&quot;VCR_RECORD&quot;) == &quot;1&quot;\nCASSETTE = ROOT \/ &quot;cassettes&quot; \/ (&quot;_pending&quot; if RECORDING else &quot;&quot;) \/ f&quot;{CASE&#x5B;&#039;id&#039;]}.yaml&quot;\nif RECORDING:\n    CASSETTE.parent.mkdir(parents=True, exist_ok=True)\n\n\ndef sanitize_response(response):\n    if response&#x5B;&quot;status&quot;]&#x5B;&quot;code&quot;] != 200:\n        return None\n    headers = response.get(&quot;headers&quot;, {})\n    for name in list(headers):\n        if name.lower() in {&quot;set-cookie&quot;, &quot;openai-organization&quot;, &quot;openai-project&quot;, &quot;x-request-id&quot;}:\n            del headers&#x5B;name]\n    return response\n\n\nrecorder = vcr.VCR(\n    record_mode=&quot;all&quot; if RECORDING else &quot;none&quot;,\n    match_on=&#x5B;&quot;method&quot;, &quot;uri&quot;, &quot;body&quot;],\n    filter_headers=&#x5B;&quot;authorization&quot;],\n    before_record_response=sanitize_response,\n    record_on_exception=False,\n    drop_unused_requests=RECORDING,\n    decode_compressed_response=True,\n)\n\n\n@recorder.use_cassette(str(CASSETTE))\ndef test_helpdesk_printing_problem():\n    if RECORDING:\n        assert os.getenv(&quot;OPENAI_API_KEY&quot;), &quot;Nagrywanie wymaga OPENAI_API_KEY&quot;\n    result = classify_ticket(CASE&#x5B;&quot;question&quot;], CASE&#x5B;&quot;procedure&quot;])\n    assert result == CASE&#x5B;&quot;expected&quot;]\n<\/pre><\/div>\n\n\n<p>Do uruchomienia przyk\u0142adu potrzebujemy Pythona 3.10+ oraz pakiet\u00f3w <code>requests, pytest, PyYAML, jsonschema<\/code> i <strong>vcrpy==8.4.0<\/strong>. Wersja VCR.py jest tutaj istotna. W starszych wydaniach po\u0142\u0105czenie ponownego nagrywania w trybie <code>all<\/code> z <code>drop_unused_requests=<\/code>True mog\u0142o zapisa\u0107 pust\u0105 kaset\u0119; problem naprawiono w wydaniu 8.4.0. W trybie <code>VCR_RECORD=1<\/code> test zapisuje rezultat do <code>tests\/cassettes\/_pending\/PRINT-003.yaml.<\/code><\/p>\n\n\n\n<p>Dopiero po sprawdzeniu odpowiedzi i przejrzeniu pliku przenosimy go do zatwierdzonego katalogu <code>tests\/cassettes<\/code>. Codzienne CI uruchamiamy bez <code>VCR_RECORD<\/code>, z <code>record_mode=\"none\"<\/code>, a wi\u0119c bez mo\u017cliwo\u015bci niejawnego dogrania brakuj\u0105cej interakcji. Je\u015bli prompt systemowy, procedura, model lub istotny parametr \u017c\u0105dania ulegn\u0105 zmianie, test nie odnajdzie pasuj\u0105cego nagrania.<\/p>\n\n\n\n<p>Je\u017celi zmienimy parser lub logik\u0119 obs\u0142ugi odpowiedzi, nadal otrzyma zapisany JSON i sprawdzi aktualny kod aplikacji. <code>record_on_exception=False<\/code> chroni przed utrwaleniem nieudanego przebiegu, ale nie zast\u0119puje r\u0119cznego zatwierdzania nowych kaset.<\/p>\n\n\n\n<p>Zwr\u00f3\u0107my te\u017c uwag\u0119 na czyszczenie nagra\u0144. <code>filter_headers<\/code> usuwa wskazany nag\u0142\u00f3wek \u017c\u0105dania, natomiast funkcja <code>sanitize_response<\/code> usuwa wybrane nag\u0142\u00f3wki odpowiedzi i pomija nieudane statusy HTTP. <code>decode_compressed_response=True<\/code> pozwala zapisa\u0107 cz\u0119\u015b\u0107 skompresowanych odpowiedzi w czytelnej postaci; przy Brotli potrzebna jest dost\u0119pna obs\u0142uga tego formatu.<\/p>\n\n\n\n<p>To nadal nie wystarczy do automatycznej anonimizacji dokumentacji ani pyta\u0144 u\u017cytkownik\u00f3w.<\/p>\n\n\n\n<p>Przed zapisaniem kasety w repozytorium trzeba przejrze\u0107 r\u00f3wnie\u017c jej tre\u015b\u0107. Nieudane odpowiedzi HTTP, b\u0142\u0119dny JSON i brakuj\u0105ce pola mo\u017cemy natomiast testowa\u0107 osobno na przygotowanych r\u0119cznie danych, bez czekania, a\u017c model rzeczywi\u015bcie pope\u0142ni taki b\u0142\u0105d.<\/p>\n\n\n\n<p>W przyk\u0142adzie nie korzystamy ze streamingu; odpowiedzi SSE wymaga\u0142yby innego sposobu odczytu i odtwarzania zdarze\u0144.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\"><strong>.NET<\/strong><\/h3>\n\n\n\n<p>W .NET mo\u017cemy wykorzysta\u0107 WireMock.Net w wersji <strong>2.19.0<\/strong> z C# 12 lub nowszym. Poni\u017cszy fragment pokazuje konfiguracj\u0119 serwera, do kt\u00f3rego aplikacyjny <code>HttpClient<\/code> wysy\u0142a \u017c\u0105dania zamiast do prawdziwego endpointu. Nie jest to pe\u0142ny test xUnit \u2013 sama aplikacja nadal musi wywo\u0142a\u0107 w\u0142asn\u0105 metod\u0119 klasyfikowania zg\u0142osze\u0144, a test powinien por\u00f3wna\u0107 jej wynik z expected w naszym scenariuszu.<\/p>\n\n\n\n<p>Podobnie jak w Pythonie, nie kopiujemy promptu do testu. W trybie nagrywania WireMock pe\u0142ni funkcj\u0119 proxy i zapisuje mapowania \u017c\u0105da\u0144 na odpowiedzi, a podczas regresji odczytuje je lokalnie.<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: xml; title: ; notranslate\" title=\"\">\nusing WireMock.Server;\nusing WireMock.Settings;\n\nvar recording =\n    Environment.GetEnvironmentVariable(&quot;VCR_RECORD&quot;) == &quot;1&quot;;\n\nusing var server = WireMockServer.Start(\n    new WireMockServerSettings\n    {\n        Urls = &#x5B;&quot;http:\/\/127.0.0.1:9095&quot;],\n        ReadStaticMappings = !recording,\n        ProxyAndRecordSettings = recording\n            ? new ProxyAndRecordSettings\n            {\n                Url = &quot;https:\/\/api.openai.com&quot;,\n                SaveMapping = true,\n                SaveMappingToFile = true,\n                ExcludedHeaders =\n                &#x5B;\n                    &quot;Authorization&quot;,\n                    &quot;User-Agent&quot;,\n                    &quot;traceparent&quot;,\n                    &quot;x-request-id&quot;,\n                    &quot;Accept-Encoding&quot;\n                ],\n                SaveMappingSettings = new ProxySaveMappingSettings\n                {\n                    StatusCodePattern = &quot;2xx&quot;\n                }\n            }\n            : null\n    });\n<\/pre><\/div>\n\n\n<p>Warto po nagraniu zajrze\u0107 do wygenerowanych plik\u00f3w mapowa\u0144, zwykle umieszczanych w katalogu <code>__admin\/mappings<\/code>, i sprawdzi\u0107 rzeczywiste regu\u0142y dopasowania.<\/p>\n\n\n\n<p>WireMock.Net potrafi por\u00f3wnywa\u0107 JSON, ale mo\u017ce r\u00f3wnie\u017c uwzgl\u0119dni\u0107 nag\u0142\u00f3wki HTTP, kt\u00f3re r\u00f3\u017cni\u0105 si\u0119 mi\u0119dzy uruchomieniami, dlatego wykluczyli\u015bmy kilka zmiennych lub poufnych nag\u0142\u00f3wk\u00f3w. Lista zale\u017cy od konkretnego klienta API.<\/p>\n\n\n\n<p>Nale\u017cy tak\u017ce zadba\u0107 o katalog roboczy dla nowych nagra\u0144, wykluczy\u0107 dane poufne z tre\u015bci, dobra\u0107 osobny port dla wsp\u00f3\u0142bie\u017cnych test\u00f3w i sprawdzi\u0107, czy brak mapowania prowadzi do niepowodzenia testu \u2013 nie do po\u0142\u0105czenia z produkcyjnym API. WireMock zapisuje mapowanie po otrzymaniu odpowiedzi HTTP i nie wie, czy p\u00f3\u017aniejsze asercje xUnit przesz\u0142y.<\/p>\n\n\n\n<p>To wa\u017cna r\u00f3\u017cnica wzgl\u0119dem naszego przyk\u0142adu Python: <strong>zatwierdzenie mapowa\u0144 w .NET musi by\u0107 osobnym krokiem<\/strong>, na przyk\u0142ad po pozytywnej weryfikacji test\u00f3w i przegl\u0105dzie zmian w repozytorium. Sam filtr statusu 2xx nie dowodzi, \u017ce odpowied\u017a modelu by\u0142a poprawna.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>Czy poprawny JSON oznacza poprawn\u0105 odpowied\u017a?<\/strong><\/h2>\n\n\n\n<p>Wr\u00f3\u0107my na chwil\u0119 do naszego zg\u0142oszenia dotycz\u0105cego drukarki. Model mo\u017ce zwr\u00f3ci\u0107 prawid\u0142owy JSON, zawieraj\u0105cy wszystkie wymagane pola, a mimo to wybra\u0107 niew\u0142a\u015bciw\u0105 kategori\u0119. Walidacja schematu potwierdzi jedynie, \u017ce odpowied\u017a ma oczekiwan\u0105 struktur\u0119. Nie dowiemy si\u0119 z niej, czy zaproponowana akcja jest zgodna z firmow\u0105 procedur\u0105.<\/p>\n\n\n\n<p>Dlatego warto rozdzieli\u0107 dwa rodzaje kontroli.<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Pierwszy sprawdza techniczny format odpowiedzi: wymagane pola, typy danych i dozwolone warto\u015bci.<\/li>\n\n\n\n<li>Drugi por\u00f3wnuje znaczenie odpowiedzi z oczekiwanym rezultatem przygotowanym dla danego scenariusza.<\/li>\n<\/ul>\n\n\n\n<p>W naszym przyk\u0142adzie mo\u017cemy dopu\u015bci\u0107 r\u00f3\u017cne sformu\u0142owania komunikatu dla pracownika, ale decyzja o sprawdzeniu kolejki drukowania przed diagnozowaniem awarii samego urz\u0105dzenia powinna pozosta\u0107 taka sama. Structured Outputs pomaga nam uzyska\u0107 poprawny format danych, ale nie zast\u0119puje sprawdzenia, czy decyzja wybrana przez model rzeczywi\u015bcie ma sens.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\"><strong>Wyrocznia testowa<\/strong><\/h3>\n\n\n\n<p>W terminologii testowania mechanizm okre\u015blaj\u0105cy, czy otrzymany wynik jest poprawny, nazywamy <em>test oracle<\/em>, czyli wyroczni\u0105 testow\u0105.<\/p>\n\n\n\n<p>W najprostszym przypadku sk\u0142adaj\u0105 si\u0119 na ni\u0105 zatwierdzone oczekiwania oraz spos\u00f3b por\u00f3wnania ich z rzeczywistym wynikiem. Nie zawsze potrzebujemy do tego kolejnego LLM-a. Je\u017celi model zwraca ustrukturyzowane dane, wiele test\u00f3w mo\u017cemy wykona\u0107 zwyk\u0142ymi asercjami, tak jak w przyk\u0142adzie z Pythonem. Dopiero przy odpowiedziach opisowych, wymagaj\u0105cych oceny znaczenia ca\u0142ego tekstu, mo\u017ce przyda\u0107 si\u0119 bardziej zaawansowana ewaluacja, czasem wykorzystuj\u0105ca drugi model.<\/p>\n\n\n\n<p>Taki evaluator r\u00f3wnie\u017c mo\u017ce pope\u0142nia\u0107 b\u0142\u0119dy, dlatego dla wa\u017cnych scenariuszy warto mie\u0107 pr\u00f3bki ocenione przez cz\u0142owieka i sprawdza\u0107, czy automatyczna ocena pozostaje z nimi zgodna. Nie chcia\u0142bym doprowadzi\u0107 do sytuacji, w kt\u00f3rej jeden model wymy\u015bla odpowied\u017a, drugi uznaje j\u0105 za poprawn\u0105, a test ko\u0144czy si\u0119 sukcesem tylko dlatego, \u017ce oba zgodnie si\u0119 pomyli\u0142y.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\"><strong>Testy kilku rodzin modeli<\/strong><\/h3>\n\n\n\n<p>W testach obejmuj\u0105cych kilka rodzin modeli mo\u017cemy zauwa\u017cy\u0107 r\u00f3\u017cnice, kt\u00f3re trudno dostrzec podczas pojedynczej rozmowy.<\/p>\n\n\n\n<p>Ten sam zestaw pyta\u0144 pozwala sprawdzi\u0107, jak poszczeg\u00f3lne modele interpretuj\u0105 firmowe procedury, czy potrafi\u0105 rozpozna\u0107 istotne szczeg\u00f3\u0142y w wypowiedziach u\u017cytkownik\u00f3w oraz jak radz\u0105 sobie z pytaniami, na kt\u00f3re odpowied\u017a wymaga po\u0142\u0105czenia informacji z kilku dokument\u00f3w. Mo\u017cemy przy okazji por\u00f3wna\u0107 obs\u0142ug\u0119 JSON Schema, formatowanie odpowiedzi i zgodno\u015b\u0107 z kontraktem naszej aplikacji.<\/p>\n\n\n\n<p>Przy korzystaniu z modeli albo dostawc\u00f3w bez \u015bcis\u0142ego wymuszania schematu zdarza si\u0119, \u017ce model dobrze rozumie pytanie, ale zwraca dane w niew\u0142a\u015bciwym formacie.<\/p>\n\n\n\n<p>W trybie Structured Outputs ze \u015bcis\u0142ym schematem eliminujemy wiele takich b\u0142\u0119d\u00f3w technicznych; nadal jednak mo\u017cemy otrzyma\u0107 poprawny JSON zawieraj\u0105cy b\u0142\u0119dn\u0105 decyzj\u0119. Dzi\u0119ki wsp\u00f3lnym przypadkom testowym mo\u017cemy rozdzieli\u0107 te problemy, por\u00f3wna\u0107 wyniki i sprawdzi\u0107, czy poprawienie dokumentacji rzeczywi\u015bcie przynios\u0142o oczekiwany efekt. Z czasem taki zbi\u00f3r staje si\u0119 r\u00f3wnie\u017c dobrym punktem odniesienia przy aktualizacji modelu albo zmianie dostawcy API.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>Co zrobi\u0107, kiedy zmieni si\u0119 procedura?<\/strong><\/h2>\n\n\n\n<p>Za\u0142\u00f3\u017cmy, \u017ce po kilku tygodniach dzia\u0142ania helpdesku aktualizujemy procedur\u0119 dotycz\u0105c\u0105 drukowania.<\/p>\n\n\n\n<p>Nowa wersja wyra\u017anie rozr\u00f3\u017cnia problem z sam\u0105 drukark\u0105 od sytuacji, w kt\u00f3rej urz\u0105dzenie wykonuje wydruk testowy, ale dokumenty zatrzymuj\u0105 si\u0119 w kolejce na komputerze. W tradycyjnym podej\u015bciu mogliby\u015bmy ponownie uruchomi\u0107 wszystkie testy na rzeczywistych modelach i sprawdzi\u0107, czy co\u015b si\u0119 zmieni\u0142o. Przy kilkudziesi\u0119ciu scenariuszach nie b\u0119dzie to jeszcze du\u017cy problem, ale przy kilku tysi\u0105cach pyta\u0144 i wielu modelach zaczynamy wykonywa\u0107 sporo niepotrzebnej pracy.<\/p>\n\n\n\n<p>Je\u017celi wiemy, kt\u00f3re przypadki testowe korzystaj\u0105 z procedury drukowania, mo\u017cemy ponownie nagra\u0107 odpowiedzi zwi\u0105zane ze zmienionym dokumentem, a pozosta\u0142e kasety wykorzysta\u0107 w zwyk\u0142ej regresji. Takie podej\u015bcie pozwala ograniczy\u0107 liczb\u0119 p\u0142atnych wywo\u0142a\u0144 i szybciej sprawdzi\u0107 najbardziej prawdopodobne skutki wprowadzenia zmiany. Przy okazji mo\u017cemy por\u00f3wna\u0107 wyniki sprzed aktualizacji i po niej, zamiast polega\u0107 wy\u0142\u0105cznie na subiektywnej ocenie, \u017ce nowa procedura jest bardziej zrozumia\u0142a.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\"><strong>Zale\u017cno\u015bci<\/strong><\/h3>\n\n\n\n<p>Musimy jednak uwa\u017ca\u0107 na zale\u017cno\u015bci mi\u0119dzy dokumentami. Je\u017celi ka\u017cde zapytanie zawiera ca\u0142\u0105 baz\u0119 wiedzy w jednym ogromnym prompcie, zmiana cho\u0107by jednego akapitu wp\u0142ywa na tre\u015b\u0107 wszystkich \u017c\u0105da\u0144. Wtedy nie mo\u017cemy bez dodatkowego uzasadnienia uzna\u0107, \u017ce pozosta\u0142e kasety nadal odpowiadaj\u0105 aktualnej konfiguracji.<\/p>\n\n\n\n<p>Znacznie \u0142atwiej zarz\u0105dza\u0107 testami, kiedy wiemy, kt\u00f3re procedury zosta\u0142y wykorzystane do przygotowania odpowiedzi. W systemie korzystaj\u0105cym z RAG mo\u017cemy zapisywa\u0107 identyfikatory i wersje fragment\u00f3w dokumentacji zwr\u00f3conych przez wyszukiwark\u0119. Przy prostszej aplikacji wystarczy jawne przypisanie scenariuszy do procedur, tak jak w naszym pliku YAML. Na tej podstawie wyznaczamy testy wymagaj\u0105ce od\u015bwie\u017cenia.<\/p>\n\n\n\n<p>Warto przy tym pami\u0119ta\u0107, \u017ce zmiana dokumentu mo\u017ce wp\u0142yn\u0105\u0107 r\u00f3wnie\u017c na wyszukiwanie i ranking fragment\u00f3w, dlatego selektywna regresja powinna by\u0107 uzupe\u0142niana okresowym pe\u0142nym przebiegiem test\u00f3w na rzeczywistym systemie. Je\u017celi korzystamy przy tym z zewn\u0119trznego API embedding\u00f3w, r\u00f3wnie\u017c t\u0119 zale\u017cno\u015b\u0107 musimy kontrolowa\u0107 podczas test\u00f3w offline. Samo nagranie ko\u0144cowej odpowiedzi modelu nie oznacza jeszcze, \u017ce pozosta\u0142e elementy systemu przesta\u0142y potrzebowa\u0107 dost\u0119pu do sieci.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\"><strong>Pull request<\/strong><\/h3>\n\n\n\n<p>Tak przygotowane kasety mo\u017cemy przechowywa\u0107 w repozytorium i uruchamia\u0107 podczas ka\u017cdego pull requestu. Programista zmienia kod odpowiedzialny za obs\u0142ug\u0119 odpowiedzi modelu, uruchamia testy i natychmiast widzi, czy aplikacja nadal poprawnie przetwarza wcze\u015bniej zatwierdzone wyniki. Nie potrzebuje do tego klucza API ani dost\u0119pno\u015bci konkretnego dostawcy. Je\u017celi w kodzie aplikacji zmieni si\u0119 prompt, model lub dokumentacja przekazywana w \u017c\u0105daniu, test mo\u017ce zako\u0144czy\u0107 si\u0119 b\u0142\u0119dem z powodu braku pasuj\u0105cej kasety.<\/p>\n\n\n\n<p>Sama zmiana pliku w repozytorium, kt\u00f3rego aplikacja nie u\u017cywa w danym te\u015bcie, nie zostanie natomiast magicznie wykryta przez VCR. Do tego potrzebujemy kontrolowa\u0107 zale\u017cno\u015bci i wersje danych.<\/p>\n\n\n\n<p>W bardziej rozbudowanym rozwi\u0105zaniu mo\u017cemy r\u00f3wnie\u017c wykrywa\u0107 takie zmiany wcze\u015bniej na podstawie fingerprint\u00f3w i mapy zale\u017cno\u015bci. Nast\u0119pnie uruchamiamy kontrolowane nagrywanie, sprawdzamy nowe odpowiedzi, przeprowadzamy review i zatwierdzamy zmian\u0119. W ten spos\u00f3b odpowiedzi modelu staj\u0105 si\u0119 kontrolowanymi danymi testowymi, a nie czym\u015b, co za ka\u017cdym uruchomieniem pojawia si\u0119 na nowo i znika po zako\u0144czeniu pipeline&#8217;u.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>Ile mo\u017cna na tym zaoszcz\u0119dzi\u0107?<\/strong><\/h2>\n\n\n\n<p>Spr\u00f3bujmy policzy\u0107 koszty na naszym przyk\u0142adzie helpdesku.<\/p>\n\n\n\n<p>Przyjmijmy, \u017ce przygotowali\u015bmy 60 scenariuszy testowych i chcemy sprawdzi\u0107 je na trzech modelach. Jeden pe\u0142ny przebieg oznacza 180 wywo\u0142a\u0144 API. Je\u017celi pipeline CI\/CD uruchamia si\u0119 80 razy w miesi\u0105cu i ka\u017cde uruchomienie rzeczywi\u015bcie odpytuje modele, wykonamy \u0142\u0105cznie 14 400 zapyta\u0144, chocia\u017c wi\u0119kszo\u015b\u0107 z nich b\u0119dzie dotyczy\u0107 tych samych pyta\u0144 i procedur. Przy czysto hipotetycznym \u015brednim koszcie 0,002 USD za wywo\u0142anie otrzymujemy 28,80 USD miesi\u0119cznie.<\/p>\n\n\n\n<p>Samo to nie jest imponuj\u0105cym rachunkiem, zw\u0142aszcza je\u015bli korzystamy z bardzo taniego modelu. Dla por\u00f3wnania, przy modelu kosztuj\u0105cym 0,10 USD za milion token\u00f3w wej\u015bciowych i 0,50 USD za milion wyj\u015bciowych, jedno \u017c\u0105danie zu\u017cywaj\u0105ce 1000 token\u00f3w wej\u015bciowych i 200 wyj\u015bciowych kosztowa\u0142oby oko\u0142o 0,0002 USD, czyli wszystkie 14 400 wywo\u0142a\u0144 oko\u0142o 2,88 USD. Przy d\u0142u\u017cszych dokumentach, bardziej wymagaj\u0105cych modelach i tokenach rozumowania koszty b\u0119d\u0105 inne. Dlatego poni\u017csze warto\u015bci maj\u0105 pokaza\u0107 mechanizm skalowania, a nie deklarowa\u0107 typowy rachunek dla ka\u017cdego helpdesku.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\"><strong>Koszt kaset VCR<\/strong><\/h3>\n\n\n\n<p>Z kasetami VCR wygl\u0105da to inaczej. Pierwszy przebieg nadal wymaga 180 rzeczywistych zapyta\u0144, poniewa\u017c musimy przygotowa\u0107 nagrania. Przy za\u0142o\u017ceniu 0,002 USD za wywo\u0142anie kosztuje to 0,36 USD. Nast\u0119pnie mo\u017cemy uruchamia\u0107 regresj\u0119 aplikacji wielokrotnie bez nowych op\u0142at za inferencj\u0119, korzystaj\u0105c z zatwierdzonych odpowiedzi.<\/p>\n\n\n\n<p>Je\u017celi w pierwszym miesi\u0105cu od\u015bwie\u017cymy 20% scenariuszy dla wszystkich trzech modeli, czyli wykonamy 36 dodatkowych wywo\u0142a\u0144, a ponadto raz uruchomimy pe\u0142n\u0105 ewaluacj\u0119 na rzeczywistych modelach obejmuj\u0105c\u0105 180 pyta\u0144, otrzymamy 396 p\u0142atnych wywo\u0142a\u0144 za 0,792 USD.<\/p>\n\n\n\n<p>W kolejnych miesi\u0105cach, bez jednorazowego nagrania pocz\u0105tkowego, b\u0119dzie to 216 wywo\u0142a\u0144 za 0,432 USD. Oznacza to ponad 97% mniej p\u0142atnych wywo\u0142a\u0144 w przedstawionym pierwszym miesi\u0105cu i 98,5% mniej w nast\u0119pnych.<\/p>\n\n\n\n<p>Por\u00f3wnujemy jednak <strong>inne harmonogramy test\u00f3w<\/strong>, a nie r\u00f3wnowa\u017cne cz\u0119stotliwo\u015bci sprawdzania bie\u017c\u0105cej jako\u015bci modelu: w pierwszym wariancie odpytujemy API 80 razy, w drugim uruchamiamy codzienn\u0105 regresj\u0119 na kasetach, a ewaluacj\u0119 rzeczywistych modeli przeprowadzamy okresowo. Je\u017celi rzeczywi\u015bcie potrzebujemy 80 pe\u0142nych ewaluacji modelu na \u017cywo, nie zast\u0105pimy ich kasetami bez utraty tej cz\u0119stotliwo\u015bci kontroli.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\"><strong>Du\u017ca skala<\/strong><\/h3>\n\n\n\n<p>Przy wi\u0119kszej skali r\u00f3\u017cnica staje si\u0119 bardziej widoczna. Tysi\u0105c scenariuszy, trzy modele i 80 uruchomie\u0144 to ju\u017c 240 000 wywo\u0142a\u0144. Je\u015bli jedno \u017c\u0105danie zawiera przyk\u0142adowo 8000 token\u00f3w wej\u015bciowych i generuje 400 wyj\u015bciowych, a u\u015brednione stawki dla wybranej mieszanki modeli wynosz\u0105 1 USD za milion wej\u015bciowych i 5 USD za milion wyj\u015bciowych, koszt jednego wywo\u0142ania wynosi 0,01 USD, a wszystkich \u2013 2400 USD. To nie jest cennik GPT-6 Luna ani pomiar konkretnego wdro\u017cenia, lecz jawnie zdefiniowany wariant dla bardziej kosztownych modeli i d\u0142ugiego kontekstu.<\/p>\n\n\n\n<p>Rzeczywiste koszty mog\u0105 by\u0107 ni\u017csze dzi\u0119ki cache&#8217;owaniu wsp\u00f3lnego prefiksu promptu, a okresowe ewaluacje mo\u017cna wykonywa\u0107 przez Batch API ze zni\u017ck\u0105 kosztem op\u00f3\u017anionego otrzymania wynik\u00f3w. Takie mechanizmy warto wykorzysta\u0107, ale nie rozwi\u0105zuj\u0105 jednak innego problemu: codzienny test regresyjny uruchamiany przy pull reque\u015bcie powinien mo\u017cliwie szybko zwr\u00f3ci\u0107 powtarzalny wynik bez zale\u017cno\u015bci od zewn\u0119trznego API.<\/p>\n\n\n\n<p>Przedstawione liczby obejmuj\u0105 wy\u0142\u0105cznie hipotetyczne koszty wywo\u0142a\u0144 modeli. Nie uwzgl\u0119dniaj\u0105 przygotowania scenariuszy, przegl\u0105dania odpowiedzi, nieudanych pr\u00f3b, utrzymywania kaset ani infrastruktury. Przy bardzo tanich modelach czas pracy zespo\u0142u mo\u017ce kosztowa\u0107 znacznie wi\u0119cej ni\u017c sama inferencja.<\/p>\n\n\n\n<p>W\u0142a\u015bnie dlatego przydatno\u015b\u0107 VCR nie sprowadza si\u0119 do rachunku za tokeny. Testy przestaj\u0105 zale\u017ce\u0107 od chwilowej dost\u0119pno\u015bci dostawcy, mo\u017cna je wykonywa\u0107 lokalnie, a analiza b\u0142\u0119d\u00f3w staje si\u0119 prostsza, poniewa\u017c aplikacja za ka\u017cdym razem otrzymuje t\u0119 sam\u0105 odpowied\u017a. Znika te\u017c potrzeba udost\u0119pniania kluczy API w codziennym CI\/CD, co ogranicza ryzyko przypadkowych wywo\u0142a\u0144 i u\u0142atwia kontrolowanie wydatk\u00f3w.<\/p>\n\n\n\n<p>Dodatkowo wyniki okresowych ewaluacji mo\u017cemy wykorzysta\u0107 do wyboru modeli dla poszczeg\u00f3lnych kategorii pyta\u0144: ta\u0144szy model mo\u017ce obs\u0142ugiwa\u0107 proste zg\u0142oszenia, bardziej zaawansowany trudniejsze, a niejednoznaczne przypadki wci\u0105\u017c trafia\u0107 do pracownika helpdesku. Nie jest to funkcja kaset samych w sobie, lecz kolejny praktyczny rezultat dobrze prowadzonego procesu testowania.<\/p>\n\n\n<div class=\"nsw-o-blogersii-banner\">\n            <picture>\n            <source srcset=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2026\/04\/Blog-Digital-Desktop_.jpg\" media=\"(min-width: 992px)\" >\n            <source srcset=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2026\/04\/Blog-Digital-Mob_.jpg\" media=\"(min-width: 300px)\" >            <img decoding=\"async\" src=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2026\/04\/Blog-Digital-Desktop_.jpg\" alt=\"\"  class=\"\"  >\n        <\/picture>\n        <div class=\"cnt\">\n                    <div class=\"nsw-m-title-block -h3 -invert  -has-title-margin-bottom-0 -has-title-font-weight-bold\">\n                                <h2 class=\"nsw-m-title-block__title\" >Digital<\/h2>\n                <\/div>\n                            <p class=\"has-nsw-p-4-font-size has-invert-color\">\n                Zwi\u0119ksz zysk i poszerzaj grono zadowolonych klient\u00f3w dzi\u0119ki naszym us\u0142ugom in\u017cynierii oprogramowania, e-commerce, mobile i digital customer experience.\n            <\/p>\n                            <a  href=\"https:\/\/sii.pl\/oferta\/digital\/\" class=\"nsw-a-button -ghost -banner-button\"   >\n        <span>Oferta Digital<\/span>\n    <\/a>\n            <\/div>\n<\/div>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>Podsumowanie<\/strong><\/h2>\n\n\n\n<p>Na pocz\u0105tek nie potrzebujemy rozbudowanej platformy do ewaluacji modeli. Wystarczy wybra\u0107 kilka reprezentatywnych pyta\u0144 z naszej bazy wiedzy, przygotowa\u0107 dla nich oczekiwane wyniki i nagra\u0107 pierwsze odpowiedzi przy u\u017cyciu VCR.py albo WireMock.Net. Nast\u0119pnie mo\u017cemy uruchomi\u0107 testy bez dost\u0119pu do rzeczywistego API, sprawdzi\u0107 zachowanie aplikacji po zmianach kodu i zobaczy\u0107, ile czasu oraz p\u0142atnych wywo\u0142a\u0144 uda\u0142o si\u0119 wyeliminowa\u0107.<\/p>\n\n\n\n<p>Gdy mechanizm zacznie dzia\u0142a\u0107, mo\u017cemy rozszerzy\u0107 go o fingerprinty, wersjonowanie procedur, selektywne od\u015bwie\u017canie kaset i por\u00f3wnywanie r\u00f3\u017cnych modeli. R\u00f3wnolegle warto okresowo uruchamia\u0107 testy na rzeczywistych modelach, aby sprawdza\u0107 ich aktualne zachowanie i uzupe\u0142nia\u0107 scenariusze o nowe pytania u\u017cytkownik\u00f3w. Dzi\u0119ki temu zbi\u00f3r testowy b\u0119dzie rozwija\u0142 si\u0119 razem z aplikacj\u0105 i firmow\u0105 baz\u0105 wiedzy, a ka\u017cda istotna zmiana b\u0119dzie mia\u0142a swoje miejsce w procesie weryfikacji.<\/p>\n\n\n\n<p>Docelowo mo\u017cemy zbudowa\u0107 w ten spos\u00f3b asystenta o mierzonej i kontrolowanej jako\u015bci, kt\u00f3ry stopniowo przejmuje coraz wi\u0119ksz\u0105 cz\u0119\u015b\u0107 typowych pyta\u0144 pracownik\u00f3w, a trudniejsze lub niejednoznaczne przypadki przekazuje cz\u0142owiekowi.<\/p>\n\n\n\n<p>Zamiast wymaga\u0107 od modelu, aby od pierwszego dnia wiedzia\u0142 wszystko i nigdy si\u0119 nie pomyli\u0142, stopniowo poszerzamy zakres zada\u0144, z kt\u00f3rymi potrafi sobie poradzi\u0107. Ka\u017cde nowe zg\u0142oszenie, przy kt\u00f3rym potrzebna by\u0142a pomoc eksperta, mo\u017ce sta\u0107 si\u0119 kolejnym przypadkiem testowym, a po dopracowaniu procedury i potwierdzeniu jako\u015bci odpowiedzi \u2013 kolejn\u0105 spraw\u0105 obs\u0142ugiwan\u0105 automatycznie.<\/p>\n\n\n\n<p>Z czasem udzia\u0142 takich zg\u0142osze\u0144 mo\u017ce wi\u0119c male\u0107, a pracownicy helpdesku b\u0119d\u0105 mogli skupi\u0107 si\u0119 na problemach, kt\u00f3re rzeczywi\u015bcie wymagaj\u0105 ich do\u015bwiadczenia. Co wa\u017cne, mo\u017cemy rozwija\u0107 ten proces w spos\u00f3b kontrolowany, mierzy\u0107 skuteczno\u015b\u0107 odpowiedzi, ogranicza\u0107 koszty testowania i wykazywa\u0107 na podstawie wynik\u00f3w, czy ka\u017cda kolejna zmiana faktycznie poprawia dzia\u0142anie naszego asystenta.<\/p>\n\n\n\n<p>To pozwala stopniowo ogranicza\u0107 udzia\u0142 r\u0119cznej obs\u0142ugi tam, gdzie jako\u015b\u0107 automatycznych decyzji zosta\u0142a ju\u017c potwierdzona.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>Dokumentacja techniczna<\/strong><\/h2>\n\n\n\n<ul class=\"wp-block-list\">\n<li><a href=\"https:\/\/vcrpy.readthedocs.io\/en\/v8.4.0\/usage.html\" target=\"_blank\" rel=\"noopener\" title=\"\" rel=\"nofollow\" >VCR.py \u2013 tryby Record\/Replay<\/a> oraz <a href=\"https:\/\/vcrpy.readthedocs.io\/en\/v8.4.0\/changelog.html\" target=\"_blank\" rel=\"noopener\" title=\"\" rel=\"nofollow\" >zmiany w wydaniu 8.4.0<\/a>.<\/li>\n\n\n\n<li><a href=\"https:\/\/vcrpy.readthedocs.io\/en\/latest\/advanced.html\" target=\"_blank\" rel=\"noopener\" title=\"\" rel=\"nofollow\" >VCR.py \u2013 filtrowanie nagra\u0144 i odpowiedzi<\/a>.<\/li>\n\n\n\n<li><a href=\"https:\/\/wiremock.org\/dotnet\/proxying\/\" target=\"_blank\" rel=\"noopener\" title=\"\" rel=\"nofollow\" >WireMock.Net \u2013 proxy<\/a> i <a href=\"https:\/\/wiremock.org\/dotnet\/mapping\/\" target=\"_blank\" rel=\"noopener\" title=\"\" rel=\"nofollow\" >mapowania \u017c\u0105da\u0144<\/a>.<\/li>\n\n\n\n<li><a href=\"https:\/\/developers.openai.com\/api\/docs\/guides\/structured-outputs\" target=\"_blank\" rel=\"noopener\" title=\"\" rel=\"nofollow\" >OpenAI \u2013 Structured Outputs<\/a> i <a href=\"https:\/\/developers.openai.com\/api\/docs\/pricing\" target=\"_blank\" rel=\"noopener\" title=\"\" rel=\"nofollow\" >cennik API<\/a>.<\/li>\n<\/ul>\n\n\n<div class=\"kk-star-ratings kksr-auto kksr-align-left kksr-valign-bottom\"\n    data-payload='{&quot;align&quot;:&quot;left&quot;,&quot;id&quot;:&quot;35091&quot;,&quot;slug&quot;:&quot;default&quot;,&quot;valign&quot;:&quot;bottom&quot;,&quot;ignore&quot;:&quot;&quot;,&quot;reference&quot;:&quot;auto&quot;,&quot;class&quot;:&quot;&quot;,&quot;count&quot;:&quot;1&quot;,&quot;legendonly&quot;:&quot;&quot;,&quot;readonly&quot;:&quot;&quot;,&quot;score&quot;:&quot;5&quot;,&quot;starsonly&quot;:&quot;&quot;,&quot;best&quot;:&quot;5&quot;,&quot;gap&quot;:&quot;2&quot;,&quot;greet&quot;:&quot;&quot;,&quot;legend&quot;:&quot;5\\\/5&quot;,&quot;size&quot;:&quot;30&quot;,&quot;title&quot;:&quot;Deterministyczne testy aplikacji z LLM: Jak kasety VCR ograniczaj\u0105 koszty i niestabilno\u015b\u0107 CI&quot;,&quot;width&quot;:&quot;159&quot;,&quot;_legend&quot;:&quot;{score}\\\/5&quot;,&quot;font_factor&quot;:&quot;1.25&quot;}'>\n            \n<div class=\"kksr-stars\">\n    \n<div class=\"kksr-stars-inactive\">\n            <div class=\"kksr-star\" data-star=\"1\" style=\"padding-right: 2px\">\n            \n\n<div class=\"kksr-icon\" style=\"width: 30px; height: 30px;\"><\/div>\n        <\/div>\n            <div class=\"kksr-star\" data-star=\"2\" style=\"padding-right: 2px\">\n            \n\n<div class=\"kksr-icon\" style=\"width: 30px; height: 30px;\"><\/div>\n        <\/div>\n            <div class=\"kksr-star\" data-star=\"3\" style=\"padding-right: 2px\">\n            \n\n<div class=\"kksr-icon\" style=\"width: 30px; height: 30px;\"><\/div>\n        <\/div>\n            <div class=\"kksr-star\" data-star=\"4\" style=\"padding-right: 2px\">\n            \n\n<div class=\"kksr-icon\" style=\"width: 30px; height: 30px;\"><\/div>\n        <\/div>\n            <div class=\"kksr-star\" data-star=\"5\" style=\"padding-right: 2px\">\n            \n\n<div class=\"kksr-icon\" style=\"width: 30px; height: 30px;\"><\/div>\n        <\/div>\n    <\/div>\n    \n<div class=\"kksr-stars-active\" style=\"width: 159px;\">\n            <div class=\"kksr-star\" style=\"padding-right: 2px\">\n            \n\n<div class=\"kksr-icon\" style=\"width: 30px; height: 30px;\"><\/div>\n        <\/div>\n            <div class=\"kksr-star\" style=\"padding-right: 2px\">\n            \n\n<div class=\"kksr-icon\" style=\"width: 30px; height: 30px;\"><\/div>\n        <\/div>\n            <div class=\"kksr-star\" style=\"padding-right: 2px\">\n            \n\n<div class=\"kksr-icon\" style=\"width: 30px; height: 30px;\"><\/div>\n        <\/div>\n            <div class=\"kksr-star\" style=\"padding-right: 2px\">\n            \n\n<div class=\"kksr-icon\" style=\"width: 30px; height: 30px;\"><\/div>\n        <\/div>\n            <div class=\"kksr-star\" style=\"padding-right: 2px\">\n            \n\n<div class=\"kksr-icon\" style=\"width: 30px; height: 30px;\"><\/div>\n        <\/div>\n    <\/div>\n<\/div>\n                \n\n<div class=\"kksr-legend\" style=\"font-size: 24px;\">\n            5\/5    <\/div>\n    <\/div>\n","protected":false},"excerpt":{"rendered":"<p>Coraz wi\u0119cej odpowiedzialno\u015bci za wyszukiwanie i interpretowanie informacji powierzamy sztucznej inteligencji. Przez dobrze ju\u017c znane okienko ulubionego czatu pytamy o &hellip; <a class=\"continued-btn\" href=\"https:\/\/sii.pl\/blog\/deterministyczne-testy-aplikacji-z-llm-jak-kasety-vcr-ograniczaja-koszty-i-niestabilnosc-ci\/\">Continued<\/a><\/p>\n","protected":false},"author":833,"featured_media":35095,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"_editorskit_title_hidden":false,"_editorskit_reading_time":0,"_editorskit_is_block_options_detached":false,"_editorskit_block_options_position":"{}","inline_featured_image":false,"footnotes":""},"categories":[1314],"tags":[2863,2427,1512,1026,682,1032],"class_list":["post-35091","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-development-na-twardo","tag-llm","tag-digital","tag-poradnik","tag-api","tag-artificial-intelligence","tag-case-study"],"acf":[],"aioseo_notices":[],"aioseo_head":"\n\t\t<!-- All in One SEO 4.9.9 - aioseo.com -->\n\t<meta name=\"description\" content=\"Jak: sprawdzi\u0107, czy system poprawnie interpretuje procedury, wykrywa\u0107 problemy jeszcze przed wdro\u017ceniem i ograniczy\u0107 liczb\u0119 b\u0142\u0119d\u00f3w po aktualizacji dokumentacji\" \/>\n\t<meta name=\"robots\" content=\"max-image-preview:large\" \/>\n\t<meta name=\"author\" content=\"Pawe\u0142 Maciejewski\"\/>\n\t<link rel=\"canonical\" href=\"https:\/\/sii.pl\/blog\/deterministyczne-testy-aplikacji-z-llm-jak-kasety-vcr-ograniczaja-koszty-i-niestabilnosc-ci\/\" \/>\n\t<meta name=\"generator\" content=\"All in One SEO (AIOSEO) 4.9.9\" \/>\n\t\t<meta property=\"og:locale\" content=\"pl_PL\" \/>\n\t\t<meta property=\"og:site_name\" content=\"blogersii\" \/>\n\t\t<meta property=\"og:type\" content=\"article\" \/>\n\t\t<meta property=\"og:title\" content=\"Jak kasety VCR ograniczaj\u0105 koszty i niestabilno\u015b\u0107 CI\" \/>\n\t\t<meta property=\"og:description\" content=\"Jak: sprawdzi\u0107, czy system poprawnie interpretuje procedury, wykrywa\u0107 problemy jeszcze przed wdro\u017ceniem i ograniczy\u0107 liczb\u0119 b\u0142\u0119d\u00f3w po aktualizacji dokumentacji\" \/>\n\t\t<meta property=\"og:url\" content=\"https:\/\/sii.pl\/blog\/deterministyczne-testy-aplikacji-z-llm-jak-kasety-vcr-ograniczaja-koszty-i-niestabilnosc-ci\/\" \/>\n\t\t<meta property=\"og:image\" content=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2026\/11\/AI-1.jpg\" \/>\n\t\t<meta property=\"og:image:secure_url\" content=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2026\/11\/AI-1.jpg\" \/>\n\t\t<meta property=\"og:image:width\" content=\"733\" \/>\n\t\t<meta property=\"og:image:height\" content=\"456\" \/>\n\t\t<meta property=\"article:published_time\" content=\"2026-11-10T04:00:00+00:00\" \/>\n\t\t<meta property=\"article:modified_time\" content=\"2026-11-09T15:20:41+00:00\" \/>\n\t\t<meta property=\"article:publisher\" content=\"https:\/\/www.facebook.com\/Sii.Polska\" \/>\n\t\t<meta name=\"twitter:card\" content=\"summary\" \/>\n\t\t<meta name=\"twitter:title\" content=\"Jak kasety VCR ograniczaj\u0105 koszty i niestabilno\u015b\u0107 CI\" \/>\n\t\t<meta name=\"twitter:description\" content=\"Jak: sprawdzi\u0107, czy system poprawnie interpretuje procedury, wykrywa\u0107 problemy jeszcze przed wdro\u017ceniem i ograniczy\u0107 liczb\u0119 b\u0142\u0119d\u00f3w po aktualizacji dokumentacji\" \/>\n\t\t<meta name=\"twitter:image\" content=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2026\/11\/AI-1.jpg\" \/>\n\t\t<script type=\"application\/ld+json\" class=\"aioseo-schema\">\n\t\t\t{\"@context\":\"https:\\\/\\\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\\\/\\\/sii.pl\\\/blog\\\/deterministyczne-testy-aplikacji-z-llm-jak-kasety-vcr-ograniczaja-koszty-i-niestabilnosc-ci\\\/#article\",\"name\":\"Jak kasety VCR ograniczaj\\u0105 koszty i niestabilno\\u015b\\u0107 CI\",\"description\":\"Jak: sprawdzi\\u0107, czy system poprawnie interpretuje procedury, wykrywa\\u0107 problemy jeszcze przed wdro\\u017ceniem i ograniczy\\u0107 liczb\\u0119 b\\u0142\\u0119d\\u00f3w po aktualizacji dokumentacji\",\"inLanguage\":\"pl-PL\",\"headline\":\"Deterministyczne testy aplikacji z LLM: Jak kasety VCR ograniczaj\\u0105 koszty i niestabilno\\u015b\\u0107 CI\",\"author\":{\"@id\":\"https:\\\/\\\/sii.pl\\\/blog\\\/author\\\/pmaciejewski1\\\/#author\"},\"publisher\":{\"@id\":\"https:\\\/\\\/sii.pl\\\/#organization\"},\"datePublished\":\"2026-11-10T04:00:00+01:00\",\"dateModified\":\"2026-11-09T15:20:41+01:00\",\"articleSection\":\"Development na twardo, LLM, Digital, Poradnik, API, Artificial Intelligence, Case Study\",\"mainEntityOfPage\":{\"@id\":\"https:\\\/\\\/sii.pl\\\/blog\\\/deterministyczne-testy-aplikacji-z-llm-jak-kasety-vcr-ograniczaja-koszty-i-niestabilnosc-ci\\\/#webpage\"},\"isPartOf\":{\"@id\":\"https:\\\/\\\/sii.pl\\\/blog\\\/deterministyczne-testy-aplikacji-z-llm-jak-kasety-vcr-ograniczaja-koszty-i-niestabilnosc-ci\\\/#webpage\"},\"image\":{\"@type\":\"ImageObject\",\"url\":\"https:\\\/\\\/sii.pl\\\/blog\\\/wp-content\\\/uploads\\\/2026\\\/11\\\/AI-1.jpg\",\"@id\":\"https:\\\/\\\/sii.pl\\\/blog\\\/deterministyczne-testy-aplikacji-z-llm-jak-kasety-vcr-ograniczaja-koszty-i-niestabilnosc-ci\\\/#articleImage\",\"width\":733,\"height\":456,\"caption\":\"Deterministyczne testy aplikacji z LLM: Jak kasety VCR ograniczaj\\u0105 koszty i niestabilno\\u015b\\u0107 CI\"}},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\\\/\\\/sii.pl\\\/blog\\\/deterministyczne-testy-aplikacji-z-llm-jak-kasety-vcr-ograniczaja-koszty-i-niestabilnosc-ci\\\/#breadcrumblist\",\"itemListElement\":[{\"@type\":\"ListItem\",\"@id\":\"https:\\\/\\\/sii.pl\\\/blog#listItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\\\/\\\/sii.pl\\\/blog\",\"nextItem\":{\"@type\":\"ListItem\",\"@id\":\"https:\\\/\\\/sii.pl\\\/blog\\\/development-na-twardo\\\/#listItem\",\"name\":\"Development na twardo\"}},{\"@type\":\"ListItem\",\"@id\":\"https:\\\/\\\/sii.pl\\\/blog\\\/development-na-twardo\\\/#listItem\",\"position\":2,\"name\":\"Development na twardo\",\"item\":\"https:\\\/\\\/sii.pl\\\/blog\\\/development-na-twardo\\\/\",\"nextItem\":{\"@type\":\"ListItem\",\"@id\":\"https:\\\/\\\/sii.pl\\\/blog\\\/deterministyczne-testy-aplikacji-z-llm-jak-kasety-vcr-ograniczaja-koszty-i-niestabilnosc-ci\\\/#listItem\",\"name\":\"Deterministyczne testy aplikacji z LLM: Jak kasety VCR ograniczaj\\u0105 koszty i niestabilno\\u015b\\u0107 CI\"},\"previousItem\":{\"@type\":\"ListItem\",\"@id\":\"https:\\\/\\\/sii.pl\\\/blog#listItem\",\"name\":\"Home\"}},{\"@type\":\"ListItem\",\"@id\":\"https:\\\/\\\/sii.pl\\\/blog\\\/deterministyczne-testy-aplikacji-z-llm-jak-kasety-vcr-ograniczaja-koszty-i-niestabilnosc-ci\\\/#listItem\",\"position\":3,\"name\":\"Deterministyczne testy aplikacji z LLM: Jak kasety VCR ograniczaj\\u0105 koszty i niestabilno\\u015b\\u0107 CI\",\"previousItem\":{\"@type\":\"ListItem\",\"@id\":\"https:\\\/\\\/sii.pl\\\/blog\\\/development-na-twardo\\\/#listItem\",\"name\":\"Development na twardo\"}}]},{\"@type\":\"Organization\",\"@id\":\"https:\\\/\\\/sii.pl\\\/#organization\",\"name\":\"Sii Polska\",\"url\":\"https:\\\/\\\/sii.pl\\\/blog\\\/\",\"sameAs\":[\"https:\\\/\\\/www.instagram.com\\\/siipolska\\\/\",\"https:\\\/\\\/www.tiktok.com\\\/@siipolska\",\"https:\\\/\\\/www.youtube.com\\\/channel\\\/UCQ3UQ7RJPEYnAWC_rPE5Dxg\",\"https:\\\/\\\/www.linkedin.com\\\/company\\\/sii-polska\",\"https:\\\/\\\/clutch.co\\\/profile\\\/sii-poland\",\"https:\\\/\\\/www.crunchbase.com\\\/organization\\\/sii\",\"https:\\\/\\\/www.wikidata.org\\\/wiki\\\/Q16601096\"]},{\"@type\":\"WebPage\",\"@id\":\"https:\\\/\\\/sii.pl\\\/blog\\\/deterministyczne-testy-aplikacji-z-llm-jak-kasety-vcr-ograniczaja-koszty-i-niestabilnosc-ci\\\/#webpage\",\"url\":\"https:\\\/\\\/sii.pl\\\/blog\\\/deterministyczne-testy-aplikacji-z-llm-jak-kasety-vcr-ograniczaja-koszty-i-niestabilnosc-ci\\\/\",\"name\":\"Jak kasety VCR ograniczaj\\u0105 koszty i niestabilno\\u015b\\u0107 CI\",\"description\":\"Jak: sprawdzi\\u0107, czy system poprawnie interpretuje procedury, wykrywa\\u0107 problemy jeszcze przed wdro\\u017ceniem i ograniczy\\u0107 liczb\\u0119 b\\u0142\\u0119d\\u00f3w po aktualizacji dokumentacji\",\"inLanguage\":\"pl-PL\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/sii.pl\\\/blog\\\/#website\"},\"breadcrumb\":{\"@id\":\"https:\\\/\\\/sii.pl\\\/blog\\\/deterministyczne-testy-aplikacji-z-llm-jak-kasety-vcr-ograniczaja-koszty-i-niestabilnosc-ci\\\/#breadcrumblist\"},\"author\":\"https:\\\/\\\/sii.pl\\\/blog\\\/author\\\/pmaciejewski1\\\/#author\",\"creator\":\"https:\\\/\\\/sii.pl\\\/blog\\\/author\\\/pmaciejewski1\\\/#author\",\"image\":{\"@type\":\"ImageObject\",\"url\":\"https:\\\/\\\/sii.pl\\\/blog\\\/wp-content\\\/uploads\\\/2026\\\/11\\\/AI-1.jpg\",\"@id\":\"https:\\\/\\\/sii.pl\\\/blog\\\/deterministyczne-testy-aplikacji-z-llm-jak-kasety-vcr-ograniczaja-koszty-i-niestabilnosc-ci\\\/#mainImage\",\"width\":733,\"height\":456,\"caption\":\"Deterministyczne testy aplikacji z LLM: Jak kasety VCR ograniczaj\\u0105 koszty i niestabilno\\u015b\\u0107 CI\"},\"primaryImageOfPage\":{\"@id\":\"https:\\\/\\\/sii.pl\\\/blog\\\/deterministyczne-testy-aplikacji-z-llm-jak-kasety-vcr-ograniczaja-koszty-i-niestabilnosc-ci\\\/#mainImage\"},\"datePublished\":\"2026-11-10T04:00:00+01:00\",\"dateModified\":\"2026-11-09T15:20:41+01:00\"},{\"@type\":\"WebSite\",\"@id\":\"https:\\\/\\\/sii.pl\\\/blog\\\/#website\",\"url\":\"https:\\\/\\\/sii.pl\\\/blog\\\/\",\"name\":\"blogersii\",\"description\":\"Blog technologiczny Sii \\u2013 praktyczna wiedza od ekspert\\u00f3w\",\"inLanguage\":\"pl-PL\",\"publisher\":{\"@id\":\"https:\\\/\\\/sii.pl\\\/#organization\"}}]}\n\t\t<\/script>\n\t\t<!-- All in One SEO -->\n\n","aioseo_head_json":{"title":"Jak kasety VCR ograniczaj\u0105 koszty i niestabilno\u015b\u0107 CI","description":"Jak: sprawdzi\u0107, czy system poprawnie interpretuje procedury, wykrywa\u0107 problemy jeszcze przed wdro\u017ceniem i ograniczy\u0107 liczb\u0119 b\u0142\u0119d\u00f3w po aktualizacji dokumentacji","canonical_url":"https:\/\/sii.pl\/blog\/deterministyczne-testy-aplikacji-z-llm-jak-kasety-vcr-ograniczaja-koszty-i-niestabilnosc-ci\/","robots":"max-image-preview:large","keywords":"","webmasterTools":{"miscellaneous":""},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/sii.pl\/blog\/deterministyczne-testy-aplikacji-z-llm-jak-kasety-vcr-ograniczaja-koszty-i-niestabilnosc-ci\/#article","name":"Jak kasety VCR ograniczaj\u0105 koszty i niestabilno\u015b\u0107 CI","description":"Jak: sprawdzi\u0107, czy system poprawnie interpretuje procedury, wykrywa\u0107 problemy jeszcze przed wdro\u017ceniem i ograniczy\u0107 liczb\u0119 b\u0142\u0119d\u00f3w po aktualizacji dokumentacji","inLanguage":"pl-PL","headline":"Deterministyczne testy aplikacji z LLM: Jak kasety VCR ograniczaj\u0105 koszty i niestabilno\u015b\u0107 CI","author":{"@id":"https:\/\/sii.pl\/blog\/author\/pmaciejewski1\/#author"},"publisher":{"@id":"https:\/\/sii.pl\/#organization"},"datePublished":"2026-11-10T04:00:00+01:00","dateModified":"2026-11-09T15:20:41+01:00","articleSection":"Development na twardo, LLM, Digital, Poradnik, API, Artificial Intelligence, Case Study","mainEntityOfPage":{"@id":"https:\/\/sii.pl\/blog\/deterministyczne-testy-aplikacji-z-llm-jak-kasety-vcr-ograniczaja-koszty-i-niestabilnosc-ci\/#webpage"},"isPartOf":{"@id":"https:\/\/sii.pl\/blog\/deterministyczne-testy-aplikacji-z-llm-jak-kasety-vcr-ograniczaja-koszty-i-niestabilnosc-ci\/#webpage"},"image":{"@type":"ImageObject","url":"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2026\/11\/AI-1.jpg","@id":"https:\/\/sii.pl\/blog\/deterministyczne-testy-aplikacji-z-llm-jak-kasety-vcr-ograniczaja-koszty-i-niestabilnosc-ci\/#articleImage","width":733,"height":456,"caption":"Deterministyczne testy aplikacji z LLM: Jak kasety VCR ograniczaj\u0105 koszty i niestabilno\u015b\u0107 CI"}},{"@type":"BreadcrumbList","@id":"https:\/\/sii.pl\/blog\/deterministyczne-testy-aplikacji-z-llm-jak-kasety-vcr-ograniczaja-koszty-i-niestabilnosc-ci\/#breadcrumblist","itemListElement":[{"@type":"ListItem","@id":"https:\/\/sii.pl\/blog#listItem","position":1,"name":"Home","item":"https:\/\/sii.pl\/blog","nextItem":{"@type":"ListItem","@id":"https:\/\/sii.pl\/blog\/development-na-twardo\/#listItem","name":"Development na twardo"}},{"@type":"ListItem","@id":"https:\/\/sii.pl\/blog\/development-na-twardo\/#listItem","position":2,"name":"Development na twardo","item":"https:\/\/sii.pl\/blog\/development-na-twardo\/","nextItem":{"@type":"ListItem","@id":"https:\/\/sii.pl\/blog\/deterministyczne-testy-aplikacji-z-llm-jak-kasety-vcr-ograniczaja-koszty-i-niestabilnosc-ci\/#listItem","name":"Deterministyczne testy aplikacji z LLM: Jak kasety VCR ograniczaj\u0105 koszty i niestabilno\u015b\u0107 CI"},"previousItem":{"@type":"ListItem","@id":"https:\/\/sii.pl\/blog#listItem","name":"Home"}},{"@type":"ListItem","@id":"https:\/\/sii.pl\/blog\/deterministyczne-testy-aplikacji-z-llm-jak-kasety-vcr-ograniczaja-koszty-i-niestabilnosc-ci\/#listItem","position":3,"name":"Deterministyczne testy aplikacji z LLM: Jak kasety VCR ograniczaj\u0105 koszty i niestabilno\u015b\u0107 CI","previousItem":{"@type":"ListItem","@id":"https:\/\/sii.pl\/blog\/development-na-twardo\/#listItem","name":"Development na twardo"}}]},{"@type":"Organization","@id":"https:\/\/sii.pl\/#organization","name":"Sii Polska","url":"https:\/\/sii.pl\/blog\/","sameAs":["https:\/\/www.instagram.com\/siipolska\/","https:\/\/www.tiktok.com\/@siipolska","https:\/\/www.youtube.com\/channel\/UCQ3UQ7RJPEYnAWC_rPE5Dxg","https:\/\/www.linkedin.com\/company\/sii-polska","https:\/\/clutch.co\/profile\/sii-poland","https:\/\/www.crunchbase.com\/organization\/sii","https:\/\/www.wikidata.org\/wiki\/Q16601096"]},{"@type":"WebPage","@id":"https:\/\/sii.pl\/blog\/deterministyczne-testy-aplikacji-z-llm-jak-kasety-vcr-ograniczaja-koszty-i-niestabilnosc-ci\/#webpage","url":"https:\/\/sii.pl\/blog\/deterministyczne-testy-aplikacji-z-llm-jak-kasety-vcr-ograniczaja-koszty-i-niestabilnosc-ci\/","name":"Jak kasety VCR ograniczaj\u0105 koszty i niestabilno\u015b\u0107 CI","description":"Jak: sprawdzi\u0107, czy system poprawnie interpretuje procedury, wykrywa\u0107 problemy jeszcze przed wdro\u017ceniem i ograniczy\u0107 liczb\u0119 b\u0142\u0119d\u00f3w po aktualizacji dokumentacji","inLanguage":"pl-PL","isPartOf":{"@id":"https:\/\/sii.pl\/blog\/#website"},"breadcrumb":{"@id":"https:\/\/sii.pl\/blog\/deterministyczne-testy-aplikacji-z-llm-jak-kasety-vcr-ograniczaja-koszty-i-niestabilnosc-ci\/#breadcrumblist"},"author":"https:\/\/sii.pl\/blog\/author\/pmaciejewski1\/#author","creator":"https:\/\/sii.pl\/blog\/author\/pmaciejewski1\/#author","image":{"@type":"ImageObject","url":"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2026\/11\/AI-1.jpg","@id":"https:\/\/sii.pl\/blog\/deterministyczne-testy-aplikacji-z-llm-jak-kasety-vcr-ograniczaja-koszty-i-niestabilnosc-ci\/#mainImage","width":733,"height":456,"caption":"Deterministyczne testy aplikacji z LLM: Jak kasety VCR ograniczaj\u0105 koszty i niestabilno\u015b\u0107 CI"},"primaryImageOfPage":{"@id":"https:\/\/sii.pl\/blog\/deterministyczne-testy-aplikacji-z-llm-jak-kasety-vcr-ograniczaja-koszty-i-niestabilnosc-ci\/#mainImage"},"datePublished":"2026-11-10T04:00:00+01:00","dateModified":"2026-11-09T15:20:41+01:00"},{"@type":"WebSite","@id":"https:\/\/sii.pl\/blog\/#website","url":"https:\/\/sii.pl\/blog\/","name":"blogersii","description":"Blog technologiczny Sii \u2013 praktyczna wiedza od ekspert\u00f3w","inLanguage":"pl-PL","publisher":{"@id":"https:\/\/sii.pl\/#organization"}}]},"og:locale":"pl_PL","og:site_name":"blogersii","og:type":"article","og:title":"Jak kasety VCR ograniczaj\u0105 koszty i niestabilno\u015b\u0107 CI","og:description":"Jak: sprawdzi\u0107, czy system poprawnie interpretuje procedury, wykrywa\u0107 problemy jeszcze przed wdro\u017ceniem i ograniczy\u0107 liczb\u0119 b\u0142\u0119d\u00f3w po aktualizacji dokumentacji","og:url":"https:\/\/sii.pl\/blog\/deterministyczne-testy-aplikacji-z-llm-jak-kasety-vcr-ograniczaja-koszty-i-niestabilnosc-ci\/","og:image":"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2026\/11\/AI-1.jpg","og:image:secure_url":"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2026\/11\/AI-1.jpg","og:image:width":733,"og:image:height":456,"article:published_time":"2026-11-10T04:00:00+00:00","article:modified_time":"2026-11-09T15:20:41+00:00","article:publisher":"https:\/\/www.facebook.com\/Sii.Polska","twitter:card":"summary","twitter:title":"Jak kasety VCR ograniczaj\u0105 koszty i niestabilno\u015b\u0107 CI","twitter:description":"Jak: sprawdzi\u0107, czy system poprawnie interpretuje procedury, wykrywa\u0107 problemy jeszcze przed wdro\u017ceniem i ograniczy\u0107 liczb\u0119 b\u0142\u0119d\u00f3w po aktualizacji dokumentacji","twitter:image":"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2026\/11\/AI-1.jpg"},"aioseo_meta_data":{"post_id":"35091","title":"Jak kasety VCR ograniczaj\u0105 koszty i niestabilno\u015b\u0107 CI","description":"Jak: sprawdzi\u0107, czy system poprawnie interpretuje procedury, wykrywa\u0107 problemy jeszcze przed wdro\u017ceniem i ograniczy\u0107 liczb\u0119 b\u0142\u0119d\u00f3w po aktualizacji dokumentacji","keywords":null,"keyphrases":{"focus":{"keyphrase":"","score":0,"analysis":{"keyphraseInTitle":{"score":0,"maxScore":9,"error":1}}},"additional":[]},"primary_term":null,"canonical_url":null,"og_title":null,"og_description":null,"og_object_type":"default","og_image_type":"default","og_image_url":null,"og_image_width":null,"og_image_height":null,"og_image_custom_url":null,"og_image_custom_fields":null,"og_video":"","og_custom_url":null,"og_article_section":null,"og_article_tags":null,"twitter_use_og":false,"twitter_card":"default","twitter_image_type":"default","twitter_image_url":null,"twitter_image_custom_url":null,"twitter_image_custom_fields":null,"twitter_title":null,"twitter_description":null,"schema":{"blockGraphs":[],"customGraphs":[],"default":{"data":{"Article":[],"Course":[],"Dataset":[],"FAQPage":[],"Movie":[],"Person":[],"Product":[],"ProductReview":[],"Car":[],"Recipe":[],"Service":[],"SoftwareApplication":[],"WebPage":[]},"graphName":"Article","isEnabled":true},"graphs":[]},"schema_type":"default","schema_type_options":null,"pillar_content":false,"robots_default":true,"robots_noindex":false,"robots_noarchive":false,"robots_nosnippet":false,"robots_nofollow":false,"robots_noimageindex":false,"robots_noodp":false,"robots_notranslate":false,"robots_max_snippet":"-1","robots_max_videopreview":"-1","robots_max_imagepreview":"large","priority":null,"frequency":"default","local_seo":null,"limit_modified_date":false,"created":"2026-11-09 14:50:17","updated":"2026-11-10 04:25:05","ai":{"faqs":[],"keyPoints":[],"schemas":[],"titles":[],"descriptions":[],"socialPosts":{"email":{"subject":"","preview":"","content":""},"linkedin":[],"twitter":[],"facebook":[],"instagram":[]}},"breadcrumb_settings":null,"seo_analyzer_scan_date":null},"aioseo_breadcrumb":"<div class=\"aioseo-breadcrumbs\"><span class=\"aioseo-breadcrumb\">\n\t\t\t<a href=\"https:\/\/sii.pl\/blog\" title=\"Home\">Home<\/a>\n\t\t<\/span><span class=\"aioseo-breadcrumb-separator\">&raquo;<\/span><span class=\"aioseo-breadcrumb\">\n\t\t\t<a href=\"https:\/\/sii.pl\/blog\/development-na-twardo\/\" title=\"Development na twardo\">Development na twardo<\/a>\n\t\t<\/span><span class=\"aioseo-breadcrumb-separator\">&raquo;<\/span><span class=\"aioseo-breadcrumb\">\n\t\t\tDeterministyczne testy aplikacji z LLM: Jak kasety VCR ograniczaj\u0105 koszty i niestabilno\u015b\u0107 CI\n\t\t<\/span><\/div>","aioseo_breadcrumb_json":[{"label":"Home","link":"https:\/\/sii.pl\/blog"},{"label":"Development na twardo","link":"https:\/\/sii.pl\/blog\/development-na-twardo\/"},{"label":"Deterministyczne testy aplikacji z LLM: Jak kasety VCR ograniczaj\u0105 koszty i niestabilno\u015b\u0107 CI","link":"https:\/\/sii.pl\/blog\/deterministyczne-testy-aplikacji-z-llm-jak-kasety-vcr-ograniczaja-koszty-i-niestabilnosc-ci\/"}],"republish_history":[],"featured_media_url":"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2026\/11\/AI-1.jpg","category_names":["Development na twardo"],"_links":{"self":[{"href":"https:\/\/sii.pl\/blog\/wp-json\/wp\/v2\/posts\/35091","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/sii.pl\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/sii.pl\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/sii.pl\/blog\/wp-json\/wp\/v2\/users\/833"}],"replies":[{"embeddable":true,"href":"https:\/\/sii.pl\/blog\/wp-json\/wp\/v2\/comments?post=35091"}],"version-history":[{"count":1,"href":"https:\/\/sii.pl\/blog\/wp-json\/wp\/v2\/posts\/35091\/revisions"}],"predecessor-version":[{"id":35092,"href":"https:\/\/sii.pl\/blog\/wp-json\/wp\/v2\/posts\/35091\/revisions\/35092"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/sii.pl\/blog\/wp-json\/wp\/v2\/media\/35095"}],"wp:attachment":[{"href":"https:\/\/sii.pl\/blog\/wp-json\/wp\/v2\/media?parent=35091"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/sii.pl\/blog\/wp-json\/wp\/v2\/categories?post=35091"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/sii.pl\/blog\/wp-json\/wp\/v2\/tags?post=35091"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}