Как тестировать multi-turn диалоги агента?
Краткий тезис
Тестирование multi-turn диалогов агента — это проверка способности системы поддерживать последовательный, контекстно-зависимый разговор из нескольких обменов (turns). Основные подходы включают запись реальных диалогов (recording), их воспроизведение в тестовой среде (replay), генерацию синтетических сценариев (synthetic generation) и state verification — проверку состояния агента (память, сессия) после каждого шага. Ключевые метрики: consistency (непротиворечивость), completion rate (доля успешно завершённых диалогов) и faithfulness (верность извлечённым фактам).
1. Термин: multi-turn диалог и его особенности
Multi-turn диалог — это последовательность сообщений между пользователем и агентом, где каждый следующий запрос зависит от предыдущих. В отличие от одношагового Q&A, агент должен помнить контекст, отслеживать намерения и не противоречить сам себе.
Основные вызовы при тестировании:
- Зависимость от контекста: ответ на turn #2 зависит от turn #1.
- Долгая память: агент должен корректно обрабатывать историю, не «забывая» ключевые детали.
- Неоднозначность: пользователь может исправляться, уточнять или менять тему.
- Вариативность: разные пользователи — разные формулировки, порядок вопросов.
Традиционные unit-тесты (один запрос → один ответ) не покрывают эти сценарии.
2. Recording: запись реальных диалогов
Recording — сбор реальных диалогов из production-логов или с помощью ручного тестирования (Wizard-of-Oz).
Зачем нужно: реальные диалоги отражают истинное поведение пользователей, включая неожиданные повороты, опечатки, незавершённые фразы.
Инструменты:
- Логирование (например, через LangSmith, MLflow, собственные логи) — сохранять все turns, timestamp, state агента.
- Анонимизация — удаление PII перед использованием.
- Формат датасета: JSON с полями session_id,
turns[]— каждый turn содержитuser_message,agent_response, action (вызов инструмента),state_after.
Пример структуры:
{
"session_id": "abc123",
"turns": [
{"user": "Какой курс валюты?", "agent": "EUR 1.12 USD", "state": {"currency": "EUR"}},
{"user": "А к доллару?", "agent": "1.0 USD", "state": {"currency": "USD"}}
]
}
Проблемы: конфиденциальность, «шум» (некорректные действия пользователя), трудоёмкость разметки.
3. Replay: воспроизведение диалогов
Replay — прогон записанных диалогов через тестируемого агента и сравнение его ответов с эталонными (записанными ранее).
Процесс:
- Загрузить датасет записанных диалогов.
- Для каждого диалога последовательно подавать
user_messageагенту в той же сессии (с тем же session_id). - Собрать ответы агента
agent_response_actual. - Сравнить с
agent_response_expected(эталон).
Метрики сравнения:
- Exact Match (EM) — строгое совпадение текста (редко применимо из-за вариативности).
- Semantic Similarity — косинусное расстояние между эмбеддингами (BERT, Sentence-BERT).
- LLM-as-judge — промпт LLM (например, GPT-4) оценивает ответ по шкале 1–5 с учётом контекста.
Код на Python (схематично):
import json
from sentence_transformers import SentenceTransformer, util
model = SentenceTransformer('all-MiniLM-L6-v2')
def replay_dialog(dialog, agent):
actual_responses = []
for turn in dialog['turns']:
user_msg = turn['user']
agent_response = agent.process(user_msg, session_id=dialog['session_id'])
actual_responses.append(agent_response)
return actual_responses
def compute_semantic_similarity(expected, actual):
emb1 = model.encode(expected)
emb2 = model.encode(actual)
return util.cos_sim(emb1, emb2).item()
4. Synthetic generation: создание синтетических сценариев
Synthetic generation — генерация диалогов с помощью LLM (self-chat, ролевые игры) или правил (template-based).
Self-chat: два LLM — один играет пользователя, другой — агента. Задаётся промпт с описанием цели диалога (например, «пользователь хочет забронировать билет и потом уточнить время»).
Промпт для генерации:
Сгенерируй диалог длиной 3-5 turns между пользователем (User) и агентом (Agent).
Тема: бронирование авиабилета.
User должен сначала спросить про рейсы, потом уточнить цену, затем изменить дату.
Формат: JSON с полями "turns".
Преимущества:
- Масштабируемость: легко создать тысячи диалогов.
- Контроль сценариев: можно задать сложные кейсы (исправление, отмена брони).
- Бесплатно (если использовать локальную LLM).
Недостатки:
- Не отражает реальные паттерны речи.
- Риск «галлюцинаций» (некорректные факты).
Рекомендация: комбинировать с real recording для валидации.
5. State verification: проверка состояния агента
State verification — после каждого turn проверяется внутреннее состояние агента: память (что он запомнил о пользователе), сессионные переменные, вызванные инструменты, планы.
Что проверяем:
- Memory: все ли ключевые факты из предыдущих turns сохранились (например, имя пользователя, выбранная дата).
- Session context: не смешались ли разные сессии.
- Tool calls: были ли вызваны правильные инструменты с правильными параметрами.
- Coherence: ответ агента логически связан с текущим состоянием.
Пример проверки (pytest):
def test_state_after_turn():
agent = TestAgent()
agent.handle_turn("Какой курс евро?", session='s1')
state = agent.get_state('s1')
assert state['currency'] == 'EUR' # ключевая переменная должна быть установлена
agent.handle_turn("А к доллару?", session='s1')
state = agent.get_state('s1')
assert state['currency'] == 'USD' # контекст обновлён
Инструменты: unit-тесты на каждый модуль (retriever, memory, tool executor), integration-тесты на весь пайплайн.
6. Метрики multi-turn диалогов
Для полноценной оценки используют комбинацию метрик:
| Метрика | Суть | Как считается |
|---|---|---|
| Consistency (непротиворечивость) | Агент не противоречит своим предыдущим ответам | LLM-оценка: предъявить историю и два ответа — противоречат ли? |
| Completion rate | Доля диалогов, где агент успешно выполнил целевую задачу (например, бронирование завершено) | Ручная или LLM-разметка (done/not done) |
| Faithfulness | Верность фактам из документов (для RAG) | RAGAS faithfulness, но с учётом всего контекста turns |
| Context adherence | Агент использует релевантный контекст, не игнорирует и не выдумывает | Сравнение использованных документов с эталоном |
| User satisfaction proxy | Средняя оценка пользователя (1-5) от LLM-симуляции или реальных отзывов | Prompt-based оценка |
Пример вычисления completion rate:
completion_count = sum(1 for dialog in test_set if dialog['completed'])
completion_rate = completion_count / len(test_set)
7. Инструменты и платформы
| Инструмент | Назначение | Особенности |
|---|---|---|
| LangSmith | Запись, реплей, метрики | Встроенная поддержка multi-turn, дашборды |
| RAGAS | Оценка faithfulness, answer relevance | Можно адаптировать под диалоги (использовать sum turns) |
| MLflow | Логирование экспериментов | Версионирование диалогов, метрик |
| DeepEval | Unit-тесты для LLM | Позволяет писать кастомные метрики |
| Pytest + LLM-fixtures | Написание тестов на диалоги | Простота, CI/CD интеграция |
8. Continuous evaluation: тестирование в CI/CD
CI/CD пайплайн должен включать:
- Regression тесты — прогон набора ранее записанных диалогов при каждом изменении кода.
- A/B тестирование — сравнение новой версии агента с предыдущей на синтетическом датасете.
- Пороговые значения — если completion rate упал ниже 80% или consistency ниже 0.9 — блокировка деплоя.
Пример скрипта для GitHub Actions:
- name: Run dialog tests
run: |
pytest tests/test_multi_turn.py --junitxml=report.xml
- name: Check metrics
run: |
python scripts/check_thresholds.py --completion 0.85 --consistency 0.9
9. Типичные ошибки и best practices
- Тестировать только одношаговые сценарии (не учитывать историю).
- Использовать только synthetic generation — теряется реализм.
- Не проверять state (память, сессию).
- Сравнивать ответы по exact match — слишком строго.
Best practices:
- Комбинируйте recording и synthetic generation (real + synthetic).
- Размечайте «золотые» диалоги для эталонных ответов.
- Автоматизируйте state verification (не только ответ, но и внутренние переменные).
- Используйте LLM-as-judge осторожно: калибруйте на небольшой выборке.
- Тестируйте краевые случаи: пустые запросы, противоречия, смена темы.
Пет-проект для закрепления
Задача: Создать тест-сьют для агента на датасете MultiWOZ (бронирование отелей, ресторанов). Реализовать recording (использовать часть датасета как «реальные»), synth generation для новых сценариев, replay и state verification.
Инструменты:
- Python, LangChain (агент с памятью и инструментами).
- pytest для тестов.
- RAGAS для faithfulness.
- Sentence-Transformers для semantic similarity.
Шаги:
- Загрузить MultiWOZ и выбрать 100 диалогов (ground truth).
- Реализовать агента с памятью (ConversationBufferMemory).
- Написать функцию replay для прогона диалогов через агента.
- Вычислить completion rate (использовать ground truth — завершён ли диалог).
- Добавить state verification (проверять, что агент запомнил тип бронирования после первого turn).
- Сгенерировать 50 синтетических диалогов (self-chat) и повторить тесты.
Ожидаемый результат: Набор pytest тестов с отчётом в формате Allure, где указан completion rate, средняя semantic similarity, faithfulness для каждого диалога. CI/CD pipeline (GitHub Actions) прогоняет тесты при каждом push.
Связь с другими вопросами
| Вопрос | Тема |
|---|---|
| 786 | Архитектура агента (как спроектировать multi-turn) |
| 787 | Память агента (как хранить историю) |
| 788 | Проектирование инструментов агента |
| 790 | Безопасность агента (защита от инъекций в диалоге) |
| 792 | Мониторинг агента в production |
| 793 | Оценка качества agentic RAG |
Навигация
- Предыдущий: 788
- Следующий: 790
- Индекс: 00. Индекс разборов