中文翻译暂不可用,显示俄语原文。

Что такое «test coverage» для агента (покрытие траекторий, а не кода)?

Краткий тезис

coverage|Test coverage для агента — это метрика, оценивающая, насколько тесты охватывают возможные траектории (последовательности шагов) и состояния агента, в отличие от традиционного code coverage, который измеряет покрытие строк кода. Поскольку агент управляется LLM, его поведение недетерминировано, и основное внимание уделяется разнообразию протестированных путей — от начального запроса до конечного действия, включая промежуточные состояния памяти и сессии. Цель — покрыть все часто встречающиеся траектории (принцип Парето 80/20) и минимизировать риск пропуска редких, но критичных маршрутов.


1. Термин: Test coverage для агента и его отличие от code coverage

Test coverage в классической разработке — это процент кода (строк, ветвей, условий), который выполняется во время тестов. Для LLM-агента этот подход не работает.

АспектCode coverageTrajectory / State coverage
Объект измеренияКод (функции, классы)Поведение агента (шаги, решения)
ДетерминизмВысокий (один и тот же вход → один выход)Низкий (LLM может выбирать разные действия)
Что тестируетсяПути исполнения кодаПоследовательности действий и переходы состояний
Инструментыpytest --cov, JaCoCoЛоги траекторий, симуляторы, краудсорсинг

Агент здесь — это программа, которая с помощью LLM принимает решения: выбирает инструменты, формирует запросы, обрабатывает ответы. Его поведение нельзя описать конечным числом путей в коде, поэтому нужна новая метрика.


2. Почему code coverage не работает для агентов

  • LLM как «чёрный ящик» – генерация текста зависит от вероятностного распределения, а не от ветвлений кода. Покрытие 100% строк Python-прокладки (функции вызова API) не гарантирует, что протестированы все сценарии поведения.
  • Бесконечное пространство действий – агент может комбинировать инструменты, генерировать произвольные запросы, реагировать на ошибочные ответы. Код агента (оркестратор) обычно мал, а вариативность поведения огромна.
  • Неявные состояния – агент накапливает историю диалога, память, результаты вызовов. Code coverage не видит, что, скажем, после третьего вызова API память переполнена.
  • Редкие, но критичные пути – типичный баг агента: «после двух неудачных попыток поиска начать бесконечно крутиться в цикле». Код покрыт, но траектория не протестирована.

Таким образом, coverage|test coverage агента — это мера проверки сценариев, а не строк.


3. Trajectory coverage (покрытие траекторий)

Траектория — это последовательность шагов агента от начального запроса пользователя до финального ответа. Например:

Запрос → инструмент_1 → парсинг ответа → условие_1 → инструмент_2 → финал.

Trajectory coverage (покрытие траекторий]]) = доля протестированных уникальных последовательностей шагов среди всех возможных (или ожидаемых) траекторий.

Пример:

ТраекторияШагиПротестирована?
Простой поискsearch → answerДа
Поиск с уточнениемsearch → parse → search → answerДа
Ошибка инструментаsearch → tool_error → retry → search → answerНет
Цикл при отсутствии данныхsearch → empty → search → empty → ... → timeoutНет

Если из 4 возможных траекторий протестированы 2, то trajectory coverage = 50%. Критично, что непротестированная траектория «цикл» может привести к бесконечному выполнению в продакшене.

Метрика похожа на branch coverage в коде, но ветви здесь — не if-else, а решения LLM (какой инструмент выбрать, как интерпретировать результат).


4. State coverage (покрытие состояний)

Состояние агента — это контекст, в котором он находится в данный момент: содержимое памяти, история диалога, текущая сессия, загруженные документы, параметры вызовов.

State coverage — доля протестированных уникальных комбинаций состояний (или классов состояний) среди всех возможных.

Примеры состояний:

  • 0 запросов (пустая память)
  • 1 успешный запрос, память < 80%
  • 2 неудачных запроса, память > 80%
  • ошибка аутентификации, необходимо перелогиниться

Каждое состояние может влиять на поведение агента. Если протестированы только состояния с малой памятью, то в продакшене при переполнении памяти может возникнуть неожиданное поведение.

Как измерять
Задать список критических состояний (например, через state space — пространство состояний) и проверять, какие из них встретились в тестах.


5. Instrumentation и логирование траекторий

Чтобы измерить coverage, нужно собирать данные о реальном поведении агента в тестах и продакшене. Для этого применяют instrumentation (инструментирование) — добавление логирования в ключевые точки: вызов инструмента, обработка результата, решение LLM.

Пример логируемого события (JSON):

{
  "session_id": "abc123",
  "step": 3,
  "action": "search",
  "input": "температура в Москве",
  "result": {"status": "success"},
  "memory_usage": 0.45,
  "decision": "proceed_to_answer"
}

