응답 품질 회귀 테스트 하네스 만들기
목표
평가 하네스를 처음부터 만들어 품질·지연을 함께 재고, 기준선 대비 회귀를 자동으로 판정하는 CI 게이트까지 완성한다.
왜 중요한가
평가 하네스 없이 모델이나 프롬프트를 바꾸는 것은 테스트 없이 리팩터링하는 것과 같습니다. 몇 개 물어보고 "좋아진 것 같다"로 결정한 뒤, 2주 지나 특정 유형에서 품질이 떨어졌다는 문의를 받고, 되돌릴지 판단할 근거가 없는 상황이 반복됩니다. LLM 출력이 결정적이지 않아 완벽한 평가는 불가능하지만, 회귀를 잡을 만큼은 충분히 잴 수 있습니다. 이 실습에서 특히 중요한 것이 5번 스텝입니다 — 품질만 재면 절반입니다. 품질이 2퍼센트포인트 좋아졌는데 지연이 두 배가 됐다면 그것은 개선이 아닙니다. 그리고 7번 스텝의 CI 게이트가 이 모든 것을 실제로 작동하게 만듭니다. 사람이 기억해서 돌리는 평가는 결국 안 돌아갑니다.
단계
/opt/fixtures/llms/evalset.jsonl(40건, 각 줄에id,prompt,expected,grader)를 읽어/root/ev/loaded.txt에cases=40 graders=<쉼표로 이은 종류>를 적는다./root/ev/run.py로 전 케이스를 8170 서버에 실행해/root/ev/results.jsonl을 만든다. 각 줄에id,output,latency_ms가 있어야 하고 40줄이어야 한다.grader가exact인 케이스를 정확 일치로 채점한다./root/ev/exact.txt에cases=<n> passed=<n> score=<0~1 소수>를 적는다.grader가contains인 케이스를 키워드 포함 비율로 채점한다./root/ev/contains.txt에 같은 형식으로 적고score는 0 이상 1 이하여야 한다.- 지연 SLO 를 800ms 로 두고 위반 건수를 센다.
/root/ev/latency.txt에slo_ms=800 violations=<n> p99_ms=<수>를 적는다. - 전체 점수를
/root/ev/baseline.json에{"score":<수>,"p99_ms":<수>,"cases":40}로 저장한다. 그다음/root/ev/compare.py로 새 실행 결과를 기준선과 비교해/root/ev/regression.txt에baseline=<수> current=<수> delta=<부호 있는 수> verdict=<PASS|FAIL>을 적는다. 임계는 -0.03 이다. /root/ev/gate.sh는 회귀면 종료 코드 1, 아니면 0 으로 끝난다. 두 경우를 모두 시험해/root/ev/gate.out에pass_exit=0 fail_exit=1을 적는다.
참고
- 127.0.0.1:8170 의 백엔드는 이 파드에 미리 떠 있지 않습니다.
POST /generate에{"prompt":..., "max_tokens":n}을 받아{"text":..., "usage":{...}}를 돌려주면 되므로, 앞선 토큰 스트리밍 실습에서 만든 서버를 그대로 쓰거나 같은 계약의 최소 서버를 직접 띄우고 시작하세요. 회귀를 만들어 보려면 품질을 바깥에서 바꿀 수 있는 손잡이를 하나 두는 편이 편합니다. - 평가셋은 실제 트래픽 분포를 반영해야 합니다. 잘 되는 케이스만 모으면 회귀를 못 잡습니다.
- 임계는 노이즈보다 커야 합니다. 3퍼센트포인트가 출발점이고 평가셋이 작으면 더 커야 합니다.
- 사용자 문의로 발견된 실패 케이스를 평가셋에 추가하는 루프를 만들면 같은 실패가 두 번 나지 않습니다.
- 흔한 실수 1: 품질만 재고 지연과 비용을 빼먹는 것.
- 흔한 실수 2: 하네스를 만들어 두고 CI 에 넣지 않는 것 — 사람이 기억해서 돌리는 평가는 결국 안 돌아갑니다.
평가셋 불러오기
/opt/fixtures/llms/evalset.jsonl(40건, 각 줄에 id, prompt, expected, grader)를 읽어 /root/ev/loaded.txt 에 cases=40 graders=<쉼표로 이은 종류> 를 적는다.
JSONL 한 줄이 한 케이스입니다. 케이스마다 채점 방식이 다를 수 있습니다.
러너로 전 케이스 실행하기
/root/ev/run.py 로 전 케이스를 8170 서버에 실행해 /root/ev/results.jsonl 을 만든다. 각 줄에 id, output, latency_ms 가 있어야 하고 40줄이어야 한다.
결과를 원본과 함께 남겨야 나중에 무엇이 틀렸는지 볼 수 있습니다.
정확 일치 채점하기
grader 가 exact 인 케이스를 정확 일치로 채점한다. /root/ev/exact.txt 에 cases=<n> passed=<n> score=<0~1 소수> 를 적는다.
구조화된 출력에 맞는 방식입니다. 공백과 대소문자 정규화를 먼저 하세요.
부분 점수 채점하기
grader 가 contains 인 케이스를 키워드 포함 비율로 채점한다. /root/ev/contains.txt 에 같은 형식으로 적고 score 는 0 이상 1 이하여야 한다.
자유 서술은 키워드 포함 비율로 점수를 냅니다. 0과 1 사이 값이 나와야 합니다.
지연 SLO 위반 세기
지연 SLO 를 800ms 로 두고 위반 건수를 센다. /root/ev/latency.txt 에 slo_ms=800 violations=<n> p99_ms=<수> 를 적는다.
품질만 재면 절반입니다. 느려진 것도 회귀입니다.
기준선 저장하고 회귀 판정하기
전체 점수를 /root/ev/baseline.json 에 {"score":<수>,"p99_ms":<수>,"cases":40} 로 저장한다. 그다음 /root/ev/compare.py 로 새 실행 결과를 기준선과 비교해 /root/ev/regression.txt 에 baseline=<수> current=<수> delta=<부호 있는 수> verdict=<PASS|FAIL> 을 적는다. 임계는 -0.03 이다.
비교 대상이 없으면 판정할 수 없습니다. 임계는 노이즈보다 커야 합니다.
CI 게이트 스크립트 만들기
/root/ev/gate.sh 는 회귀면 종료 코드 1, 아니면 0 으로 끝난다. 두 경우를 모두 시험해 /root/ev/gate.out 에 pass_exit=0 fail_exit=1 을 적는다.
사람이 기억하지 않아도 되게 만드는 것이 목표입니다. 종료 코드로 통과 여부를 알리세요.