LLM 기반 기능은 한 가지 곤란한 성질을 가진다. 같은 입력에도 출력이 매번 달라지고, "좋아졌다"가 숫자로 잘 잡히지 않는다. 프롬프트 한 줄을 고치거나 모델 버전을 올렸을 때, 그게 정말 개선인지 아니면 어떤 케이스를 조용히 망가뜨린 퇴행인지 눈으로는 알 수 없다. 그래서 LLM 제품의 신뢰성은 모델 자체보다 평가 체계(evaluation) 에서 갈린...
LLM 기반 기능은 한 가지 곤란한 성질을 가진다. 같은 입력에도 출력이 매번 달라지고, "좋아졌다"가 숫자로 잘 잡히지 않는다. 프롬프트 한 줄을 고치거나 모델 버전을 올렸을 때, 그게 정말 개선인지 아니면 어떤 케이스를 조용히 망가뜨린 퇴행인지 눈으로는 알 수 없다.
그래서 LLM 제품의 신뢰성은 모델 자체보다 평가 체계(evaluation) 에서 갈린다. 평가가 없으면 모든 변경이 "느낌상 더 나아진 것 같다"로 머물고, 평가가 있으면 변경을 측정 가능한 의사결정으로 바꿀 수 있다.
이 문서는 운영 무용담이 아니라 평가를 어떻게 설계하는가의 관점에서 네 가지 축을 한 흐름으로 엮는다.
에이전트처럼 여러 단계를 밟는 워크플로의 궤적(trajectory) 평가와 위험 게이트는 별도 주제이므로, 그쪽은 Agentic Workflow 평가와 Risk Gate 설계로 넘긴다. 여기서는 단일 호출·단일 응답 수준의 평가 기반을 다룬다.
평가 프레임워크는 따로 노는 도구 묶음이 아니라 하나의 순환이다.
golden set (고정된 평가 데이터)
│
▼
runner (프롬프트/모델 버전마다 출력 생성)
│
▼
scorer (정확 매칭 + 메트릭 + LLM-as-a-judge)
│
▼
regression gate (이전 버전 점수와 비교 → 통과/차단)
│
▼
human review (낮은 점수·judge 불확실 구간만 샘플 검수)
│
└──→ 발견한 새 실패 케이스를 golden set에 환류핵심은 데이터(golden set)와 채점(scorer)을 분리하고, 변경 판단(regression gate)을 자동화하며, 사람(human)을 전수가 아니라 선택 지점에만 넣는다는 점이다. 이 분리가 안 되면 평가가 매번 즉흥적인 수동 검수로 회귀한다.
골든셋은 "이 입력에는 이런 출력이 나와야 한다"를 고정해 둔 평가용 데이터셋이다. 학습 데이터와 다르다. 학습은 모델을 바꾸지만, 골든셋은 모델을 바꾸지 않고 판정만 한다.
좋은 골든셋의 조건은 크기보다 분포다.
각 케이스는 단순 입력/정답 쌍을 넘어, 무엇을 기준으로 통과로 볼지를 함께 적는다.
{
"id": "refund-policy-001",
"category": "policy_qa",
"input": "결제 후 3일 지났는데 환불 되나요?",
"reference": "기간 조건에 따라 다르며, 구체 조건은 정책 문서를 인용해 안내해야 함",
"rubric": [
"환불 가능 여부를 단정하지 않고 조건을 설명한다",
"근거가 되는 정책 출처를 인용한다",
"모르는 부분은 추측하지 않는다"
],
"must_not": ["무조건 환불 가능하다고 단정", "정책에 없는 기간 임의 생성"],
"difficulty": "boundary"
}흔한 실수:
회귀 테스트의 목적은 "프롬프트/모델/검색 파이프라인을 바꿨을 때, 기존에 잘 되던 케이스가 깨지지 않았는가"를 자동으로 확인하는 것이다. 일반 단위 테스트와 다른 점은 출력이 비결정적이라 정확 일치(exact match)만으로는 부족하다는 데 있다.
설계 포인트는 세 가지다.
CI에 붙일 때는 "이전 버전 대비 임계 이상 하락하면 차단"이 기본형이다.
# 예: PR마다 평가 실행 후 임계값 게이트
eval-gate:
run: python run_eval.py --golden golden_v3.json --out report.json
rules:
- metric: overall_pass_rate
min: 0.85 # 절대 하한
- metric: overall_pass_rate
max_drop_vs_main: 0.03 # main 대비 3%p 이상 하락 시 실패
- metric: policy_qa_pass_rate
max_drop_vs_main: 0.0 # 핵심 카테고리는 하락 불허스냅샷 비교(이전 출력과 텍스트 diff)도 보조로 쓸 수 있지만, 생성 과제에서는 표현만 바뀌어도 diff가 터지므로 메트릭 게이팅을 주로 두고 스냅샷은 참고용으로 둔다.
요약·설명·대화처럼 정답이 한 개로 안 떨어지는 과제는, 사람이 매번 채점하면 느리고 비싸다. 그래서 모델이 모델의 출력을 채점하는 LLM-as-a-judge를 쓴다. 다만 judge도 LLM이라 같은 약점을 공유한다는 점을 전제로 설계해야 한다.
채점 방식은 크게 둘이다.
judge가 가진 알려진 편향:
가장 중요한 원칙은 judge 자체를 검증해야 한다는 것이다. judge를 믿기 전에, 사람이 라벨링한 소규모 셋에서 judge 판정과 사람 판정의 일치도(agreement)를 측정한다. 일치도가 낮으면 점수가 아니라 rubric을 고친다.
judge 신뢰성 점검 절차
1. 사람이 라벨링한 케이스 50~100건 준비
2. 동일 케이스를 judge로 채점
3. 사람 vs judge 일치율 / 상관 측정
4. 불일치 케이스 원인 분석 → rubric 문장 구체화
5. 목표 일치율 도달 후에야 judge를 대규모로 사용rubric은 추상어("좋은 답인가")를 피하고, 통과 조건을 검증 가능한 문장으로 쪼갠다.
앞의 골든셋 예시에 넣은 rubric 배열이 그대로 judge 입력이 된다 — 골든셋과 judge가 같은 기준을 공유하게 설계하는 것이 핵심이다.
에이전트 궤적처럼 출력이 단일 응답이 아닌 경우의 judge 사용과 risk gate 결합은 Agentic Workflow 평가 문서에서 이어진다.
사람 검수는 가장 정확하지만 가장 비싸다. 그래서 설계의 핵심은 "사람을 전수 검수에 쓰지 않고, 자동 평가가 불확실한 지점에만 배치"하는 것이다.
사람을 끼우면 좋은 지점:
피드백을 받는 방식도 비용 차이가 크다.
가장 중요한 건 루프가 닫혀야 한다는 것이다. 사람이 찾은 실패가 골든셋의 새 케이스로 들어가고, 그 케이스가 다음 회귀 테스트에서 다시 검사되어야 같은 실패가 재발하지 않는다. 피드백을 모으기만 하고 골든셋에 환류하지 않으면, 평가는 점점 현실과 멀어진다.
나쁜 설계:
개선된 설계:
거창한 플랫폼 없이도 로컬에서 평가 골격을 만들 수 있다. JSON 골든셋 + 러너 + 간단한 채점으로 시작한 뒤 judge를 붙이는 순서가 좋다.
# 1. 골든셋과 러너만으로 시작
mkdir llm-eval && cd llm-eval
python -m venv .venv && source .venv/bin/activate
pip install pytest
# golden.json 작성 후
pytest -q test_eval.py가장 단순한 형태의 평가 러너는 다음과 같다.
실제 모델 호출부는 call_model로 추상화해 두고, 채점은 rubric 통과 비율을 집계한다.
import json
def call_model(prompt: str) -> str:
# 실제로는 LLM API 호출. 테스트에서는 stub로 대체.
...
def judge(output: str, rubric: list[str]) -> float:
# 실제로는 judge 모델 호출. 여기서는 rubric 통과 개수 비율을 반환한다고 가정.
...
def run_eval(golden_path: str) -> dict:
cases = json.load(open(golden_path, encoding="utf-8"))
by_category: dict[str, list[float]] = {}
for c in cases:
out = call_model(c["input"])
score = judge(out, c["rubric"])
by_category.setdefault(c["category"], []).append(score)
return {
cat: sum(scores) / len(scores)
for cat, scores in by_category.items()
}
if __name__ == "__main__":
report = run_eval("golden.json")
for cat, avg in report.items():
print(f"{cat}: {avg:.2f}")출력은 카테고리별 평균으로 떨어진다.
policy_qa: 0.88
refund: 0.79
small_talk: 0.95이 숫자를 이전 버전과 비교하는 한 줄을 CI에 추가하면, 그 순간부터 "느낌"이 아니라 "지표"로 변경을 판단하게 된다.
평가 설계 질문은 화려한 운영 규모가 아니라 사고의 분리를 본다. 다음 골격으로 답하면 운영 경험을 과장하지 않고도 설계 감각을 보여줄 수 있다.
면접에서 함정은 "정확도 몇 %를 봤냐"가 아니라 "그 정확도를 어떻게 신뢰하느냐"를 되물을 때다. 이때 judge 검증과 데이터 오염 방지를 언급하면 한 단계 위의 답이 된다.