Настроить regression testing промптов

ТЕХНИЧЕСКОЕ ЗАДАНИЕ: Настроить regression testing промптов

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

Настроить автоматическое регрессионное тестирование для промптов, используемых в LLM-приложении. Создать golden set из 100 пар «запрос → эталонный ответ» и внедрить CI-пайплайн, который при каждом изменении промпта (или конфигурации модели) запускает тесты и гарантирует, что ответы LLM не изменились (или не деградировали) относительно эталона.

Ключевой результат При любом коммите, затрагивающем файлы с промптами или параметры генерации, в CI запускается набор тестов, который должен пройти с успешностью 100% — все ответы на golden set точно совпадают с эталонными (exact match или эквивалентность с заданным порогом). В случае несовпадения пайплайн падает, и команда получает уведомление.

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

Что нужноОткуда взять
Рабочее LLM-приложение с промптами (Python, LangChain, OpenAI API, или кастомное)Существующий проект / pet-проект
Список из 100 разнообразных запросов (представительный для домена)Ручной сбор + аугментация / синтез
Эталонные ответы для каждого запроса (сгенерированы один раз и проверены вручную)Однократная генерация при фиксированной модели и seed=0
CI/CD-система (GitHub Actions, GitLab CI, Jenkins)Репозиторий проекта
Python 3.10+Система разработки

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

  1. Создаём простой Python-скрипт prompt_engine.py, который принимает строку запроса и заданный шаблон промпта, вызывает LLM (openai / mock), возвращает ответ.
  2. В качестве модели используем OpenAI GPT-4-mini (или любой доступный endpoint). Если нет ключа — используем симуляцию: возвращаем фиксированный ответ для каждого запроса из golden set (так регрессия станет абсолютной). Для учебных целей допускается mock.
  3. Разворачиваем репозиторий на GitHub, настраиваем Actions.

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

КомпонентИнструментыНазначение
Тестовый фреймворкpytest + pytest-jsonЗапуск тестов, сериализация результатов
Golden setJSON-файл (100 записей: {"query": ..., "expected": ...})Хранение эталонных пар
CI-пайплайнGitHub Actions / GitLab CIАвтоматический запуск тестов при каждом изменении
Сравнение ответовdifflib.SequenceMatcher или rouge-score (exact match при seed=0)Детерминированное сравнение строк
Управление зависимостямиpip + requirements.txt или poetryФиксация версий библиотек
Линтер/форматтерblack, flake8 (опционально)Единый стиль кода

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

Этап 1: Формирование golden set (2–3 часа)

Действия

  1. Составить 100 запросов, покрывающих все типичные сценарии: короткие/длинные, простые/сложные, краевые случаи (пустой запрос, очень длинный контекст, нецензурная лексика, переключение языка и т.д.). Записать в файл golden_set_inputs.txt (по одному запросу на строку).
  2. Сгенерировать эталонные ответы — запустить текущий prompt_engine.py со всеми запросами при температура=0, seed=42, фиксированном системном сообщении. Ответы сохранить в golden_set_expected.txt.
  3. Валидировать ответы вручную (выборочно 5–10 штук) — убедиться, что они осмысленны и соответствуют ожиданиям. При необходимости подкорректировать запросы или ответы.
  4. Сформировать итоговый JSON-файл golden_set.json:
[
  {
    "id": 1,
    "query": "What is the capital of France?",
    "expected": "The capital of France is Paris."
  },
  ...
]

Ожидаемый результат этапа Файл golden_set.json из 100 записей, готовых к использованию в тестах.

Этап 2: Разработка тестового фреймворка (2–3 часа)

Действия

  1. Создать структуру репозитория
    tests/
        conftest.py
        test_regression.py
    golden_set.json
    requirements.txt
    .github/workflows/regression.yml
    prompt_engine.py
    
  2. Написать conftest.py — фикстуру, загружающую golden_set.json и возвращающую список тестовых случаев.
  3. Написать test_regression.py:
import json
import pytest
from prompt_engine import generate_answer  # функция, которая принимает query, возвращает ответ

def load_golden_set():
    with open("golden_set.json", "r") as f:
        return json.load(f)

@pytest.mark.parametrize("case", load_golden_set(), ids=lambda c: c["id"])
def test_regression(case):
    actual = generate_answer(case["query"], temperature=0, seed=42)
    assert actual == case["expected"], f"Query {case['id']}: expected '{case['expected']}', got '{actual}'"
  1. Убедиться, что generate_answer использует фиксированные параметры (seed, temperature) и ту же версию модели, что и при создании golden set. Если модель меняется, golden set должен обновляться отдельным процессом.
  2. Запустить тесты локально — pytest tests/ -v — все 100 должны пройти.

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

Этап 3: Интеграция в CI (1–2 часа)

Действия

  1. Создать файл GitHub Actions .github/workflows/regression.yml:
name: Prompt Regression Tests
on: [push, pull_request]

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - name: Set up Python
        uses: actions/setup-python@v4
        with:
          python-version: '3.10'
      - name: Install dependencies
        run: pip install -r requirements.txt
      - name: Run regression tests
        run: pytest tests/ -v --junitxml=report.xml
      - name: Upload test report
        if: failure()
        uses: actions/upload-artifact@v3
        with:
          name: test-report
          path: report.xml
  1. Добавить секреты (например, OPENAI_API_KEY) в настройках репозитория.
  2. Сделать коммит с изменением промпта (например, добавить слово "please" в системное сообщение) — CI должен упасть.
  3. Проверить, что лог ошибки показывает, на каком запросе произошло расхождение.
  4. Добавить отправку уведомлений (Slack / email) при падении тестов (опционально, можно в следующей итерации).

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

