CI/CD에서의 단위 테스트

CI/CD에서의 단위 테스트 (Unit-Testing in CI/CD)

CI/CD 파이프라인에서 LLM 앱에 자동화된 배포 전 워크플로우를 설정해볼게요. Python 사용자는 deepeval의 pytest 네이티브 통합을 활용해 CI/CD 파이프라인에서 LLM 앱에 단위 테스트를 실행할 수 있어요. 현재 CI/CD에서는 종단 간 테스트만 지원되며 평가는 반드시 로컬에서 실행해야 해요.

출처: 문서

본문

개요

특히 Python 사용자는 deepeval의 pytest 네이티브 통합을 활용해 CI/CD 파이프라인에서 LLM 앱에 단위 테스트를 실행할 수 있어요.

현재 CI/CD에서는 종단 간 테스트만 지원돼요. 평가는 반드시 로컬에서 실행해야 해요.

CI 환경 설정

테스트 파일 만들기

test_[name].py 파일을 만들고 다음 코드를 붙여넣어요:

싱글턴 E2E

import pytest
from deepeval.test_case import LLMTestCase
from deepeval.dataset import EvaluationDataset
from deepeval.metrics import AnswerRelevancyMetric
from deepeval import assert_test

dataset = EvaluationDataset()
dataset.pull(alias="YOUR-DATASET-ALIAS")

for golden in dataset.goldens:
    test_case = LLMTestCase(input=golden.input, actual_output=llm_app(input))
    dataset.add_test_case(test_case)

# Loop through test cases using pytest
@pytest.mark.parametrize("test_case", dataset.test_cases)
def test_llm_app(test_case: LLMTestCase):
    assert_test(test_case, metrics=[AnswerRelevancyMetric()]) # Replace with your metrics

아직이라면 싱글턴 종단 간 평가를 로컬에서 실행하는 방법을 여기에서 배울 수 있어요.

멀티턴 E2E

from deepeval.test_case import ConversationalTestCase
from deepeval.simulator import ConversationSimulator
from deepeval.dataset import EvaluationDataset
from deepeval.metrics import TurnRelevancyMetric
from deepeval import assert_test

dataset = EvaluationDataset()
dataset.pull(alias="YOUR-DATASET-ALIAS")

simulator = ConversationSimulator(model_callback=chatbot_callback)
for golden in dataset.goldens:
    test_case = simulator.simulator(golden)
    dataset.add_test_case(test_case)

# Loop through test cases using pytest
@pytest.mark.parametrize("test_case", dataset.test_cases)
def test_llm_app(test_case: ConversationalTestCase):
    assert_test(test_case, metrics=[TurnRelevancyMetric()]) # Replace with your metrics

아직이라면 멀티턴 종단 간 평가를 로컬에서 실행하는 방법을 여기에서 배울 수 있어요.

만든 테스트 파일에는 최소한 하나의 테스트 함수(test_로 시작하고 assert_test()를 호출하는 함수)가 있어야 해요. 이전 섹션에서 배운 것처럼 evaluate()를 호출하면 안 돼요 — 이는 pytest 통합 스위트에 속하지 않기 때문이에요.

모든 것이 잘 동작하는지 확인하려면 터미널에서 deepeval test run을 실행해 테스트 파일을 트리거해요:

deepeval test run test_llm_app.py

완료 ✅. deepeval test run 명령은 pytest와 네이티브로 통합되며 테스트 런을 하나만 만들어요.

.yml 파일 설정

CI/CD 파이프라인에서 테스트 파일을 자동으로 실행할 YAML 파일을 만들어요. 다음 예시는 설치에 poetry를, 로컬 평가의 LLM 심사로 OPENAI_API_KEY를, 결과 전송에 CONFIDENT_API_KEY를 사용해요:

name: Unit-Testing LLM App

on:
  push:
  pull_request:

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - name: Check out repository
        uses: actions/checkout@v3
      - name: Set up python
        id: setup-python
        uses: actions/setup-python@v4
        with:
          python-version: "3.11"

      - name: Install Poetry
        uses: snok/install-poetry@v1
        with:
          virtualenvs-create: true
          virtualenvs-in-project: true
          installer-parallel: true

      - name: Load cached venv
        id: cached-poetry-dependencies
        uses: actions/cache@v3
        with:
          path: .venv
          key: venv-${{ runner.os }}-${{ steps.setup-python.outputs.python-version }}-${{ hashFiles('**/poetry.lock') }}

      - name: Install dependencies
        if: steps.cached-poetry-dependencies.outputs.cache-hit != 'true'
        run: poetry install --no-interaction --no-root --only main

      - name: Install project
        run: poetry install --no-interaction --only main

      - name: Run tests
        env:
          OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
          CONFIDENT_API_KEY: ${{ secrets.CONFIDENT_API_KEY }}
        run: |
          poetry run pytest tests/test_core/ --ignore=tests/test_core/test_synthesizer/

CONFIDENT_API_KEY를 반드시 제공하세요. 그렇지 않으면 데이터셋에 접근할 수 없고, 평가 완료 시 Confident AI에 테스트 런을 만들 수 없어요.

