Оцінювання голосових агентів за результатом: тришаровий тестовий стенд
Метрики за транскриптом не помічають, коли голосовий агент оновив не те замовлення або залишив застарілий запис. Будуємо стенд із трьох шарів, шести осей і релізного шлюзу.
Уявіть три телефонні дзвінки, які на рев'ю виглядають бездоганно. У першому клієнт посеред речення виправляє номер замовлення, і агент зачитує йому вже виправлений номер. У другому клієнт перебиває агента, і той чисто замовкає, ніхто ні на кого не наговорює. У третьому агент повідомляє, що замовлення оновлено.
А тепер погляньте на бекенд. Перший дзвінок оновив не те замовлення, а саме те, від якого клієнт щойно відмовився. У другому виклик інструмента, що виконувався в момент перебивання, так і працював далі. У третьому оновлення взагалі не дійшло, і в системі досі лежить старе значення.
Рев'ю транскриптів жодного з цих випадків не впіймає. Перевірка WER чи рубрика якості розмови теж. Проте всі три дзвінки провалили те, що справді важило. Далі розберемо, як побудувати тестовий стенд, який ловить увесь цей клас помилок раніше, ніж на нього натрапить клієнт.
Чому метрик за транскриптом замало
Голосових агентів зазвичай міряють за word error rate, затримкою, швидкістю реакції на перебивання, вимовою та природністю мовлення. Усе це варто відстежувати, і жодна з метрик не хибна. Але вони відповідають на вужче питання, ніж те, яке ставить прод. Вони кажуть, чи правильно агент звучав. І нічого не кажуть про те, чи щось насправді відбулося.
Стенд, який має сенс запускати, складається з трьох шарів:
- Розмова: агент правильно почув і правильно відповів?
- Виконання: викликано потрібні інструменти з потрібними аргументами?
- Стан: задумана зміна справді застосувалася в зовнішній системі?
Застосуймо це до трьох дзвінків. Перший пройшов шар розмови й провалив виконання. Другий теж провалив виконання, але з іншої причини: застарілий виклик так і не скасували. Третій пройшов і розмову, і виконання, але провалився на стані, бо запис у бекенд не зберігся. Три провали на різних шарах, і в усіх один симптом: транскрипт, що виглядає нормально.
Спершу опишіть, що може піти не так
Нічого не варто оцінювати, доки немає матриці режимів відмови. Чотири категорії покривають більшість випадків.
Перебивання: клієнт втручається до того, як агент договорив; поки збирається виклик інструмента; одразу після того, як виклик почав виконуватися; а також випадкові перебивання через фоновий шум, які взагалі не мали зупиняти агента.
Виправлення: змінений номер замовлення, нова дата доставки, інша кількість і заперечення, як-от «відправляйте», а одразу потому «ні, не треба».
Збої інструментів: від таймаутів і порожніх відповідей до інструмента, що дістав не той запис, 500-х від бекенда, запізнілої відповіді про успіх і дубльованих запитів через неідемпотентний ретрай.
Конфлікти стану виникають, коли одночасно існують три версії правди: у транскрипті одне, у живому стані інше, а в payload, який реально пішов в інструмент, третє.
На практиці категорії перетинаються. Виправлення, що прийшло під час ретраю після збою інструмента, це окремий сценарій. І все ж таке групування дає змогу спершу перевірити кожну категорію окремо.
Який вигляд має тест-кейс
Кожному кейсу потрібні вхідний сценарій, опис того, що правильна система має зробити на всіх чотирьох рівнях, і структура, достатньо жорстка для автоматичного оцінювання, щоб нікому не доводилося читати й судити вручну. Мінімальна схема виглядає так:
{
"id": "corr-02",
"category": "correction",
"setup": {
"field": "order_number",
"initial_value": "5590",
"corrected_value": "5599",
"interrupt_stage": "during_tool_prep"
},
"expected": {
"captured": true,
"active_state_correct": true,
"tool_args_correct": true,
"stale_write_prevented": true,
"backend_correct": true,
"confirmation_matches_backend": true,
"should_execute": true
}
}
Блок setup навмисно вільний: перебиванням і збоям інструментів потрібні інші поля, ніж виправленням. Блок expected суворий. Кожен кейс оцінюється за тими самими шістьма осями, і саме тому 30 зовсім різних сценаріїв зводяться в одну таблицю результатів.
Шість осей
- Подію зафіксовано. Система взагалі помітила виправлення чи перебивання?
- Коректний активний стан. Після події живий робочий стан оновився відповідно?
- Коректні аргументи інструмента. Значення у виклику інструмента актуальні й узгоджені?
- Застарілий запис відвернуто. Чи заблоковано кожну дію, що спиралася на застарілий стан?
- Бекенд коректний. Збережений результат відповідає тому, чого клієнт насправді хотів?
- Підтвердження збігається з бекендом. Те, що агент сказав клієнтові, збігається з тим, що реально збережено?
Одне правило важливіше за решту: ці осі ніколи не усереднюються в одне число. Система, що має рацію за п'ятьма осями й помиляється за backend_correct, видала хибний результат, і п'ять зелених галочок поруч цього не пом'якшують. Кейс зараховується як справжній прохід, End-to-End Task Integrity, лише коли всі шість осей істинні водночас.
Який вигляд має успішний трейс
Візьмімо сценарій, де виправлення приходить рано: воно з'являється раніше, ніж виник будь-який виклик інструмента, тож скасовувати нічого.
Customer says 4821 → state_version = 4
Correction: 4281 → state_version = 5
Tool call built using version 5
Backend verified = 4281
Agent confirms 4281
Кожен перехід стану має мітку версії, а виклик інструмента збирається лише тоді, коли версія стабільна. Ніщо ніколи не будується на версії 4, тому потім нічого викидати. Ось так виглядає успішний кейс. Це не «виправлення не було». Це «виправлення повністю поширилося до того, як щось далі ланцюжком зафіксувалося».
Прогін на свідомо кривому моку
Щоб було предметно, візьмімо робочий стенд (фікстури, схема та скорер) і спрямуймо його на мок-пайплайн на правилах. Мок точно розпізнає мовлення й добре тримає черговість реплік, але ніколи не інвалідує виклик інструмента, якщо той уже зібраний чи надісланий, і ніколи не перепровіряє результат інструмента, якщо не розпізнав його як пошкоджений. Це рівно та система, що стоїть за провалами вище, лише описана явно, щоб стенд мав ціль.
У прогоні було 30 синтетичних дзвінків, порівну розділених на сценарії виправлень, перебивань і збоїв інструментів.
- Подію зафіксовано: 24 з 30
- Коректний активний стан: 24 з 30
- Коректні аргументи інструмента: 11 з 30
- Застарілий запис відвернуто: 8 з 30
- Бекенд коректний: 8 з 30
- Підтвердження збіглося з бекендом: 7 з 30
- Наскрізна цілісність задачі: 7 з 30
Шар розмови виглядає міцним: у 24 дзвінках із 30 подію впіймано, а стан оновлено. Далі виконання й стан просідають. Лише у 8 дзвінках вдалося не допустити застарілого запису, і тільки в 7 озвучене підтвердження збіглося з тим, що зрештою опинилося в бекенді. Якщо судити лише за якістю транскрипту, система здавалася б готовою до проду. Але це не так.
Як перетворити провали на список виправлень
Червоний результат корисний, лише коли вказує, який шар лагодити. Якщо розкласти 23 непройдені кейси за причинами, вийде так:
- 10 кейсів: аргументи інструмента зібрано із застарілих значень
- 4 кейси: пошкоджений результат інструмента прийнято без жодної перевірки
- 3 кейси: уже запущені виклики так і не скасували
- 2 кейси: фоновий шум помилково сприйнято як перебивання клієнта
- 2 кейси: помилка бекенда плюс ретрай, який не можна було безпечно повторювати
- 1 кейс: підтвердження озвучено до того, як повернувся асинхронний результат
- 1 кейс: у дії немає ключа ідемпотентності
Десять із 23 промахів мають одну причину. Виклик інструмента вже був зібраний або надісланий, коли надійшло виправлення чи перебивання, і жодного механізму далі ланцюжком, щоб його скасувати, не було. Це одна архітектурна діра, що проявилася в десяти сценаріях, а не десять багів, і побачити її якраз і є сенсом прогону стенда. Голе «дзвінок провалився» команді нічого не дає, а от «збирання аргументів зламалося в 10 з 23 кейсів» задає пріоритети на наступний спринт.
Релізний шлюз для вагомих дій
Перш ніж викочувати зміну голосового агента, який виконує вагомі дії, планки «якість розмови зросла» недостатньо. Шлюз має звучати так:
- Жодного провалу із застарілим записом у наборах виправлень і перебивань
- Жодної розбіжності між підтвердженням і бекендом
- Ідемпотентні ретраї інструментів
- Критичні дії перевіряються за бекендом після виконання, а не приймаються на віру з відповіді інструмента
Голосовий агент готовий до проду лише тоді, коли розмова, трейс інструментів і стан бекенда сходяться в тому, що сталося. Більшість систем сьогодні цим похвалитися не можуть, і щоб це з'ясувати, треба перестати оцінювати транскрипт і почати оцінювати результат.