Этап 4: Документирование и мониторинг (1 час)

Действия

  1. Написать README.md с описанием:
    • Как обновить golden set (процедура: python scripts/update_golden_set.py, ручная проверка).
    • Как добавить новый запрос (добавить в JSON, сгенерировать ответ, запустить тесты).
    • Как интерпретировать отчёт CI.
  2. Добавить скрипт обновления update_golden_set.py, который генерирует новые эталонные ответы при смене модели или крупном изменении промпта.
  3. Настроить дашборд (если есть Grafana / Prometheus) или хотя бы простой лог-файл с историей прогонов.

Ожидаемый результат этапа Полная документация и повторяемый процесс ведения golden set.

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

  • Golden set содержит ровно 100 пар «запрос → эталонный ответ» в формате JSON.
  • Все тесты запускаются одной командой pytest tests/ (локально) и проходят при неизменённых промптах.
  • В репозитории корректно настроен CI (GitHub Actions или аналог), который запускает тесты на каждый push в любую ветку.
  • При изменении промпта (например, добавление одного слова в системную инструкцию) CI падает с четким указанием, какой тест не пройден (номер запроса, ожидаемый и фактический ответ).
  • При удалении или изменении golden set CI также падает (если нарушен формат или количество записей).
  • Процесс обновления golden set описан в README и воспроизводится по шагам.
  • Тесты используют фиксированные параметры генерации (temperature=0, seed=42) — воспроизводимость.
  • Длительность выполнения тестов в CI не превышает 10 минут (с учётом вызовов LLM). При использовании mock — менее 30 секунд.

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

Главный артефакт Репозиторий (или выделенная директория в проекте) со следующей структурой:

  • golden_set.json — 100 пар запрос-ответ.
  • tests/test_regression.py — параметризованный тест.
  • tests/conftest.py — фикстуры.
  • .github/workflows/regression.yml — CI-пайплайн.
  • requirements.txt — зависимости (pytest, openai и т.д.).
  • README.md — документация по ведению golden set и интерпретации результатов.

Дополнительно (опционально):

  • Скрипт scripts/update_golden_set.py для автоматической перегенерации эталонов.
  • Интеграция с системой оповещений (Slack, email) при падении регрессии.
  • JUnit-отчёт, загружаемый в артефакты CI.

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

СложностьРешение
Недетерминированность LLM — даже при temperature=0 и seed=42 ответы могут различаться на разных версиях модели или платформы.Использовать точную фиксацию версии модели (например, gpt-4-0613) и кеширование эмбеддингов. Вместо exact match можно использовать семантическое сходство (rouge-L > 0.95) с порогом, но цель задачи — 100% совпадение, поэтому лучше изначально генерировать golden set на той же модели и с теми же параметрами. Если нестабильность сохраняется — использовать mock-объект, который возвращает фиксированный ответ.
Объём golden set — 100 пар много ручной работы.Использовать синтетические запросы (генерация из ключевых слов) и проверять только 15–20% вручную. Остальные считать правильными, если они сгенерированы текущей стабильной версией.
Частое изменение промптов — каждый PR может требовать обновления golden set, что замедляет разработку.Разделить золотой сет на две части: критический (10–20 запросов, которые обязаны совпадать) и опорный (80 запросов, которые разрешено менять при утверждении). CI настроить: критический — всегда exact match, опорный — допускает изменение, но уведомляет.
Зависимость от внешнего API — тесты могут падать из-за таймаутов или ошибок сети.Добавить retry-механизм в фикстуру (максимум 3 попытки с паузой). Если API недоступен — тест не запускается (пропускается), но CI завершается успехом. Альтернатива — использовать локальную open-source модель (например, Llama.cpp) для полной детерминированности.
Забыли обновить golden set после изменения логики приложения — тесты проходят, но ответы перестали быть актуальными.Ввести триггер: при изменении любого файла с промптами (.prompt, .jinja) или при изменении prompt_engine.py автоматически создавать issue/задачу на обновление golden set. Можно также добавить pre-commit hook, который напоминает.

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

ЭтапВремя (часы)
1. Формирование golden set2–3
2. Разработка тестового фреймворка2–3
3. Интеграция в CI1–2
4. Документирование и мониторинг1
Итого6–9

Примечание для первого раза Если golden set создаётся вручную, этап 1 может занять до 5 часов. Использование автоматической генерации (LLM на тестовых запросах) сокращает время до 30–60 минут.

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

ВопросТема
15Основы тестирования в AI/ML
21Настройка CI/CD для ML-проектов
45Метрики качества ответов LLM (ROUGE, BLEU)
67Работа с seed и воспроизводимостью генерации
89Интеграционное тестирование LLM-приложений
101Управление промптами в Git
155Паттерн golden set для ML
203Параметризованные тесты в pytest
256Асинхронные вызовы LLM в тестах
310Оповещения о сбоях в CI

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

  • Я проверил, что golden_set.json содержит 100 записей с корректными полями и валидным JSON.
  • Я запустил pytest tests/ локально — все тесты зелёные (пройдены).
  • Я изменил один из промптов (например, убрал точку в конце системного сообщения), закоммитил и убедился, что CI упал с сообщением о расхождении на конкретном запросе.
  • Я снова вернул промпт и убедился, что CI зелёный.
  • Я описал в README процедуру обновления golden set и ссылку на неё внёс в описание репозитория.
  • Я проверил, что файл requirements.txt содержит все необходимые библиотеки с фиксированными версиями.
  • Я убедился, что секреты (API key) правильно настроены в GitHub Actions.
  • Я посмотрел лог первого успешного прогона в CI — все тесты выполнены за разумное время (<5 мин).