После сбора логов траектории кластеризуются (например, с помощью sequence matching или Levenshtein distance по списку действий). Редкие траектории (низкая частота) считаются «непокрытыми» и добавляются в план тестирования.

Инструменты

  • Специализированные библиотеки для логирования агентов (LangSmith, Weights & Biases, custom logging)
  • Анализаторы логов (ELK, Grafana + Prometheus для агрегации)

6. Цель покрытия — принцип Парето (80/20)

Не нужно стремиться к 100% coverage (как в коде). Цель — покрыть все часто встречающиеся траектории и состояния. Обычно 80% сессий пользователей проходят по 20% траекторий. Эти 20% должны быть полностью протестированы.

Остальные 80% траекторий — редкие. Их нужно проанализировать и протестировать только те, которые ведут к критическим ошибкам (потеря данных, бесконечный цикл, раскрытие конфиденциальной информации).

Процесс

  1. Собрать логи из продакшена или симуляций.
  2. Подсчитать частоту каждой траектории (топ-N).
  3. Для топ-N траекторий написать автоматизированные тесты.
  4. Для редких траекторий провести ручной анализ рисков и, если риск высок, добавить тесты.
  5. Периодически обновлять метрику по мере изменения агента (fine-tuning, смена инструментов).

7. Пример тестирования траекторий на практике

Допустим, агент для ответов на вопросы по документации компании:

Возможные траектории (фрагмент):

  1. user_query → search → answer (простой вопрос, документ найден)
  2. user_query → search → no_result → reformulate → search → answer (переформулировка)
  3. user_query → search → no_result → reformulate → search → no_result → fallback: «извините, не знаю»
  4. user_query → search → too_many_results → ask_clarification → user_input → search → answer

Тест для траектории №3

def test_no_result_fallback():
    agent = Agent()
    # Мокаем vector search так, чтобы он всегда возвращал пустой результат
    with patch('agent.vector_search', return_value=[]):
        response = agent.process("уникальный запрос без документа")
        assert "извините" in response.lower()

Если такой тест не написан, trajectory coverage ниже. При изменении LLM (например, fine-tune на новую модель) может измениться решение: агент начнёт бесконечно переформулировать. Тест поймает регрессию.


8. Состояния и memory coverage

Кроме траекторий, важно тестировать состояния памяти и сессии:

  • Empty memory — начало диалога.
  • Partial memory — до 50% лимита токенов.
  • Near full memory — > 90%, агент должен сжимать или обрезать историю.
  • Error state — предыдущий вызов инструмента вернул ошибку.

Таблица test cases для состояний

СостояниеОписаниеТестовый сценарий
memory_fullПамять заполнена на 100%Создать 100 последовательных запросов, проверить, что агент не падает
tool_errorИнструмент вернул HTTP 500Проверить, что агент делает retry (или graceful shutdown)
timeoutИнструмент не отвечает 30 сПроверить timeout и логирование

Пет-проект для закрепления

Задача Разработать простого агента для извлечения информации из новостных RSS и написать систему измерения test coverage его траекторий.

Инструменты Python, OpenAI API (или локальная LLM), pandas, pytest, библиотека для RSS (feedparser), JSON-логирование.

Шаги:

  1. Реализовать агента с двумя инструментами: fetch_rss(url) и summarize(text). Агент получает запрос типа «найди последние новости про ИИ и кратко перескажи».
  2. Определить 4–6 возможных траекторий: успешный fetch + успешный summarize, ошибка fetch (недоступен источник), ошибка summarize (текст слишком длинный), пустой RSS и т.д.
  3. Написать инструментирование: логировать каждый шаг в JSON-файл.
  4. Создать тестовый сценарий и написать pytest-тесты для каждой траектории, используя моки (unittest.mock) для имитации ответов.
  5. Написать скрипт, который читает логи тестов и подсчитывает trajectory coverage: число уникальных протестированных траекторий / общее число определённых траекторий.
  6. Дополнительно: ввести state coverage — проверить, что протестированы состояния empty_memory, after_error, summary_too_long.

Ожидаемый результат

  • Агент, который легко мокается и логируется.
  • Отчёт о coverage (например: Trajectory coverage: 83% (5 из 6), State coverage: 75% (3 из 4)).
  • Чёткое понимание, какую траекторию нужно дотестировать.

Связь с другими вопросами

ВопросТема
790Что такое агент в контексте LLM и RAG?
791Как проектировать траектории агента?
793Как мониторить агентов в продакшене?
795Как организовать регрессионное тестирование агентов?
800Какие метрики качества для агента существуют?

Навигация