Настроить RAGAS evaluation pipeline с автоматическим запуском при каждом PR

ТЕХНИЧЕСКОЕ ЗАДАНИЕ: Настроить RAGAS evaluation pipeline с автоматическим запуском при каждом PR

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

Создать CI/CD-пайплайн на GitHub Actions, который при каждом Pull Request автоматически запускает оценку качества RAG-системы с помощью библиотеки RAGAS. Пайплайн должен генерировать читаемый отчёт (HTML + markdown) с метриками (faithfulness, answer relevancy, context precision, context recall) за время не более 5 минут для набора из 20–30 тестовых примеров. Дополнительно внедрить регрессионный тест: если хотя бы одна метрика падает ниже заданного порога относительно последнего main-релиза, CI должен завершиться с ошибкой.

Ключевой результат автоматизированный evaluation-пайплайн, который блокирует PR при деградации качества и предоставляет разработчику прозрачный отчёт за 5 минут.

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

Что нужноОткуда взять
RAG-приложение (на Python)Взять из examples/rag_app внутри этого репозитория (если нет — создать минимальный RAG на LangChain с FAISS и OpenAI API)
Тестовый набор вопросов + ground truth ответовИспользовать встроенный датасет из RAGAS: ragas.testset.synthesizer.generate (синтезировать 30 примеров)
Конфигурация CI (GitHub Actions)Файл .github/workflows/eval.yml
Результаты baseline (main-ветка)Сохранить в ragas_baseline.json рядом с кодом; обновлять при мерже в main

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

  1. Создайте папку rag_app/ с файлом rag.py, который реализует класс RAGEngine с методом answer(question: str) -> dict (текст ответа + список контекстов).
  2. Используйте эмбеддинги OpenAI text-embedding-3-small и LLM gpt-4o-mini (дешёво и быстро).
  3. Для тестовых вопросов — запустите генератор RAGAS один раз вручную, сохраните в test_data.json.

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

КомпонентИнструментыНазначение
Библиотека оценкиragas (>=0.2.0)Вычисление метрик faithfulness, answer_relevancy, context_precision, context_recall
RAG-фреймворкLangChain (>=0.3.0) + FAISSПостроение минимального RAG
ЯзыкPython 3.11Основной язык скриптов
CI/CDGitHub ActionsАвтоматический запуск оценки на каждый PR
Формат отчётаHTML (jinja2 шаблон) + MarkdownВизуализация метрик
Хранение baselineJSONСравнение с предыдущим стабильным состоянием
ТестированиеpytestПроверка корректности работы пайплайна

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

Этап 1: Подготовка окружения и минимального RAG (оценка 45 мин)

Действия

  1. Установите зависимости:
    pip install ragas langchain langchain-openai faiss-cpu jinja2 pytest
    
  2. Создайте структуру проекта:
    .
    ├── rag_app/
    │   ├── __init__.py
    │   └── rag.py          # RAGEngine класс
    ├── eval/
    │   ├── __init__.py
    │   ├── run_eval.py     # скрипт запуска оценки
    │   └── report_template.html  # шаблон отчёта
    ├── test_data.json      # тестовые примеры (будет сгенерирован)
    ├── ragas_baseline.json # baseline метрики (создать пустым)
    └── .github/
        └── workflows/
            └── eval.yml
    
  3. Реализуйте RAGEngine в rag_app/rag.py:
    • Используйте OpenAIEmbeddings + FAISS для векторного поиска.
    • Используйте ChatOpenAI(model="gpt-4o-mini") для генерации ответа.
    • Метод answer(question) -> {"answer": str, "contexts": list[str]}.

Ожидаемый результат этапа
Рабочая RAG-система, принимающая вопрос и возвращающая ответ с контекстом. Успешный тест: python -c "from rag_app.rag import RAGEngine; eng = RAGEngine(); print(eng.answer('Что такое RAG?'))".

Этап 2: Генерация тестового набора и написание скрипта оценки (оценка 1 час)

