Интеграция DeepEval для тестирования LlamaIndex Workflow

Введение
Тестирование сложных систем, таких как LlamaIndex Workflow, включающих несколько шагов, извлечение данных и генерацию текста с помощью LLM, является нетривиальной задачей. Стандартные методы тестирования не всегда могут оценить семантическое качество и релевантность генерируемых ответов. DeepEval предоставляет набор инструментов и метрик, специально разработанных для оценки LLM-приложений, что делает его подходящим решением для тестирования LlamaIndex Workflow.
В ходе исследования были рассмотрены два основных подхода к интеграции DeepEval:
Прямое использование DeepEval: Оценка результатов Workflow с помощью функции deepeval.evaluate () в обычном Python-скрипте.
Интеграция с Pytest: Использование deepeval.assert_test () в рамках тестового фреймворка Pytest для автоматической проверки соответствия метрик заданным порогам и интеграции с CI/CD.
В качестве примера использовался тестовый Workflow на базе LlamaIndex, реализующий простой RAG Workflow с моками Retriever и LLM, и оценивающий финальный результат с помощью метрик AnswerRelevancy и Faithfulness.
Подход 1: Прямое использование DeepEval (evaluate)
Этот подход фокусируется на получении оценок и их анализе без привязки к стандартным тестовым фреймворкам.
Процесс:
Запускается LlamaIndex Workflow для конкретного входного запроса (await workflow.run (input_query=…)).
Из финального события Workflow (например, RagWorkflowOutput) извлекаются необходимые данные: input (исходный запрос), actual_output (сгенерированный ответ), retrieval_context (извлеченный контекст).
Создается экземпляр deepeval.test_case.LLMTestCase, заполненный этими данными.
Определяются необходимые метрики DeepEval (например, AnswerRelevancyMetric, FaithfulnessMetric) с их порогами.
Вызывается функция deepeval.evaluate (test_cases=[test_case], metrics=[list_of_metrics]).
Пример кода:
# ... (после получения final_event из workflow.run) test_case = LLMTestCase( input=final_event.original_query, actual_output=final_event.final_answer, retrieval_context=final_event.retrieved_context ) answer_relevancy_metric = AnswerRelevancyMetric(threshold=0.7) faithfulness_metric = FaithfulnessMetric(threshold=0.8) evaluation_results = evaluate( test_cases=[test_case], metrics=[answer_relevancy_metric, faithfulness_metric] ) print(evaluation_results) # Вывод результатов # ... (добавлен код для детальной распечатки evaluation_results.test_results)
Результат:
В консоль выводится прогресс-бар, сводка по метрикам (название, оценка, порог, статус ✅/❌, причина) и общие показатели Pass Rate.
Если пользователь залогинен (deepeval login), результаты оценки (включая детали тест-кейса и метрик) автоматически отправляются на дашборд Confident AI для хранения, визуализации и сравнения между запусками.
Функция evaluate возвращает объект EvaluationResult, содержащий структурированные данные по всем оценкам.
Преимущества:
Простота реализации в обычном скрипте.
Наглядный вывод результатов в консоль.
Автоматическая отправка данных на дашборд для анализа.
Хорошо подходит для исследовательских целей, ручной проверки качества и первоначальной настройки метрик.
Недостатки для автоматизации:
Функция evaluate не прерывает выполнение скрипта и не возвращает код ошибки, даже если какие-то метрики не прошли проверку по порогу (success=False).
Для интеграции с CI/CD (автоматического определения «сборка прошла/не прошла») требуется дополнительная логика в скрипте: нужно вручную анализировать возвращенный объект evaluation_results, проверять поле success у каждой метрики и явно завершать скрипт с ненулевым кодом выхода (sys.exit (1)) или вызывать исключение при обнаружении проваленной метрики.
Подход 2: Интеграция с Pytest (assert_test)
Этот подход встраивает оценку DeepEval в стандартный процесс тестирования с использованием фреймворка Pytest, что идеально подходит для автоматизации и CI/CD.
Процесс:
Тестовая логика (запуск Workflow, подготовка LLMTestCase, определение метрик) оборачивается в тестовую функцию Pytest (например, async def test_workflow_evaluation (…), помеченную @pytest.mark.asyncio).
Вместо evaluate (…) используется функция deepeval.assert_test (test_case, [list_of_metrics]).
Тесты запускаются через команду deepeval test run <имя_файла_с_тестами>.py (которая использует Pytest под капотом).
Пример кода:
import pytest @pytest.mark.asyncio async def test_rag_workflow_with_deepeval(): # Название функции для pytest # ... (код запуска Workflow и создания test_case - как раньше) ... answer_relevancy_metric = AnswerRelevancyMetric(threshold=0.7) faithfulness_metric = FaithfulnessMetric(threshold=0.8) # Вместо evaluate используем assert_test assert_test(test_case, [answer_relevancy_metric, faithfulness_metric]) # Если assert_test не вызвал ошибку, тест считается пройденным print("DeepEval assert_test evaluation completed successfully!")
Результат:
assert_test вычисляет все переданные метрики.
Она сравнивает score каждой метрики с ее threshold.
Ключевое отличие: Если хотя бы одна метрика не проходит проверку по порогу (score < threshold), assert_test вызывает исключение AssertionError.
Pytest перехватывает это AssertionError и помечает тест как проваленный (FAILED).
Если все метрики прошли проверку, AssertionError не вызывается, и Pytest помечает тест как пройденный (PASSED).
При запуске через deepeval test run, результаты (статус PASS/FAIL, детали метрик) также отправляются на дашборд Confident AI в виде «Test Run».
Преимущества:
Полная автоматизация CI/CD: Проваленный тест (из-за AssertionError) автоматически приводит к ненулевому коду выхода, что сигнализирует CI/CD системе о проблеме без необходимости писать дополнительную логику проверки результатов.
Использование возможностей Pytest: Легко применять фикстуры (для инициализации Workflow, LLM, Retriever), параметризацию (для запуска одного теста на множестве входных данных), маркировку тестов и т.д.
Структурированное тестирование: Позволяет организовать тесты в понятную структуру.
Единый запуск и отчетность: Команда deepeval test run запускает все тесты и формирует единый отчет на Confident AI.
Недостатки:
Требует базового понимания Pytest.
Стандартный вывод Pytest фокусируется на статусе PASS/FAIL, а не на немедленной детальной распечатке метрик (хотя они доступны в отчете на дашборде).
Сравнение и рекомендации
Характеристика | Подход 1 (evaluate) | Подход 2 (assert_test + Pytest) |
Основная цель | Оценка, логирование, анализ результатов | Автоматическая проверка Pass/Fail |
CI/CD Интеграция | Требует доп. логики для Pass/Fail | Встроенная (через AssertionError) |
Запуск | python script.py | deepeval test run script.py |
Вывод в консоль | Подробная сводка метрик | Статус PASS/FAIL (детали — в отчете) |
Отправка на дашборд | Да (как Evaluation Run) | Да (как Test Run) |
Структура тестов | Отсутствует (обычный скрипт) | Предоставляется Pytest |
Простота | Выше для простых сценариев | Требует знания Pytest |
Рекомендации:
Используйте Подход 1 (evaluate), когда вы:
Только начинаете интегрировать DeepEval и хотите исследовать метрики.
Проводите ручную оценку или отладку конкретных кейсов.
Не используете CI/CD или готовы написать свою логику для определения Pass/Fail статуса пайплайна.
Используйте Подход 2 (assert_test + Pytest), когда вы:
Хотите построить автоматизированный набор регрессионных тестов.
Нуждаетесь в интеграции с CI/CD для автоматической проверки качества при изменениях.
Хотите использовать возможности тестовых фреймворков (фикстуры, параметризация) для организации тестов.
Планируете регулярно запускать тесты и сравнивать результаты прогонов на дашборде.
Заключение
DeepEval предоставляет мощные возможности для оценки качества LlamaIndex Workflow, выходя за рамки традиционного тестирования. Прямое использование функции evaluate удобно для анализа и логирования, в то время как интеграция с Pytest через assert_test и запуск с помощью deepeval test run открывают путь к полной автоматизации тестирования и интеграции в CI/CD пайплайны, что является предпочтительным подходом для поддержания качества сложных LLM-систем в долгосрочной перспективе.