GitHub 워크플로우에 포함

마지막 단계는 모든 것을 자동화하는 거예요:

  1. 아직 없다면 저장소에 .github/workflows 디렉터리를 만드세요
  2. 이 디렉터리에 unit-testing.yml 파일을 배치하세요
  3. GitHub 저장소에 Confident AI API 키를 secret으로 설정하세요

이제 커밋하고 변경 사항을 푸시할 때마다, GitHub Actions가 지정된 트리거에 따라 테스트를 자동으로 실행해요.

프롬프트와 모델 기록 (Log Prompts and Models)

evaluate()로 프롬프트, 모델, 다른 파라미터를 기록할 수 있는 것처럼, 테스트 파일에서도 동일하게 할 수 있어요:

import pytest
from deepeval.test_case import LLMTestCase
from deepeval.dataset import EvaluationDataset
from deepeval.metrics import AnswerRelevancyMetric
from deepeval import assert_test
from typing import Union
import deepeval

dataset = EvaluationDataset()
dataset.pull(alias="YOUR-DATASET-ALIAS")

for golden in dataset.goldens:
    test_case = LLMTestCase(input=golden.input, actual_output=llm_app(input))
    dataset.add_test_case(test_case)

# Loop through test cases using pytest
@pytest.mark.parametrize("test_case", dataset.test_cases)
def test_llm_app(test_case: LLMTestCase):
    assert_test(test_case, metrics=[AnswerRelevancyMetric()]) # Replace with your metrics

# Log configs used in LLM app at this point in time
@deepeval.log_hyperparameters()
def hyperparameters() -> dict[str, Union[str, int, float]]:
    # Return an empty Dict if there's nothing to log
    return {
        "Model": "gpt-4o",
        "Temperature": 1,
        "Chunk Size": 500
    }

deepeval test run을 실행하면 Confident AI가 하이퍼파라미터를 만든 테스트 런과 자동으로 연결해요.

플래그 구성 (Flag Configs)

deepeval test run은 pytest를 쓰듯이 단위 테스트를 실행하게 해주는 강력한 명령이에요. 병렬 프로세스 수, 오류 처리 등을 개선하는 수십 개의 플래그가 deepeval test run을 커스터마이즈할 수 있게 해줘요.

병렬화 (Parallelization)

-n 플래그에 숫자를 넣어 몇 개의 프로세스를 사용할지 지정해요.

deepeval test run test_example.py -n 4

이 경우 -n 4는 deepeval이 4개의 프로세스를 띄워 한 번에 4개의 테스트 케이스를 평가한다는 뜻이에요.

캐시 (Cache)

-c 플래그(인자 없이)를 제공하면 같은 메트릭에 대해 테스트 케이스를 다시 평가하는 대신 로컬 deepeval 캐시에서 읽어요.

deepeval test run test_example.py -c

이것은 많은 양의 테스트 케이스를 실행할 때 매우 유용해요. 예를 들어 deepeval test run으로 1000개의 테스트 케이스를 실행하는데 999번째에서 오류가 났다고 해볼게요. 캐시 기능 덕분에 이미 평가된 999개의 테스트 케이스를 건너뛰고 나머지 하나만 평가하면 돼요.

오류 무시 (Ignore Errors)

-i 플래그(인자 없이)는 테스트 런 중 메트릭 실행 오류를 무시하게 해줘요.

deepeval test run test_example.py -i

-i, -c, -n 플래그를 조합해, 캐시되지 않은 테스트 케이스를 오류를 무시하면서 병렬로 실행할 수 있어요:

deepeval test run test_example.py -i -c -n 2

상세 모드 (Verbose Mode)

-v 플래그(인자 없이)는 deepeval test run으로 실행하는 모든 메트릭의 verbose_mode를 켜요. -v 플래그를 주지 않으면 각 메트릭의 verbose_mode는 인스턴스화 시점의 값으로 기본 설정돼요.

deepeval test run test_example.py -v

메트릭의 verbose_mode가 True일 때, 평가 중 해당 메트릭을 계산하는 데 사용된 중간 단계를 콘솔에 출력해요.

테스트 케이스 건너뛰기 (Skip Test Cases)

-s 플래그(인자 없이)는 평가에 필요한 파라미터(예: retrieval_context)가 누락되었거나 불충분한 테스트 케이스의 메트릭 실행을 건너뛰게 해줘요. 유용한 예시는 ContextualPrecisionMetric 같은 메트릭을 쓰는데 retrieval_context가 None일 때 적용하고 싶지 않을 때예요.

deepeval test run test_example.py -s

식별자 (Identifier)

-id 플래그 뒤에 문자열을 넣어 테스트 런에 이름을 붙이고, 테스팅 리포트와 회귀 테스트에서 더 잘 식별하게 해줘요.

deepeval test run test_example.py -id "My Latest Test Run"

반복 (Repeats)

-r 플래그에 숫자를 넣어 각 테스트 케이스를 몇 번 다시 실행할지 지정해요.

deepeval test run test_example.py -r 2

더 알아보기