Действия

  1. Создайте скрипт eval/prepare_testset.py, который:

    • Использует ragas.testset.synthesizer.Synthesizer для генерации 30 вопросов на основе документации (например, статьи из Wikipedia).
    • Сохраняет результат в test_data.json (формат: [{"question": ..., "ground_truth": ..., "contexts": ...}]).
    • Запустите скрипт однократно, результат закоммитьте.
  2. Напишите eval/run_eval.py:

    • Загружает test_data.json и baseline (ragas_baseline.json, если существует).
    • Для каждого вопроса вызывает RAGEngine.answer() и собирает результаты.
    • Вычисляет метрики RAGAS с использованием Evaluate:
      from ragas import evaluate
      from ragas.metrics import faithfulness, answer_relevancy, context_precision, context_recall
      # ... подготовка dataset ...
      result = evaluate(dataset, metrics=[faithfulness, ...])
      
    • Генерирует HTML-отчёт (используя jinja2 и шаблон report_template.html) и JSON с метриками.
    • Сравнивает каждую метрику с baseline: если drop > 0.05 (5%), выбрасывает исключение RegressionError.
    • Сохраняет последний результат в ragas_latest.json (для артефактов CI).
  3. Создайте шаблон report_template.html:

    • Таблица с метриками (имя, текущее значение, baseline, дельта).
    • Гистограмма распределения метрик по вопросам.
    • Список топ-5 худших вопросов для каждой метрики.

Ожидаемый результат этапа
Выполнение python eval/run_eval.py проходит без ошибок и генерирует файлы report.html, ragas_latest.json.

Этап 3: Настройка регрессионного сравнения (оценка 30 мин)

Действия

  1. В run_eval.py добавьте логику:

    if baseline_exists:
        for metric_name in ["faithfulness", "answer_relevancy", "context_precision", "context_recall"]:
            current = result[metric_name]
            baseline = baseline[metric_name]
            if current < baseline - 0.05:
                raise RegressionError(f"Drop in {metric_name}: {current:.3f} vs {baseline:.3f}")
    
  2. При успешном проходе сохраните ragas_latest.json как новый baseline (но только после мержа в main – это будет сделано на этапе CI).

  3. Протестируйте: искусственно ухудшите RAG (например, удалите один из контекстов) и убедитесь, что скрипт падает.

Ожидаемый результат этапа
Скрипт корректно обнаруживает регрессии и завершается с ненулевым кодом выхода.

Этап 4: Интеграция с GitHub Actions (оценка 1.5 часа)

Действия

  1. Создайте .github/workflows/eval.yml:

    name: RAGAS Evaluation
    on:
      pull_request:
        branches: [main]
    jobs:
      evaluate:
        runs-on: ubuntu-latest
        steps:
          - uses: actions/checkout@v4
          - name: Set up Python 3.11
            uses: actions/setup-python@v5
            with:
              python-version: '3.11'
          - name: Install dependencies
            run: pip install -r requirements.txt
          - name: Run evaluation
            run: python eval/run_eval.py
            env:
              OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
          - name: Upload report
            uses: actions/upload-artifact@v4
            with:
              name: eval-report
              path: |
                report.html
                ragas_latest.json
          - name: Comment PR
            uses: actions/github-script@v7
            with:
              script: |
                const fs = require('fs');
                const report = fs.readFileSync('report.html', 'utf8');
                github.rest.issues.createComment({
                  issue_number: context.issue.number,
                  owner: context.repo.owner,
                  repo: context.repo.repo,
                  body: `## RAG Evaluation Report\n\n<details><summary>Metrics</summary>\n\n${report.substring(0, 30000)}\n\n</details>`
                });
    
  2. Добавьте шаг pytest для проверки работоспособности вспомогательных функций.

  3. Протестируйте: сделайте PR с изменением кода RAG, убедитесь, что CI запускается, падает при регрессии и публикует отчёт.

Ожидаемый результат этапа
При каждом PR в main автоматически запускается оценка, отчёт загружается как артефакт, а также публикуется в комментарии PR.

Этап 5: Обновление baseline при мерже (оценка 30 мин)

