Osiem miesięcy w kolejce do scalania (merge queue) w GitHub Actions uczy czegoś, czego nigdy nie nauczą cię macierze porównawcze funkcji. Framework może oferować pięćdziesiąt metryk, przepiękne pulpity nawigacyjne i cytaty z szanowanych laboratoriów badawczych. Jeśli jednak blokuje wdrożenie, ponieważ wynik „vibe check” zmienił się z 0,72 na 0,68 przy identycznym kodzie, jest gorszy niż bezużyteczny. Staje się aktywnym zagrożeniem dla tempa dostarczania oprogramowania (shipping velocity).
To jest filtr, który większość zestawień dotyczących ewaluacji LLM pomija. Liczą one możliwości. Rzadko zadają jedyne pytanie, które ma znaczenie w kolejce do scalania: czy to sprawdzenie przechodzi i kończy się niepowodzeniem dokładnie w ten sam sposób za każdym razem, gdy jest uruchamiane?
Nauczyłem się tego, wykonując niekomfortową pracę. Podłączyłem sześć open-source'owych frameworków do ewaluacji LLM do prawdziwego potoku CI. Przez osiem miesięcy działały one na żywych pull requestach produkcyjnych. Dwa z nich zyskały prawo do pozostania jako strażnicy (gatekeepers). Reszta została zdegradowana do roli doradczych pulpitów nawigacyjnych, przeniesiona do zadań nocnych (nightly jobs) lub całkowicie usunięta. Lekcja była bolesna i kosztowna: deterministyczna struktura wygrywa z probabilistyczną jakością, gdy strzeżesz głównej gałęzi (main branch).
Prawdziwe zadanie bramki scalania
Bramka CI to nie środowisko badawcze. To bramkarz. Jej jedynym celem jest spojrzenie na konkretną zmianę i udzielenie odpowiedzi „tak” lub „nie”. Tak, ten PR może dołączyć do głównej gałęzi. Nie, nie może. Odpowiedź ta musi pojawić się w ciągu sekund, kosztować grosze i nigdy nie zmieniać się wstecznie. Jeśli ponownie uruchomisz ten sam potok dla tego samego commita w spokojny wtorek i w gorączkowy piątek, wynik musi być identyczny.
To tutaj większość frameworków do ewaluacji LLM wykłada się na plecy. Są budowane przez naukowców danych (data scientists) dla naukowców danych. Optymalizują pod kątem wglądu, eksploracji i niuansowania wyników. Kolejka do scalania optymalizuje decyzje binarne, szybkość i zerową niestabilność (flakiness). Te dwa cele pokrywają się tylko częściowo.
Dlaczego LLM-as-Judge psuje kolejkę
Narzędzia, które zawiodły w moim teście, miały jedną wspólną wadę projektową: zbyt mocno polegały na wywołaniach LLM-as-judge jako głównym mechanizmie bramkowania.
Prompt typu LLM-as-judge prosi model o ocenę wyniku w skali od jednego do dziesięciu, wybranie lepszej z dwóch odpowiedzi lub ocenę poprawności faktograficznej. Podejście to jest potężne w zrozumieniu trendów jakościowych. Jest jednak trucizną dla blokującej kontroli CI. Ten sam input może wygenerować różne wyniki w różne dni, ponieważ temperatura, wersjonowanie modelu i formatowanie promptu wprowadzają szum. Gdy wynik ten jest powiązany ze sztywnym progiem i sztywnym kodem wyjścia (exit code), Twoja kolejka blokuje się z powodu „duchów”.
Awaria kaskadowo rozprzestrzenia się szybko. Niedeterministyczne sprawdzenie powoduje zatory w kolejce. Inżynierowie uczą się ponawiać próby, aż wynik będzie korzystny, co uczy zespół ignorowania czerwonych buildów. Koszty tokenów rosną, ponieważ każda ponowna próba zużywa więcej kredytów API. Co najgorsze, sygnał staje się bez znaczenia. Czerwony build powinien oznaczać „wprowadziłeś błąd”. Jeśli oznacza „model sędzia obudził się dziś wybredny”, zaufanie zostaje nadszarpnięte.
Co robią ocalali
Promptfoo i DeepEval przetrwały, ponieważ traktują sprawdzenia deterministyczne jako obywatele pierwszej kategorii, a wyniki sędziego LLM jako wtórne, nieblokujące sygnały. Rozumieją, że bramka potrzebuje kodu wyjścia, a nie liczby zmiennoprzecinkowej z własną opinią.
Promptfoo, wydany na licencji MIT, jest zbudowany z myślą o linii komend. Wykonuje asercje, takie jak dopasowania regex, walidację schematu JSON, sprawdzenia zawartości (contains) i dokładne porównania ciągów znaków. Nie są to wyrafinowane funkcje. To po prostu ulepszone polecenia grep i jq. Właśnie dlatego działają w CI. Wyrażenie regularne albo pasuje, albo nie. Schemat JSON albo przechodzi walidację, albo rzuca błędem. Promptfoo zwraca standardowe kody wyjścia Unix, dzięki czemu GitHub Actions natywnie rozumie, kiedy przerwać scalanie. Jest niezależny od języka, ponieważ działa jako narzędzie CLI. Nie musisz instalować ekosystemu Pythona w repozytorium usługi Node.js tylko po to, aby walidować wyniki.
DeepEval, na licencji Apache 2.0, jest wyborem dla zespołów Pythonowych. Integruje się tak jak pytest. Piszesz testy w znajomej składni, a niepowodzenie naturalnie blokuje cały zestaw testów. DeepEval oferuje ogromny katalog metryk, ale kluczowym szczegółem jest to, że należy ich używać ostrożnie. W przypadku bramek polegaj na metrykach deterministycznych lub heurystycznych. Jeśli wprowadzasz G-Eval lub inne metody oparte na sędziach, opakuj je w nieblokujące generatory raportów zamiast stosować sztywne asercje (hard asserts). Stosowany w ten sposób, DeepEval zapewnia ergonomię frameworka testowego bez niestabilności typowej dla notatników badawczych.
Gdzie pasują pozostałe cztery
Cztery frameworki, które nie przetrwały jako bramki, wciąż mają wartość. Po prostu należą do innych części Twojego łańcucha narzędzi (toolchain).
Future AGI (Apache 2.0) dostarcza ponad pięćdziesiąt metryk i jest skierowany do zespołów budujących własne SDK. Metryki są wyczerpujące. Problem polega na tym, że narzędzie wymaga napisania własnego mechanizmu (harness), aby uruchomić je w kolejce CI. W kontekście badawczym jest to rozsądny kompromis. W kolejce merge każda warstwa własnego okablowania to nowe źródło niestabilności. To sprawny silnik ewaluacyjny, ale nie gotowy strażnik (gatekeeper).
RAGAS (Apache 2.0) doskonale radzi sobie z mierzeniem jakości generowania wspomaganego wyszukiwaniem (RAG). Jego metryki wierności (faithfulness) i trafności odpowiedzi (answer relevance) są naprawdę przydatne do zrozumienia, jak baza wiedzy radzi sobie w czasie. Niestety, metryki te w dużym stopniu opierają się na sędziach LLM. Są doskonałe dla nocnych zadań kontroli jakości, które publikują trendy na Slacku. Słabo sprawdzają się jednak jako „strażnicy” przy pull requestach. Przenieś RAGAS do harmonogramowanego potoku analizy, a nie do mechanizmów blokujących merge.
Arize Phoenix posiada licencję Elastic License 2.0 i znajduje się w zupełnie innym punkcie styku. Łączy rozproszone śledzenie (distributed tracing) z ewaluacją, zapewniając obserwowalność (observability) w zakresie tego, dlaczego model zachował się w określony sposób. Potrzebujesz tego podczas debugowania incydentu produkcyjnego lub śledzenia halucynacji aż do błędnego fragmentu wyszukanego w bazie. Nie chcesz jednak, aby narzędzie do śledzenia decydowało o tym, czy gałąź (feature branch) młodszego programisty może zostać wdrożona. Jego architektura została zbudowana z myślą o uzyskiwaniu wglądu, a nie o binarnych bramkach.
MLflow Evaluate (Apache 2.0) wywodzi swoje pochodzenie z monitorowania eksperymentów. Jest ciężkie. Wprowadzenie go do lekkiego obrazu CI dodaje czas uruchamiania i zależności, które spowalniają każde zadanie. Jeśli absolutnie musisz go używać wewnątrz potoku, trzymaj się jego metryk heurystycznych do kontroli strukturalnej. Nawet wtedy walczysz z fundamentalnym projektem tego frameworka. MLflow chce logować przebiegi i porównywać eksperymenty na przestrzeni tygodni. Kolejka merge potrzebuje werdyktu w mniej niż minutę.
Praktyczne zasady bramkowania
Jeśli nie wyniesiesz z tego eksperymentu niczego innego, zapamiętaj te trzy zasady.
Po pierwsze, bramkuj strukturę, a nie „vibe”. Możesz wymusić, aby wyjście było poprawnym formatem JSON. Możesz wymusić obecność wymaganych kluczy. Możesz wymusić, aby etykieta klasyfikacji należała do dozwolonego wyliczenia (enum). Te kontrole są szybkie, tanie i deterministyczne. Nie możesz niezawodnie wymusić, aby podsumowanie było „przyjazne” lub aby parafraza była „kreatywna”. Te cechy należą do recenzji ludzkiej lub okresowej ewaluacji wsadowej, a nie do zautomatyzowanych bramek.
Po drugie, jeśli wynik zmienia się przy niezmienionym wejściu, natychmiast go zdegraduj. Uruchom zestaw ewaluacyjny dwukrotnie na dokładnie tym samym artefakcie. Jeśli jakakolwiek metryka zmieni się ze stanu „pass” na „fail”, traci ona prawo do blokowania merge'a. Przenieś ją do panelu doradczego (advisory dashboard), gdzie zmienność jest oczekiwana i dopuszczalna.
Po trzecie, szanuj kod wyjścia. Ładny raport HTML z czerwonym banerem nie zatrzyma merge'a. Zrobi to kod wyjścia różny od zera. Twoje narzędzie ewaluacyjne musi mówić w natywnym języku Twojej platformy CI. Standardowe wyjście (stdout) jest dla ludzi. Kody wyjścia są dla maszyn.
Wnioski
Wciąż jesteśmy na wczesnym etapie ustalania, jak testować aplikacje oparte na LLM. Pokusą jest traktowanie ewaluacji jak ludzkiej skali ocen: niuansowej, kontekstowej i nieco subiektywnej. To działa w pracy naukowej. W kolejce merge to się załamuje.
Po ośmiu miesiącach ruchu produkcyjnego mój potok uruchamia teraz Promptfoo do asercji strukturalnych i schematów w różnych usługach oraz DeepEval do kontroli behawioralnych po stronie Pythona, które łatwo mapują się na warunki pass-fail. Wszystko inne raportuje do nocnych pulpitów nawigacyjnych. Kolejka jest stabilna. Sygnał jest czysty. Zespół znów ufa czerwonym buildom.
Nie potrzebujesz więcej metryk przy swojej bramce. Potrzebujesz mniej metryk, które za każdym razem mówią prawdę.
*Na podstawie oryginalnych testów i artykułu udostępnionego na Dev.to. Aby dołączyć do dyskusji na temat budowania niezawodnych systemów AI, dołącz do [społeczności GyaanSetu na Telegramie](
