Minimalna platforma do eksperymentowania dla agentów AI: testy A/B i testy w tle
Jak zdecydować, czy wersja agenta B powinna zastąpić A, wykorzystując definicje pipeline’ów, lokalne przypisanie, zdarzenia ekspozycji, ślady działania, ocenę w środowisku sandbox, testy w ukryciu oraz zasady ograniczające.
Źródła: A minimum viable experimentation platform for AI agents
Ocena, czy nowa wersja agenta jest lepsza od tej używanej w produkcji, jest trudniejsza, niż się wydaje. W fazie rozwoju jest to do opanowania: wystarczy przygotować zestaw przypadków testowych, uruchomić obie wersje, przeanalizować kilka wyników, poprosić inny model o ocenę rezultatów i zdecydować, czy ta zmiana wymaga dalszych prac. Sytuacja komplikuje się, gdy celem nie jest prosty prompt czy pojedyncze wezwanie modelu, lecz system, którego komponenty zmieniają się według własnych harmonogramów i wchodzą ze sobą w interakcje, których sens nie zawsze jest oczywisty.
Załóżmy agenta do obsługi klientów składającego się z tych elementów:
agent runtime
memory and retrieval
system prompt
model
tools
guardrails
application logic
Załóżmy, że strategia wyszukiwania ulegnie zmianie i stopień ukończenia zadań na zbiorze testowym wzrośnie. Agent może rzeczywiście być lepszy. Może też wykonywać więcej wywołań modelu, co kosztuje o 20 procent więcej, zachowywać się inaczej wobec rzeczywistych użytkowników lub czerpać korzyści z jakiegoś innego komponentu, który przypadkowo uległ zmianie w tym samym oknie czasowym. W takim przypadku zadanie przypomina mniej ocenę modelu, a bardziej eksperyment.
Poniżej znajduje się zarys najprostszej platformy, na którą można by polegać przy zadaniu dość prostym:
should version B replace version A?
To ma sens tylko w określonej skali. Jeden agent, dwadzieścia ręcznie sprawdzonych przypadków testowych oraz garstka użytkowników wewnętrznych nie potrzebuje większości z tych funkcji. Platforma staje się przydatna, gdy kilka zespołów niezależnie modyfikuje modele, prompty, pamięć, narzędzia oraz implementacje agentów, eksperymenty są prowadzone bez przerwy, a ktoś musi ostatecznie zatwierdzić zmianę w produkcji, posługując się dowodami, które nadal są czytelne nawet po kilku miesiącach. Platforma agentów dostępna dla całej firmy odpowiada temu opisowi.
Czym właściwie jest B
Pierwszy problem jest podstawowy. Załóżmy, że eksperyment stwierdza:
A = agent-v17
B = agent-v18
Czym dokładnie jest agent-v18? Jeśli prompt zmieni się w trakcie eksperymentu, indeks wyszukiwania zostanie odbudowany, a brama modelu zamieni swój domyślny model, to etykieta prawie nic nie mówi o tym, co zostało ocenione. To szczególnie dotkliwie wpływa na agenty, ponieważ zachowanie widziane na końcu pochodzi z całego procesu, a nie z pojedynczej, izolowanej wywołania modelu.
Dlatego niezmienne definicje procesów stanowią obiekt oceny. Mogą wyglądać w ten sposób:
pipeline:
name: support-agent
version: 18
components:
runtime:
artifact: agent-runtime@sha256:...
memory:
artifact: memory-service@sha256:...
configuration:
strategy: hybrid
top_k: 8
model:
route: support
model: provider/model-x
prompt:
artifact: sha256:...
tools:
- artifact: customer-lookup@sha256:...
- artifact: refund-tool@sha256:...
Dokładny format artefaktu prawie nie ma znaczenia. Ważne jest to, że wariant można odtworzyć jako konfigurację systemu, dzięki czemu miesiące później ktoś może powiedzieć, co użytkownik przypisany do B miał uruchomić. To także sprawia, że platforma do eksperymentowania pozostaje niezależna od tego, co się aktualnie zmienia. Jeden z eksperymentów może polegać na porównaniu:
model X vs model Y
Ktoś inny mógłby porównać:
memory top_k=5 vs top_k=8
A trzeci podejście mogłoby postawić naprzeciw siebie dwie zupełnie różne implementacje agentów. Z punktu widzenia eksperymentu każda z nich jest po prostu inną wersją procesu. Gdy eksperyment się rozpoczyna, te definicje stają się niezmienne. Jeśli trzeba zmienić sposób postępowania, tworzy się nowa iteracja eksperymentu, dzięki czemu historyczne znaczenie B nigdy nie jest potajemnie zmieniane.
Zadanie nie powinno stać się zależnością produkcyjną
Następnie pojawia się pytanie, kto otrzymuje A, a kto B. Kuszącym rozwiązaniem jest takie, w którym każda prośba kierowana jest do centralnej usługi eksperymentalnej w celu uzyskania odpowiedniego sposobu postępowania. To wprowadza platformę eksperymentalną do zakresu dostępności i opóźnień samego aplikacji, co jest słabym kompromisem dla systemu, którego głównym celem jest pomiar.
Pierwsza wersja wykorzystuje deterministyczne haszowanie. Warstwa sterująca wysyła swoją aktualną konfigurację do SDK znajdującego się wewnątrz aplikacji, a wybór odpowiedniej architektury następuje bezpośrednio tam, lokalnie. Dzięki temu uzyskujemy przydatną właściwość odporności na awarie: jeśli usługa eksperymentów przestanie działać, platforma tymczasowo nie może uruchamiać nowych eksperymentów ani zmieniać alokacji zasobów, jednak aplikacja pozostaje nienaruszona, a SDK nadal funkcjonuje na podstawie swojej ostatniej ważnej konfiguracji.
Dla wersji MVP wystarczy kilka jednostek randomizacji:
request
user
session
workflow_instance
Ostatni przypadek istnieje, ponieważ agenci niekoniecznie żyją tak długo jak żądanie sieciowe. Agent może rozpocząć migrację repozytorium dzisiaj, czekać na zatwierdzenie z zewnątrz i kontynuować jutro. Gdyby przydział był obliczany na podstawie konfiguracji z jutra, ten sam proces mógłby rozpocząć się w jednej implementacji i zakończyć w innej, co bez potrzeby utrudniałoby zarówno operacje, jak i analizę. Dlatego przydział pozostaje nieruchomy:
workflow_instance = migration-8291
variant = B
pipeline = agent-v18
i podróżuje wraz z przepływem pracy za każdym razem, gdy jest wznowiony.
Przypisanie różni się od ekspozycji
Kolejna różnica staje się istotna, gdy obróbka znajduje się gdzieś wewnątrz pipeline agenta. Trafienie do konkretnej grupy obróbek nie gwarantuje, że obróbka została wykonana.
Weźmy dwa wdrażenia pamięci poddawane testom. Użytkownik trafia do wersji B, nowej wersji pamięci, ale agent odpowiada bez wykonywania żadnych wyszukiwań w pamięci. Przypisanie istnieje, jednak testowany komponent nigdy nie miał szansy wpłynąć na wynik. To powinny być dwa oddzielne zdarzenia:
ASSIGNMENT
user-18271 → B
EXPOSURE
memory-v2 retrieval started
To dostarcza również informacji o stanie eksperymentu. Oprócz liczby użytkowników losowanych do poszczególnych grup można przejrzeć szczegóły dotyczące ekspozycji:
assigned 10,428
exposed 8,912
exposure rate 85.46%
Jeśli A i B wykazują nieoczekiwanie różne stawki ekspozycji, ta różnica wymaga najpierw uwagi.
Powstaje powiązany problem, gdy przydzielone leczenie nie jest tym, które ostatecznie zostanie zastosowane. Załóżmy, że B wybiera model Y, model Y zwraca błąd 503, a brama fallbackowo przechodzi na X. Zapis wygląda następująco:
assigned_variant = B
realized_model = X
fallback_reason = provider_unavailable
a realizacja pozostaje w grupie B.
To jest zgodne z zasadą intencji leczenia, ITT. Każdy uczestnik jest analizowany w grupie, do której został przypisany losowo, nawet jeśli faktycznie zastosowana terapia się różni. Takie rozwiązanie stanowi część doświadczenia z grupy B, a usunięcie takich zapytań później zmieniłoby skład populacji na podstawie zdarzenia, które miało miejsce po losowaniu. Dane dotyczące ekspozycji i faktycznie zastosowanej terapii pozostają cenne, głównie jako wskaźniki zdrowotne i diagnostyczne, chyba że analiza wskazuje inaczej.
Śledzenie wyjaśnia, co się stało
Wynik eksperymentu informuje o tym, czy wynik się zmienił, ale nic nie mówi o przyczynie. Aby dowiedzieć się dlaczego, rozsądnym wyborem jest OpenTelemetry, a nie nowy protokół śledzenia. Warstwa eksperymentalna musi jedynie dodać do każdego interwału nieco dodatkowego kontekstu:
experiment.id
experiment.version
experiment.variant
experiment.assignment_id
pipeline.id
pipeline.version
execution.purpose
Gdy to zostanie ustawione, wynik łączny będzie wyglądał tak:
B has a lower task completion rate
może przekształcić się w znacznie bardziej przydatną prośbę:
show failed B traces
A ślady pokazują, czy nowy pipeline korzysta z słabszego kontekstu, powtarza wywołania narzędzi, przechodzi na inny model, utyka w pętlach lub po prostu podąża dłuższą drogą do tego samego rezultatu.
Ślad nadal nie jest zapisem eksperymentu. Dobrze wyjaśnia, jak przebiegała jedna realizacja, ale wyniki eksperymentów mogą przetrwać go dłużej. Klient może dziś porozmawiać z agentem i odnowić subskrypcję tydzień później, długo po zakończeniu pierwotnego śladu. Dlatego obok śledzenia funkcjonuje mniejszy strumień zdarzeń eksperymentalnych:
assignment
exposure
outcome
feedback
evaluation
Ślad rejestruje przebieg wykonywania. Strumień zdarzeń zajmuje się ewidencją, która łączy proces randomizacji z wynikami pojawiającymi się później.
Ocena offline wykorzystuje ten sam proces
Zanim B trafi do użytkowników produkcyjnych, powinien obsłużyć znane scenariusze. W przypadku agentów scenariusz nie powinien sprowadzać się do:
prompt
expected answer
Ponieważ interesującym wynikiem często nie jest zwrócony tekst, lecz stan środowiska po jego przetworzeniu. Scenariusz wsparcia może być napisany w ten sposób:
scenario:
id: duplicate-charge-001
input:
message: >
I was charged twice for order 9811.
environment:
fixture: duplicate-charge-customer
expected:
refund_count: 1
ticket_status: resolved
limits:
max_turns: 15
max_tool_calls: 20
max_cost: 0.50
To samo scenariusz jest następnie wykonywany dla pipeline A i pipeline B, być może kilka razy, ponieważ wykonywania agentów są stochastyczne i dwa przebiegi tej samej konfiguracji mogą podążać różnymi ścieżkami, wychodząc od tego samego stanu początkowego. MVP wykorzystuje resetowalną środowisko testowe z usługami wspieranymi przez fixture’y lub bazami danych do testów. Budowa ogólnego mechanizmu odtwarzania wieloetapowych śledzeń lepiej pozostawić na później.
Odtwarzanie jest możliwe tylko wtedy, gdy kandydat żąda interakcji, które zostały już zapisane. Załóżmy, że B podejmie inną decyzję na trzecim etapie i wywoła narzędzie, którego oryginalna ścieżka nigdy nie dotknęła. W takim przypadku żaden zapisany stan świata nie zawiera tej gałęzi. Szczery wynik jest następujący:
REPLAY_DIVERGED
Stworzenie sfałszowanej odpowiedzi, która udaje, że proces nadal jest powtórzeniem, to błędny krok. Środowisko typu sandbox z pamięcią umożliwia obu agentom naturalne rozgałęzianie się przy jednoczesnym kontrolowaniu ich warunków startowych, co stanowi lepsze pierwsze przybliżenie rzeczywistej oceny agentów.
Egzekucja cieniowa wypełnia lukę
Pomiędzy kontrolowanym środowiskiem sandbox a prezentacją rozwiązań B istnieje przydatny krok: należy uruchomić B asynchronicznie na kopii rzeczywistych danych wejściowych z produkcji, nie udostępniając wyników. Zapisy oraz zewnętrzne efekty uboczne są wyłączane lub kierowane do izolowanego środowiska. Dzięki temu uzyskuje się dane wejściowe przypominające te z produkcji, co umożliwia zespołowi obserwację:
latency
cost
tool usage
model fallbacks
guardrail failures
judge scores
trajectory differences
Zanim jakikolwiek użytkownik zobaczy wynik obliczeń.
Simulacje cieniowe nie mogą pokazać, czy B poprawia najważniejszy wynik, ponieważ użytkownicy nigdy nie widzą B i dlatego nie mogą na nie zareagować. Służą one jako pomost: realistyczne dane wejściowe i rzeczywiste zachowanie w trakcie działania, bez żadnego ryzyka związanego z eksperymentem w rzeczywistych warunkach.
Nie każda weryfikacja wymaga sędziego w postaci LLM
Gdy agent zakończy scenariusz, wynik nadal musi zostać oceniony, dlatego pierwsza wersja zawiera dwa rodzaje oceniających.
Pierwszy rodzaj jest deterministyczny. Gdy zadanie polega na dokonaniu jednej zwrotu pieniędzy i rozwiązaniu zgłoszenia, środowisko można po prostu sprawdzić:
refund_count == 1
ticket_status == resolved
Zadawanie innemu modelowi pytania, czy zadanie zostało prawdopodobnie ukończone, nie przynosi żadnych korzyści, gdy stan wynikowy można sprawdzić.
Drugi typ opiera się na modelu i dotyczy właściwości, które sprawiają trudności przy tworzeniu stwierdzeń:
helpfulness
clarity
tone
quality of explanation
Sędzia oparty na LLM pomaga w tym, ale staje się kolejnym komponentem w wersjonowaniu i nigdy nie jest anonimowym źródłem prawdy:
evaluator
evaluator version
judge model
judge prompt
rubric
Każda wygenerowana ocena zachowuje tę informację o pochodzeniu. Utrzymywany jest również niewielki zbiór danych kalibracyjnych oznaczonych ręcznie, a sędzia jest okresowo porównywany z nim. Sędzia oparty na LLM to przyrząd pomiarowy, a gdy zmienia się ten przyrząd, ma znaczenie, czy jego odczyty również uległy zmianie. Sędziowie agenty, którzy przeglądają treści, korzystają z narzędzi lub znajdują dodatkowe dowody, są przydatni i prawdopodobnie nieuniknieni w niektórych dziedzinach, ale nie mają miejsca w pierwszej wersji.
Statystyki – utrzymywane w skali minimalnej
Zaawansowane systemy eksperymentalne oferują dużą stopień złożoności. Lepszym punktem wyjścia jest niewielka liczba poprawnie zaimplementowanych metod, zamiast imponującego menu statystycznego, którego niewielu użytkowników rozumie. Pierwsza wersja obsługuje:
A/B allocation
binary metrics
continuous metrics
95% confidence intervals
sample ratio mismatch detection
fixed-horizon analysis
To wystarczy, aby odpowiedzieć na wiele przydatnych pytań, a podsumowanie wyników mogłoby brzmieć:
Experiment
support-agent-v18
Randomization
user
Primary metric
ticket resolution
A 81.4%
B 85.1%
Difference
+3.7 percentage points
95% CI
[...]
Experiment health
SRM PASS
Niezgodność stosunku próbek wymaga szczególnej uwagi, ponieważ wyniki nie powinny wyglądać wiarygodnie, gdy sama randomizacja wydaje się błędna. Jeśli plan zakładał równy podział, a zaobserwowany podział wyraźnie się od niego różni, eksperyment jest oznaczony jako niesprawny i to koniec sprawy – nie pokazuje się żadnego zwycięzcy obok ikony ostrzeżenia.
Zasady operacyjne należą do tej samej warstwy sprawności. Wariant może zawieść pod względem kosztów, nie ponosząc przy tym błędu technicznego. Wyobraźmy sobie pętlę promptów, która kieruje wywołania z:
4 model calls
do:
47 model calls
a odpowiedź może nadal być HTTP 200, nawet przy prawidłowej odpowiedzi. Dlatego konieczne są górne granice:
max cost / execution
max tokens / execution
max agent turns
max tool calls
max variant spend / hour
Przekroczenie progu może zatrzymać tę eksploatację lub wstrzymać dalsze narażenie na tę odmianę. Raport zdrowotny może wtedy brzmieć następująco:
Variant B
SRM PASS
Error rate PASS
Cost / request +312%
Circuit breaker TRIPPED
New exposure PAUSED
Przerywacz obwodu to nie wniosek statystyczny. Jest to mechanizm bezpieczeństwa operacyjnego, który zapobiega temu, by patologiczne rozwiązania pochłaniały ruch i budżet, dopóki eksperyment nie zgromadzi wystarczającej liczby obserwacji, by móc wyciągnąć jakiekolwiek wnioski statystyczne.
Bardziej zaawansowane metody mogą poczekać: CUPED, testowanie sekwencyjne, próby kontrolne, warstwy eksperymentalne, randomizacja klastrowa, adaptacyjne przydziały i wiele innych. Model danych powinien zapewnić dla nich miejsce, ale wersja pierwsza musi jedynie prawidłowo przeprowadzać zwykłe eksperymenty A/B.
Co budować najpierw
Gdy usunięto większość elementów, które można odłożyć, platforma staje się zarządzalna. Potrzebuje usługi kontrolnej, która będzie przechowywać:
- rejestr eksperymentów
- rejestr pipeline’ów
- rejestr zbiorów danych
- rejestr oceniających
- definicje metryk
Na górze znajduje się SDK, które pobiera konfigurację eksperymentu i przypisuje ją w sposób deterministyczny po stronie klienta. Potrzebna jest ścieżka pobierania dla:
assignment
exposure
outcome
feedback
evaluation
Również ślady OpenTelemetry są przetwarzane. Rozsądnymi opcjami przechowywania są PostgreSQL dla stanu warstwy sterującej, NATS JetStream lub Kafka dla zdarzeń, ClickHouse do analiz eksperymentów i śladów, a przechowywanie obiektów kompatybilne z S3 dla dużych plików. Silnik działający offline przetwarza zestawy danych w środowiskach A i B, które można sformatować.
Bardzo wiele zostało celowo pominięte. MVP nie zajmuje się dowolną wirtualizacją środowisk z pamięcią stanu, subtelnymi interakcjami między eksperymentami, dewiacjami w ocenie wyników, automatyczną analizą przyczyn źródłowych, trwałymi efektami leczenia ani przypisywaniem przyczyn między różnymi elementami systemu, a może bez tego funkcjonować. To, co jest ważne od samego początku, to elementy, których naprawa jest trudna, gdy dane historyczne od nich zależą:
what was randomized
what treatment was assigned
what treatment actually ran
what pipeline version produced the execution
what outcome was measured
how that outcome was evaluated
O ile te identyfikatory przetrwają pierwszą wersję, platforma może stać się znacznie bogatsza bez zmiany znaczenia jej dawnych eksperymentów.
Cenną informacją jest to, że ocena offline, wykonywanie w tle oraz eksperymenty na żywo mogą dzielić się definicjami procesów, śladami działania oraz narzędziami do oceny, chociaż każdy z nich odpowiada na własne pytanie. Wykonywania offline pokazują, jak zachowują się A i B w kontrolowanych scenariuszach. Wykonywania w tle pokazują, jak B radzi sobie z danymi z produkcji, przy czym użytkownicy pozostają nienaruszeni. Eksperymenty na żywo pokazują, jakie zmiany zachodzą, gdy rzeczywiści użytkownicy są losowo podzieleni. Śledzenie rejestruje to, co działo się w trakcie każdego wykonywania, i pomaga interpretować wyniki, ale diagnoza na poziomie śladów nigdy nie powinna zastępować oceny przyczynowej. Razem umożliwiają one stworzenie MVP, które może niezawodnie odpowiedzieć na trzy pytania:
What exactly did we run?
What happened when we ran it?
Did assigning users to B improve the outcome we care about?
Wszystko inne może rozwijać się wokół tego.