English translation is not available yet. Showing Russian content.

Интегрировать тестирование в CI/CD

ТЕХНИЧЕСКОЕ ЗАДАНИЕ: Интегрировать тестирование в CI/CD

1. Цель задачи

Разработать и внедрить процесс автоматизированного тестирования AI-агента, который выполняется при каждом Pull Request. Настроить quality gates — пороговые метрики, при падении которых PR блокируется. В результате команда получает CI/CD pipeline, гарантирующий стабильное качество основного сценария агента перед мержем.

Ключевой результат Pipeline в Actions (или аналоге), который при каждом PR запускает тесты агента, сравнивает метрики с порогами и блокирует мерж при их падении.


2. Исходные данные

Что нужноОткуда взять
Репозиторий с кодом AI-агента (любой агент на LangGraph / LangChain / custom)Собственный пет-проект, Pet 221 (RAG-агент), или учебный репозиторий
Тестовые сценарии (не менее 3-х)Реальные примеры запросов + эталонные ответы (вручную или синтезированные)
CI-система с доступом к репозиториюGitHub Actions, GitLab CI, Jenkins — предпочтительно GitHub Actions
Базовые метрики агента (pass rate, latency, cost)Запуск тестов на main ветке до изменений
Docker / Python окружение для воспроизведенияDockerfile или requirements.txt

Если нет реального агента — симулируем:

  1. Создать минимального RAG-агента на LangChain: retriever (FAISS с тестовыми документами) + LLM (любая дешёвая модель, например gpt-3.5-turbo или локальная через Ollama).
  2. Подготовить 10 тестовых вопросов с эталонными ответами (txt/csv).
  3. Опубликовать репозиторий на GitHub.

3. Технологический стек

КомпонентИнструментыНазначение
CI/CDGitHub ActionsАвтоматический запуск тестов на PR
Тестовый фреймворкpytest + pytest-covНаписание и выполнение тестов
Метрики качестваRAGAS (или самописные: точность ответа, latency)Оценка ответов агента
Оркестрация агентаLangChain / LangGraphОсновной код агента
Векторное хранилищеFAISS (тестовая in-memory)Retrieval для тестов
LLMOllama (llama3 8B) или OpenAIГенерация ответов
ДокеризацияDocker + docker-composeИзолированный запуск в CI
Quality gatesСкрипт проверки метрик (Python)Сравнение с порогами

4. Этапы выполнения

Этап 1: Подготовка тестового окружения и репозитория (1 час)

Действия

  1. Форкнуть или создать репозиторий с кодом агента. Убедиться, что проект собирается локально (pip install -r requirements.txt).
  2. Добавить конфигурацию Docker
    • Dockerfile с Python 3.11, установкой зависимостей, копированием кода.
    • docker-compose.yml для запуска тестов (опционально – с LLM через Ollama).
  3. Создать папку tests/ с файлом conftest.py для фикстур (агент, тестовые данные).
  4. Зафиксировать baseline-метрики
    • Запустить тесты вручную на ветке main, сохранить результаты в baseline.json.

Ожидаемый результат этапа Рабочее Docker-окружение, структура тестов, baseline-метрики.

Этап 2: Написание тестов для агента (2-3 часа)

Действия

  1. Unit-тесты компонентов

    • test_retriever.py — проверка, что retriever возвращает не менее K документов на тестовый запрос.
    • test_llm.py — проверка, что LLM не возвращает пустой ответ.
    • test_agent.py — проверка корректной маршрутизации (если используется router).
  2. Интеграционные тесты

    • test_full_flow.py — запуск агента на заранее подготовленных 10 вопросах, сравнение ответов с эталонными или оценка через RAGAS.
  3. Написание тестов с использованием pytest

    # example test_full_flow.py
    import pytest
    from agent import Agent
    from metrics import compute_accuracy, compute_latency
    
    TEST_QUESTIONS = [...]  # 10 вопросов
    EXPECTED_ANSWERS = [...]  # эталонные ответы
    
    @pytest.fixture
    def agent():
        return Agent()
    
    def test_accuracy(agent):
        correct = 0
        for q, a in zip(TEST_QUESTIONS, EXPECTED_ANSWERS):
            response = agent.run(q)
            if similarity(response, a) > 0.7:  # порог для ручной проверки
                correct += 1
        accuracy = correct / len(TEST_QUESTIONS)
        assert accuracy >= 0.6, f"Accuracy {accuracy} < 0.6"
    
    def test_latency(agent):
        latencies = []
        for q in TEST_QUESTIONS:
            t0 = time.time()
            agent.run(q)
            latencies.append(time.time() - t0)
        avg_latency = sum(latencies) / len(latencies)
        assert avg_latency <= 5.0, f"Avg latency {avg_latency} > 5.0s"
    
  4. Добавить тесты на безопасность (опционально):

    • Проверка, что агент не выдает запрещённые ответы (фильтр токсичности).

Ожидаемый результат этапа Пакет тестов, которые проходят на baseline (main) и могут быть запущены через pytest.

Этап 3: Реализация quality gates (1 час)

Действия

  1. Создать скрипт quality_gate.py

    • Читает результаты тестов (pass/fail) и метрики.
    • Сравнивает с порогами (из baseline.json или явно заданными).
    • Возвращает exit code 0 — проход, 1 — блокировка.
  2. Пороги (пример):

    • Точность ответов (accuracy): >= 0.7 (или снижение не более 5% от baseline).
    • Средняя задержка: <= 5 сек.
    • Pass rate всех pytest: 100% (никакие тесты не падают).
  3. Интеграция с pytest

    • Использовать pytest.ini с настройками.
    • В conftest.py добавить хуки для сбора метрик в JSON.