Действия

  1. Добавьте второй workflow update_baseline.yml:
    on:
      push:
        branches: [main]
    jobs:
      update-baseline:
        runs-on: ubuntu-latest
        steps:
          - uses: actions/checkout@v4
          - name: Run eval
            run: python eval/run_eval.py
            env:
              OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
          - name: Copy as new baseline
            run: cp ragas_latest.json ragas_baseline.json
          - name: Commit new baseline
            uses: stefanzweifel/git-auto-commit-action@v5
            with:
              commit_message: "chore: update RAGAS baseline [skip ci]"
    
  2. Убедитесь, что ragas_baseline.json не отслеживается в .gitignore для main, но обновляется только через этот workflow.

Ожидаемый результат этапа
После мержа PR baseline автоматически обновляется до метрик нового кода.

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

  • При каждом PR на main запускается job evaluate в GitHub Actions.
  • Полный цикл оценки (генерация ответов + вычисление метрик + генерация отчёта) занимает ≤ 5 минут для 30 вопросов.
  • Сгенерированный report.html содержит все четыре метрики RAGAS, их средние значения, распределение и худшие примеры.
  • В случае падения любой метрики более чем на 5% от baseline CI завершается с ошибкой (красный статус).
  • Отчёт в виде HTML доступен как артефакт workflow и как комментарий к PR.
  • На ветке main после мержа автоматически обновляется ragas_baseline.json.
  • В корне репозитория есть README.md с инструкцией по локальному запуску (python eval/run_eval.py).

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

Основной артефакт

  • Файл .github/workflows/eval.yml — готовый CI пайплайн.
  • Файл .github/workflows/update_baseline.yml — автоматическое обновление baseline при мерже.
  • Файлы: eval/run_eval.py, eval/prepare_testset.py, eval/report_template.html.
  • Файл test_data.json — тестовый набор (30 вопросов).

Дополнительные результаты

  • Локальная возможность запуска run_eval.py для быстрой итерации.
  • Возможность расширения метрик (например, harmfulness или custom_metric).
  • Документирование процесса добавления новых тестовых примеров.

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

СложностьРешение
Время выполнения > 5 минут при 30 вопросахУменьшить число вопросов до 20; использовать gpt-4o-mini вместо gpt-4; кэшировать эмбеддинги вопросов.
OpenAI API rate limitsДобавить tenacity retry с exponential backoff; использовать ключ с достаточным TPM (>= 60k).
Изменчивость метрик из-за недетерминированности LLMВыполнять каждый вопрос 3 раза и усреднять; фиксировать temperature=0.
Большой HTML-отчёт не помещается в комментарий PRУрезать детализацию в комментарии (только средние метрики и список падений), полный отчёт в артефактах.
Сложность локального запуска из-за API ключейИспользовать .env файл, загружаемый через python-dotenv; документировать.

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

ЭтапВремя
Этап 1: Подготовка окружения и RAG45 мин
Этап 2: Генерация тестов и скрипт оценки60 мин
Этап 3: Регрессионное сравнение30 мин
Этап 4: Интеграция с GitHub Actions90 мин
Этап 5: Обновление baseline30 мин
Итого4 ч 15 мин

Примечание: для первого раза заложите +30% на отладку CI триггеров и правку шаблона отчёта.

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

ВопросТема
21Что такое RAGAS и какие метрики он предоставляет?
45Как синтезировать тестовые наборы для RAG?
78CI/CD для ML проектов: best practices
112Оценка faithfulness: принцип работы
134Использование GitHub Actions для периодического запуска evaluation
201Сравнение метрик до/после изменений: статистические тесты
245Как построить дашборд для мониторинга качества RAG?
267Регрессионные тесты для NLP-моделей
311Кастомные метрики в RAGAS
401Интеграция LLM-as-judge в CI pipeline

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

  • Я локально запустил python eval/run_eval.py и получил report.html и ragas_latest.json.
  • Я проверил, что при искусственном ухудшении RAG скрипт завершается с ошибкой и красным статусом.
  • Я закоммитил test_data.json и убедился, что CI использует именно этот файл.
  • Я протестировал полный сценарий PR: создал ветку, изменил RAG, запушил, проверил, что GitHub Actions выполнился, а отчёт отобразился.
  • Я обновил README.md с командой для локального запуска и указал переменную окружения OPENAI_API_KEY.