English translation is not available yet. Showing Russian content.

Как тестировать 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 — прогон записанных диалогов через тестируемого агента и сравнение его ответов с эталонными (записанными ранее).

Процесс:

  1. Загрузить датасет записанных диалогов.
  2. Для каждого диалога последовательно подавать user_message агенту в той же сессии (с тем же session_id).
  3. Собрать ответы агента agent_response_actual.
  4. Сравнить с 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Логирование экспериментовВерсионирование диалогов, метрик
DeepEvalUnit-тесты для 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.

Инструменты:

Шаги:

  1. Загрузить MultiWOZ и выбрать 100 диалогов (ground truth).
  2. Реализовать агента с памятью (ConversationBufferMemory).
  3. Написать функцию replay для прогона диалогов через агента.
  4. Вычислить completion rate (использовать ground truth — завершён ли диалог).
  5. Добавить state verification (проверять, что агент запомнил тип бронирования после первого turn).
  6. Сгенерировать 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

Навигация