Ожидаемый результат этапа Скрипт, который можно запустить после тестов и получить результат «proceed» или «block».

Этап 4: Настройка CI/CD pipeline (2 часа)

Действия

  1. Создать .github/workflows/test_on_pr.yml

    name: PR Tests & Quality Gates
    
    on:
      pull_request:
        branches: [ main ]
    
    jobs:
      test:
        runs-on: ubuntu-latest
        steps:
          - uses: actions/checkout@v4
    
          - name: Build Docker image
            run: docker build -t agent-test .
    
          - name: Run tests
            run: docker run --rm agent-test pytest tests/ --json-report=report.json
    
          - name: Upload test report
            uses: actions/upload-artifact@v4
            with:
              name: test-report
              path: report.json
    
          - name: Check quality gates
            run: docker run --rm agent-test python quality_gate.py --report report.json
            # quality_gate.py вернёт exit 1 → PR будет помечен как failed
    
  2. Добавить защиту ветки main (branch protection):

    • В GitHub: Settings → Branches → Add rule.
    • Включить «Require status checks to pass before merging».
    • Выбрать статус «PR Tests & Quality Gates».
  3. Проверить pipeline на реальном PR

    • Создать ветку с намеренным ухудшением (например, уменьшить количество документов в retriever).
    • Убедиться, что тесты падают, quality gate блокирует, PR не мержится.

Ожидаемый результат этапа Работающий pipeline, блокирующий PR при падении тестов или выходе метрик за порог.

Этап 5: Документация и демонстрация (1 час)

Действия

  1. Написать README.md в репозитории с описанием:

    • Как запустить тесты локально.
    • Какие quality gates настроены.
    • Скриншот успешного и неуспешного PR.
  2. Создать небольшой слайд (или раздел в wiki) для команды: цель, метрики, процесс.

  3. Зафиксировать артефакты

    • Baseline метрики.
    • Пример отчёта с падением.

Ожидаемый результат этапа Документированный процесс, готовый к использованию.


5. Критерии приемки (Definition of Done)

  • Pipeline запускается на каждый PR в main и на каждый push в PR.
  • Тесты включают как unit, так и интеграционные (не менее 3).
  • Quality gates проверяют как минимум: точность ответов и среднюю задержку.
  • При падении любого теста или выходе метрики за порог PR получает статус failed.
  • Настроена защита ветки main: требуется успешный статус pipeline.
  • Документация (README) описывает, как добавить новый тест или изменить порог.
  • Baseline-метрики зафиксированы в репозитории.
  • Pipeline выполняется менее чем за 10 минут (с учётом загрузки модели/докера).
  • Все тесты проходят на ветке main.
  • В репозитории есть файл quality_gate.py с читаемыми порогами.

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

Основной артефакт Репозиторий с кодом агента и настроенным CI/CD pipeline.

  • Файл .github/workflows/test_on_pr.yml.
  • Папка tests/ с тестами.
  • Файл quality_gate.py.
  • Файл baseline.json.
  • README.md с инструкцией.

Дополнительно Демонстрация, что PR с плохим кодом блокируется, а хороший — проходится.


7. Возможные сложности и их решение

СложностьРешение
LLM модель недоступна в CI (требует API ключ)Использовать локальную модель через Ollama в Docker. Или мокировать LLM в тестах.
Долгий запуск (скачивание модели)Закешировать Docker слои с моделью; использовать cache: actions/cache для Ollama.
Метрики субъективны (точность ответа)Использовать LLM-as-judge (например, GPT-4 оценивает ответ) или метрики RAGAS.
Flaky тесты из-за недетерминизма LLMЗафиксировать seed/температуру=0; использовать deterministic модели (например, local small model).
Пороги quality gates устареваютBaseline обновлять после каждого успешного мержа (commit baseline.json с новыми значениями).

8. Бюджет времени (оценка)

ЭтапВремя
Этап 1: Подготовка окружения1 час
Этап 2: Написание тестов2-3 часа
Этап 3: Quality gates1 час
Этап 4: CI/CD pipeline2 часа
Этап 5: Документация1 час
Итого7-8 часов

Примечание: для первого раза может потребоваться до 10 часов из-за отладки инфраструктуры Docker/GitHub Actions.


9. Связанные вопросы из базы знаний

ВопросТема
157Интеграция тестирования в CI/CD (данная задача)
12Как написать assert для метрик качества ответа
38Настройка GitHub Actions для Python проекта
41Использование Docker в CI для воспроизводимости
89Что такое quality gates и как их настроить
103Оценка качества RAG-агента (RAGAS)
144Лучшие практики тестирования LLM-агентов
215Защита веток в GitHub (branch protection)
301Mocking LLM в тестах
442Подходы к детерминированному тестированию

10. Чек-лист самопроверки

  • Я подключил репозиторий к CI и настроил триггер на PR.
  • Написал хотя бы 3 теста, включая интеграционный, и они проходят локально.
  • Скрипт quality_gate.py корректно завершается с exit 1 при плохих метриках.
  • В .github/workflows/ есть корректный YAML, и pipeline запустился на тестовом PR.
  • Ветка main защищена: требуется статус pipeline перед мержем.
  • README содержит как минимум команды для локального запуска тестов.
  • Файл baseline.json закоммичен и соответствует реальным метрикам на main.
  • Поменял код так, чтобы тест упал, — pipeline действительно заблокировал PR.
  • Pipeline не превышает лимит времени выполнения (10 минут).