본문 바로가기
카테고리 없음

promptfoo로 프롬프트 바꿀 때마다 회귀 테스트 돌리는 방법

by Juelria 2026. 10. 15.
728x90
반응형
SMALL

프롬프트 문장을 몇 글자 다듬었을 뿐인데 이전까지 잘 나오던 JSON 응답의 특정 키가 사라지거나 예외 처리가 무너지는 일이 흔합니다. promptfoo는 YAML 설정 파일 하나에 비교할 프롬프트, 호출할 언어 모델, 테스트 입력값과 단언문(assert)을 묶어 두고 명령줄에서 검증하는 평가 도구입니다. 프롬프트 수정 사항이 생길 때마다 모델 창에 일일이 붙여 넣어 보지 않고도 이전 테스트 케이스들이 그대로 통과하는지 빠르게 확인할 수 있습니다.

설정 파일 하나에 프롬프트와 모델과 검증식을 모읍니다

작업 폴더에서 npx promptfoo init 명령을 실행하면 기본 틀인 promptfooconfig.yaml 파일이 만들어집니다. 이 파일은 크게 prompts, providers, tests 세 덩어리로 나뉩니다.

prompts 항목에는 모델에 전달할 프롬프트 템플릿을 적습니다. 파일 경로를 연결해도 되고, 문자열 안에 중괄호 둘({{topic}})로 변수 자리를 만들어 인라인으로 써도 됩니다. 프롬프트를 v1과 v2 두 개 적어 두면 같은 입력값에 대해 두 프롬프트 결과를 나란히 대조할 수 있습니다.

providers 항목에는 평가 대상 모델을 지정합니다. openai:gpt-4o나 anthropic:messages:claude-3-5-haiku-20241022 같은 식별자를 적습니다. 환경 변수에 API 키가 있으면 별도 코드 없이 호출됩니다. 로컬 Ollama 모델이나 사내 전용 엔드포인트도 등록할 수 있어서, 가벼운 모델로 바꿨을 때 품질이 얼마나 달라지는지 측정할 때도 유용합니다.

tests 항목은 실제 검증 데이터셋입니다. vars 아래에 변수 값을 넣고, assert 아래에 검증 규칙을 추가합니다.

prompts:

  • ""다음 주제를 한 문장으로 요약하세요: {{topic}}""
  • ""핵심만 단문으로 정리해 주세요: {{topic}}""

providers:

  • openai:gpt-4o-mini

tests:

  • vars:
    topic: ""웹 프론트엔드 캐싱 전략""
    assert:
    • type: contains
      value: ""캐시""
    • type: latency
      threshold: 2500

단언문 assert로 출력 형식과 필수 단어를 검사합니다

단순히 모델이 에러 없이 응답을 돌려주었는지만 확인해서는 회귀 테스트가 성립하지 않습니다. promptfoo는 여러 형태의 내장 단언문을 제공합니다.

자주 쓰는 규칙은 contains와 not-contains입니다. 모델이 반드시 포함해야 할 안내 문구가 있는지, 반대로 환각으로 섞여 나오는 금지 단어가 빠졌는지 검사합니다. 대소문자를 가리지 않고 보려면 icontains를 씁니다.

JSON 출력을 요구하는 프롬프트라면 is-json 타입을 지정합니다. 응답 문자열이 유효한 JSON인지 검사할 뿐 아니라, jsonpath 표현식을 써서 status가 success인지, 특정 배열의 길이가 1 이상인지까지 함께 확인합니다.

type: is-json
value:

  • jsonpath: ""$.status""
    operator: ""equals""
    value: ""success""

문자열 일치만으로 평가하기 어려운 서술형 답변에는 llm-rubric을 씁니다. 판정용 모델에게 채점 기준표를 주고 답변이 기준에 맞는지 채점하게 만드는 방식입니다. latency와 cost 단언문을 걸어 두면 프롬프트가 길어져서 토큰 비용이 예산을 넘기거나 응답 속도가 3초를 초과하는 지연도 걸러낼 수 있습니다.

터미널에서 평가를 돌리고 웹 화면으로 결과를 대조합니다

설정 작성을 마친 뒤 터미널에서 npx promptfoo eval 명령을 내리면 평가가 시작됩니다.

각 프롬프트와 프로바이더의 조합마다 테스트 케이스가 병렬로 실행됩니다. 실행이 끝나면 터미널 창에 표 형태로 성공과 실패 여부, 걸린 시간, 토큰 소모량이 출력됩니다. 단언문 조건을 만족하지 못한 셀은 빨간색으로 표시되고 사유가 찍힙니다.

결과를 시각적으로 대조하고 싶다면 npx promptfoo view 명령을 입력합니다. 로컬 웹 서버가 뜨면서 브라우저에 행과 열로 정돈된 매트릭스 뷰어가 열립니다. 왼쪽 열에는 테스트 입력값, 오른쪽 열에는 각 프롬프트와 모델 조합의 실제 답변 텍스트가 표시됩니다. 글자 단위 diff 기능을 켜면 v1과 v2 사이에 어떤 문장이 달라졌는지 한눈에 비교할 수 있습니다.

기본적으로 이전 실행 결과는 로컬 캐시에 저장되므로, 같은 입력과 프롬프트는 재호출하지 않고 빠르게 통과합니다. 응답 변동성을 다시 측정하고 싶다면 --no-cache 옵션을 붙여 캐시 조회를 건너뜁니다.

깃허브 액션과 연결해 풀 리퀘스트를 검증합니다

promptfoo의 장점은 CI 파이프라인 안에서 일반 단위 테스트처럼 실행할 수 있다는 점입니다. 프롬프트 파일을 수정해 올릴 때마다 자동으로 eval을 돌리면 기준 미달 변경 사항이 배포 브랜치에 들어가는 일을 막을 수 있습니다.

GitHub Actions 워크플로에서는 node 환경을 세팅한 뒤 npx promptfoo eval --no-cache 명령을 실행하도록 구성합니다. 단언문이 하나라도 깨지면 명령이 0이 아닌 종료 코드를 반환하므로 잡이 실패 처리됩니다.

name: Prompt Evaluation
on:
pull_request:
paths:
- 'prompts/*'
- 'promptfooconfig.yaml'

jobs:
evaluate:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20
- name: Run promptfoo evaluation
env:
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
run: npx promptfoo eval --no-cache

테스트 케이스는 5개 정도로 시작하고, 오답이 발견될 때마다 실패 케이스를 tests 배열에 누적해 나가는 방식이 효과적입니다. 이렇게 쌓인 테스트 세트는 이후 모델을 상위 버전으로 교체하거나 프롬프트를 압축할 때 든든한 기준점이 되어 줍니다.

참고한 자료

728x90
반응형
LIST