Мінімальна функціональна платформа для експериментування з AI-агентами: тести типу A/B та тіньові запуски
Як вирішити, чи слід версія агента B замінити A, використовуючи визначення конвеєрів, локальне призначення, події експозиції, сліди, оцінку в пісочниці, тестування в тіні та захисні механізми.
Джерела: A minimum viable experimentation platform for AI agents
Визначити, чи нова версія агента краща за ту, що використовується у продакшені, складніше, ніж здається. Під час розробки цей процес під контролем: достатньо мати набір тестових випадків, запустити обидві версії, переглянути кілька результатів, доручити іншій моделі оцінити вихідні дані та вирішити, чи варто приділити більше зусиль цим змінам. Справи ускладнюються, коли метою є не просто запит чи один виклик моделі, а система, компоненти якої змінюються за власними графіками та взаємодіють способами, які не завжди є очевидними.
Уявіть собі агента підтримки клієнтів, складеного з цих компонентів:
agent runtime
memory and retrieval
system prompt
model
tools
guardrails
application logic
Припустимо, що стратегія отримання даних змінюється, і рівень виконання завдань на тестовому наборі підвищується. Це може означати, що агент справді став кращим. Але це також може бути наслідком більшої кількості викликів моделі, що збільшує витрати на 20 відсотків, іншої поведінки щодо реальних користувачів чи переваг від іншого компонента, який випадково змінився протягом того ж періоду. У такому випадку це більше схоже на експериментування, ніж на оцінку моделі.
Нижче наведений опис найменшої платформи, на яку можна буде зручно покластися для відповіді на досить просте запитання:
should version B replace version A?
Це має сенс лише при певному розмірі. Один агент, двадцять тест-кейсів, перевірених вручну, та кілька внутрішніх користувачів не потребують більшої частини цього. Платформа стає корисною тоді, коли кілька команд незалежно змінюють моделі, запити, пам’ять, інструменти та реалізації агентів, експерименти тривають безперервно, і хтось у кінцевому підсумку має схвалити зміни в продакшені, надавши докази, які залишатимуться зрозумілими навіть через кілька місяців. Платформа агентів для всієї компанії відповідає цьому опису.
Що насправді є B
Перша проблема є базовою. Припустимо, експеримент стверджує:
A = agent-v17
B = agent-v18
Що саме таке agent-v18? Якщо під час експерименту змінюється запит, індекс пошуку перебудовується, а шлюз моделі замінює свою стандартну модель, тоді позначка майже нічого не говорить про те, що саме оцінювалося. Це особливо сильно впливає на агентів, оскільки поведінка, яку спостерігають у кінцевому підсумку, є результатом всього процесу обробки, а не лише одного окремого виклику моделі.
Отже, об’єктом оцінки є незмінна визначення процесу обробки. Воно може виглядати так:
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:...
Точний формат артефакта майже не має значення. Важливо те, що варіант можна відновити у вигляді конфігурації системи, щоб через кілька місяців хтось міг сказати, що саме мав запустити користувач, призначений до B. Це також забезпечує незалежність платформи для експериментів від усього, що може змінюватися. Один експеримент може порівнювати:
model X vs model Y
Інший може порівняти:
memory top_k=5 vs top_k=8
А ще один підхід може протиставити одна одній два абсолютно різних варіанти реалізації агентів. З точки зору експерименту кожен із цих варіантів є просто різною версією конвеєру обробки даних. Коли починається експеримент, ці визначення стають незмінними. Якщо необхідно змінити схему обробки, створюється нова ітерація експерименту, тож історичне значення B ніколи не переписується таємно.
Завдання не повинні стати залежністю від продакшну
Далі постає питання про те, хто отримуватиме A, а хто — B. Привабливим рішенням є таке, коли кожен запит звертається до центральної служби експериментування за відповідною схемою обробки. Це втягує платформу експериментування у параметри доступності та затримки самого додатку, що є поганою заміною для системи, основною метою якої є вимірювання.
Перша версія використовує детерміністичне хешування. Контрольний рівень надсилає свою поточну конфігурацію до SDK, який знаходиться всередині додатку, і вибір архітектури здійснюється безпосередньо там, локально. Це забезпечує корисну властивість при збоях: якщо сервіс експериментів виходить з ладу, платформа тимчасово не може запускати нові експерименти чи змінювати розподіл ресурсів, проте додаток залишається недоторканим, а SDK продовжує працювати зі своєю останньою дійсною конфігурацією.
Для MVP достатньо кількох пристроїв випадковостей:
request
user
session
workflow_instance
Останній варіант існує тому, що агенти не обов’язково працюють стільки ж часу, скільки триває веб-запит. Агент може розпочати міграцію репозиторію сьогодні, почекати на зовнішнє схвалення та продовжити завтра. Якби призначення обчислювалося на основі конфігурації завтрашнього дня, той самий процес міг би розпочатися в одній реалізації та завершитися в іншій, що зробило б як операції, так і аналіз зайво складними. Тому призначення залишається незмінним:
workflow_instance = migration-8291
variant = B
pipeline = agent-v18
і воно переміщується разом із потоком роботи щоразу, коли він відновлюється.
Призначення відрізняється від експозиції
Ще одна відмінність стає важливою, коли обробка знаходиться десь усередині конвеєра агента. Те, що обробка потрапила до певної групи, не гарантує, що вона справді була виконана.
Візьмемо дві реалізації пам’яті, які перебувають на тестуванні. Користувач потрапляє до версії B — нової версії пам’яті, але агент відповідає, не здійснюючи жодного пошуку в пам’яті. Призначення існує, проте тестовий компонент так і не мав можливості вплинути на результат. Це мають бути окремі події:
ASSIGNMENT
user-18271 → B
EXPOSURE
memory-v2 retrieval started
Це також надає інформацію про стан експерименту. Окрім кількості користувачів, рандомізованих до кожної групи, можна переглянути дані про вплив:
assigned 10,428
exposed 8,912
exposure rate 85.46%
Якщо A та B демонструють несподівано різні показники експозиції, саме ця різниця потребує першочергової уваги.
Подібна проблема виникає, коли призначений спосіб обробки не є тим, який насправді використовується. Наприклад, B обирає модель Y, але вона повертає помилку 503, тож шлюз переходить на модель X. Запис виглядає так:
assigned_variant = B
realized_model = X
fallback_reason = provider_unavailable
і виконання залишається у B.
Це відповідає правилу наміру лікування, ITT. Кожен учасник аналізується у групі, до якої він був рандомізований, навіть якщо фактичне виконання відрізняється. Такий підхід є частиною досвіду групи B, і видалення подібних запитів пізніше призведе до зміни складу групи на основі події, яка сталася після рандомізації. Дані про вплив та фактичне лікування залишаються цінними, переважно як сигнали щодо стану здоров’я та діагностики, якщо тільки аналіз не вказує на щось інше.
Трекінг пояснює, що сталося
Результат експерименту вказує, чи змінився результат, але нічого не говорить про причину. Для з’ясування причини розумним вибором є OpenTelemetry, а не новий протокол трекінгу. Шар експериментування має лише додати трохи додаткового контексту до кожного інтервалу:
experiment.id
experiment.version
experiment.variant
experiment.assignment_id
pipeline.id
pipeline.version
execution.purpose
З урахуванням цього загальний результат буде таким:
B has a lower task completion rate
може перетворитися на набагато корисніше запитання:
show failed B traces
А сліди показують, чи новий конвеєр використовує слабший контекст, чи повторює виклики інструментів, чи переходить на інший модель, чи застрягає у циклах, або просто йде довшим шляхом до того самого результату.
Слід все одно не є записом експерименту. Він добре пояснює, як відбувалась одна з експекуацій, але результати експерименту можуть існувати довше. Клієнт може поговорити з агентом сьогодні та оновити підписку через тиждень, дуже довго після завершення початкового сліду. Тож поруч із відстеженням працює менша потік подій експерименту:
assignment
exposure
outcome
feedback
evaluation
Журнал слідування фіксує процес виконання. Потік подій здійснює облік, який пов’язує процес випадкового формування з наслідками, що надходять пізніше.
Офлайн-оцінка використовує той самий процес
Перш ніж B потрапить до користувачів у продакшені, він має пройти перевірку на відповідність відомим сценаріям. Для агентів сценарій не повинен зводитися до:
prompt
expected answer
адже цікавим результатом часто є не повернутий текст, а стан середовища після цього. Сценарій підтримки може бути написаний ось так:
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
Та сама ситуація потім применюється до пайплайну A та пайплайну B, можливо, кілька разів, оскільки виконання агентів є стохастичними, і два запуски з однією конфігурацією можуть слідувати різними траєкторіями від одного й того ж початкового стану. MVP використовує сандбокс, який можна скинути, з послугами, підтримуваними фікстурами, або тестовими базами даних. Створення здатності до повторного відтворення багатьох етапів краще залишити на пізніший час.
Повторне відтворення можливе лише тоді, коли кандидат запитує взаємодії, які вже були записані. Припустимо, B приймає інше рішення на третьому етапі та використовує інструмент, якого не торкалася початкова траєкторія. Тоді жоден з записаних станів не містить цього розгалуження. Чесний результат буде таким:
REPLAY_DIVERGED
А вигадана відповідь, яка удає, ніби виконання все ще є повтором, — це неправильний крок. Середовище зі станом дозволяє обом агентам природним чином розгалужуватися, контролюючи при цьому їхні початкові умови, що є кращою первинною оцінкою реальних агентів.
Виконання в тіні заповнює прогалину
Є корисний крок між контрольованим середовищем та впровадженням B для користувачів: виконати B асинхронно на копії справжніх вхідних даних у продакшені, не надаючи його результату. Записи та зовнішні наслідки дій блокуються або перенаправляються в ізольоване середовище. Це забезпечує вхідні дані, схожі на ті у продакшені, та дозволяє команді спостерігати:
latency
cost
tool usage
model fallbacks
guardrail failures
judge scores
trajectory differences
Перш ніж будь-який користувач побачить результат обробки.
Шадоу-тестування не можуть показати, чи покращує B найважливіший результат, оскільки користувачі ніколи не бачать B і тому не можуть на це відреагувати. Вони є проміжним етапом: реалістичні вхідні дані та справжня поведінка під час виконання, без ризиків, пов’язаних із живим експериментом.
Не кожна перевірка потребує оцінки від ШІ
Коли агент завершує сценарій, результат все одно потребує оцінки, тому перша версія містить два типи оцінювачів.
Перший тип є детерміністичним. Коли завдання полягає у виплаті єдиного повернення коштів та вирішенні проблеми, середовище можна просто перевірити:
refund_count == 1
ticket_status == resolved
Запитувати інший модель, чи, ймовірно, завдання виконано, не має сенсу, якщо можна перевірити отриманий стан.
Другий тип ґрунтується на моделях та використовується для властивостей, які не піддаються підтвердженню:
helpfulness
clarity
tone
quality of explanation
Суддя у вигляді ШІ допомагає в цьому, але стає ще одним компонентом з версіями та ніколи не є анонімним джерелом істини:
evaluator
evaluator version
judge model
judge prompt
rubric
Кожен створений рейтинг зберігає своє походження. Також підтримується невеликий набір даних для калібрування, позначених людьми, і суддя періодично порівнюється з ними. Суддя на основі ШІ є вимірювальним інструментом, і коли інструмент змінюється, важливо, чи змінилися також його показники. Агентні судді, які переглядають інформацію, використовують інструменти чи знаходять додаткові докази, є корисними та, ймовірно, неминучими в деяких галузях, але вони не мають місця у першій версії.
Статистика, у обмеженій кількості
Розвинені системи експериментування пропонують велику кількість складних функцій. Кращою відправною точкою є невеликий набір правильно реалізованих методів замість вражаючого меню статистики, яке розуміють лише деякі користувачі. Перша версія підтримує:
A/B allocation
binary metrics
continuous metrics
95% confidence intervals
sample ratio mismatch detection
fixed-horizon analysis
Цього достатньо, щоб відповісти на багато корисних запитань, а узагальнення результатів може виглядати так:
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
Неспівпадання співвідношення зразків вимагає особливої уваги, адже результати ніколи не повинні виглядати авторитетними, якщо сам процес випадкового вибору видається пошкодженим. Якщо план передбачав рівний розподіл, а фактичний розподіл суттєво відрізняється, експеримент вважається проблемним, і на цьому все закінчується — без показу переможця поруч із попереджувальним іконкою.
Оперативні механізми захисту також належать до цього ж рівня стану. Варіант може зазнати невдачі з точки зору витрат, не маючи технічних проблем. Уявіть собі цикл запитів, який постійно надсилає виклики з:
4 model calls
У:
47 model calls
І відповідь все одно може бути HTTP 200, навіть якщо це правильна відповідь. Тому потрібні верхні межі:
max cost / execution
max tokens / execution
max agent turns
max tool calls
max variant spend / hour
Перетинання «капи» може зупинити цю обробку або призупинити подальший вплив варіанту. Тоді звіт про стан здоров’я може виглядати так:
Variant B
SRM PASS
Error rate PASS
Cost / request +312%
Circuit breaker TRIPPED
New exposure PAUSED
Механізм аварійного вимкнення — це не статистичний висновок. Це оперативний механізм безпеки, який запобігає тому, щоб неправильне рішення споживало ресурси та бюджет, поки експеримент все ще збирає достатньо даних для статистичних висновків.
Більш складні методи можуть почекати: CUPED, послідовне тестування, контрольні групи, різні рівні експериментів, кластерна випадковізація, адаптивне розподілення та інші. Модель даних має залишати простір для них, але перша версія має лише правильно виконувати звичайні експерименти типу A/B.
Що будувати спочатку
Після видалення більшості елементів, які можна відкласти, платформа стає керованою. Їй потрібен сервіс керування, який буде містити:
- реєстр експериментів
- реєстр потоків обробки даних
- реєстр наборів даних
- реєстр оцінювачів
- визначення метрик
Нагорі розташований SDK, який завантажує конфігурацію експерименту та детерміновано її присвоює на стороні клієнта. Для цього потрібен шлях імпорту:
assignment
exposure
outcome
feedback
evaluation
Також обробляються треки OpenTelemetry. Розумним вибором для зберігання є PostgreSQL для стану керуючої платформи, NATS JetStream або Kafka для подій, ClickHouse для аналізу експериментів та треків, а також об’єктне зберігання, сумісне з S3, для великих об’ємів даних. Офлайн-запускник використовує набір даних для тестування в середовищах типу A та B, які можна скинути.
Багато чого навмисно відсутнє. MVP не передбачає довільної віртуалізації середовищ із станом, тонких взаємодій між експериментами, аналізу причин проблем автоматично, тривалих ефектів лікування чи причинно-наслідкових зв’язків між кількома агентами, і може обійтися без цього. Те, що має значення з самого початку, — це те, виправлення чого є складним, коли від нього залежать історичні дані:
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
Якщо ці ідентичності залишаться у версії один, платформа може стати набагато багатшою, не змінюючи значення її попередніх експериментів.
Корисний підхід полягає у тому, що офлайн-оцінка, тіньове виконання та прямі експерименти можуть користуватися спільними визначеннями потоків обробки, записами та інструментами оцінювання, хоча кожен з них відповідає на своє запитання. Офлайн-виконання показують, як поводяться A та B у контрольованих сценаріях. Тіньові виконання демонструють, як B справляється з вхідними даними у реальних умовах, не впливаючи на користувачів. Прямі експерименти виявляють зміни, які виникають при випадковому розподілі справжніх користувачів. Записи виконань фіксують усе, що відбувається під час кожного виконання, та допомагають інтерпретувати результати, проте діагностика на рівні записів ніколи не повинна замінювати причинно-наслідковий аналіз. Разом вони дозволяють створити MVP, який може надійно відповісти на три запитання:
What exactly did we run?
What happened when we ran it?
Did assigning users to B improve the outcome we care about?
Усе інше може рости навколо цього.