Part 2. 품질은 Prompt가 아니라 평가 루프에서 나온다
LLM 품질은 문장 튜닝이 아니라 데이터셋, 평가 기준, 온라인 피드백, 회귀 감지 루프로 관리해야 안정화된다.
데모 환경에서는 "프롬프트를 조금 바꾸니 답변이 좋아졌다"는 경험이 자주 나온다. 그러나 서비스 운영에서는 이 경험이 오래가지 않는다. 트래픽 유형이 바뀌고, 데이터가 변하고, 정책 요구사항이 추가되면 같은 프롬프트가 서로 다른 결과를 만든다. 그래서 품질을 프롬프트 문장으로 관리하면 결국 품질이 아니라 운을 관리하게 된다.
문제 제기
운영 환경에서 LLM 품질 문제가 장기화되는 이유는 대부분 "평가 체계 부재"다. 자주 발생하는 패턴은 다음과 같다.
- 오프라인 샘플에서는 정확도가 높았지만 실제 사용자 요청에서는 회피형 답변이 늘어난다.
- 안전성(safety)을 강화했더니 유용성(usefulness)이 급락한다.
- 모델 비용을 줄이기 위해 라우팅 정책을 바꿨더니 특정 도메인 질문에서 실패율이 급증한다.
- 운영팀은 "요즘 답변이 별로"라는 정성 피드백만 있고, 무엇이 얼마나 나빠졌는지 수치로 설명하지 못한다.
실전 예시 A: 사내 지식검색 챗봇
사내 문서 검색 챗봇의 오프라인 평가 정확도는 86%였다. 하지만 실제 운영에서 "정책 문서 인용이 틀렸다"는 티켓이 급증했다. 원인은 모델 문제가 아니라 검색 인덱스 최신성 지연이었다. 평가셋이 최신성 시나리오를 포함하지 않아 배포 전에는 발견되지 않았다.
실전 예시 B: 결제 도메인 고객지원 에이전트
"환불 가능 여부" 답변 품질을 올리기 위해 프롬프트를 상세화했더니 평균 응답 길이가 2배로 증가했다. 그 결과 모바일 사용자에서 이탈률이 상승했고, CS 만족도는 오히려 하락했다. 정확도 개선이 UX 품질 하락을 덮지 못한 사례다.
핵심 개념
LLM 품질은 단일 점수가 아니라 다차원 품질 벡터로 봐야 한다.
| 축 | 핵심 질문 | 대표 지표 | 실패 신호 |
|---|---|---|---|
| 정답성(Accuracy) | 사실/정책에 맞는가 | task pass rate, citation precision | 헛답변, 근거 불일치 |
| 유용성(Usefulness) | 사용자가 실제로 해결되었는가 | resolution rate, follow-up ratio | 재질문 증가 |
| 안전성(Safety) | 금지 정책 위반이 없는가 | policy violation rate | 민감정보 노출 |
| 일관성(Consistency) | 같은 입력에 결과 편차가 큰가 | variance score | 재현 불가 이슈 |
| 효율성(Efficiency) | 품질 대비 시간/비용은 적정한가 | p95 latency, cost/request | SLA/예산 초과 |
중요한 점은 축 간 trade-off다.
- 안전성 임계치를 높이면 정답 거부(false refusal)가 증가할 수 있다.
- 비용 최적화를 위해 작은 모델 비중을 늘리면 난이도 높은 질의에서 정확도가 하락할 수 있다.
- 응답 길이를 줄이면 지연은 개선되지만 설명 충분성이 떨어질 수 있다.
따라서 품질 관리는 "모델 점수 최적화"가 아니라 "목표 함수 설계"다.
실전 패턴
패턴 1: 평가셋을 "정답 모음"이 아니라 "운영 리스크 모음"으로 구성
운영 가능한 평가셋은 최소 3개 계층으로 분리하는 것이 좋다.
- Golden Set: 핵심 유스케이스의 기준 문제
- Adversarial Set: 정책 위반, 애매한 질문, jailbreak 시도
- Drift Set: 최근 7~30일 실트래픽 샘플
type EvalCase = {
id: string;
tier: "golden" | "adversarial" | "drift";
input: string;
expected: {
mustInclude?: string[];
mustNotInclude?: string[];
policyTags?: string[];
};
};
type EvalResult = {
caseId: string;
pass: boolean;
score: number;
reasons: string[];
};
export async function runEval(cases: EvalCase[]): Promise<EvalResult[]> {
const results: EvalResult[] = [];
for (const c of cases) {
const output = await infer(c.input);
const passInclude = (c.expected.mustInclude ?? []).every((k) => output.includes(k));
const passExclude = (c.expected.mustNotInclude ?? []).every((k) => !output.includes(k));
const pass = passInclude && passExclude;
results.push({
caseId: c.id,
pass,
score: pass ? 1 : 0,
reasons: pass ? [] : ["constraint mismatch"],
});
}
return results;
}
운영 포인트:
- Golden Set만 좋아지는 배포는 금지하고, Adversarial 최소 점수 하한을 강제한다.
- Drift Set은 주기적으로 자동 갱신하되 개인정보 마스킹을 선행한다.
- 제품/정책 변경 시 평가셋 버전도 함께 올린다.
패턴 2: 온라인 품질 신호를 "사건(event)"으로 수집
오프라인 점수만으로는 실제 품질을 설명할 수 없다. 온라인에서는 사용자 행동과 시스템 실패를 결합해 품질 이벤트를 기록해야 한다.
{
"event": "llm_response_completed",
"request_id": "req_20260303_102310",
"prompt_version": "support-v4.2.1",
"model_route": "small->large-fallback",
"latency_ms": 812,
"cost_usd": 0.0048,
"policy_violation": false,
"user_follow_up_within_120s": true,
"thumb_down": false,
"resolved": true
}
# 품질 회귀 감지 예시: 15분 윈도우에서 후속 질문 비율 급등 탐지
./alerts/eval-guard.sh \
--metric user_follow_up_ratio \
--window 15m \
--baseline 7d \
--threshold +18%
운영 포인트:
- "사용자 재질문"은 낮은 품질의 강한 시그널이므로 latency, 정책 위반과 같이 본다.
- 모델 변경뿐 아니라 검색 인덱스/정책 엔진 변경도 동일 대시보드에 표시한다.
- 품질 경보는 배포 경보와 연결해 자동 트래픽 축소를 허용한다.
실패 사례/안티패턴
장애 시나리오: "오프라인 정확도 상승, 운영 불만 폭증"
상황:
- 새 프롬프트 + 모델 라우팅 적용 후 Golden Set 점수는 78%에서 90%로 상승했다.
- 배포 2시간 후
user_follow_up_ratio가 12%에서 31%로 증가했다. - 원인 분석 결과, 거절 정책이 과도하게 적용되어 정상 질문까지 "도움 불가" 응답이 증가했다.
탐지 절차:
- 온라인 지표 경보:
follow_up_ratio,resolved_rate동시 악화 - Trace 샘플링: 정책 엔진
deny_reason=ambiguous비율 급증 확인 - 버전 비교: 프롬프트 v4.2.1 + policy rule-set v19 동시 배포 확인
완화 절차:
- 정책 rule-set만 즉시 이전 버전(v18)으로 롤백
- 영향 도메인(결제/환불) 트래픽을 human-review 경로로 임시 우회
- 자동 응답 길이 제한을 완화해 설명 부족 문제 감소
회복 절차:
- Adversarial Set에 "정상 질문 오탐" 케이스 추가
- 정책 분류기 임계치 AB 테스트로 재조정
- "거절률 상한"을 배포 게이트의 필수 조건으로 추가
대표 안티패턴
- 오프라인 점수 1개로 배포 결정을 내리는 방식
- 품질 이슈를 프롬프트 담당자 개인 역량 문제로 환원하는 문화
- 사용자 피드백(thumb down, 재질문)을 제품 지표로만 보고 운영 지표에서 제외
- 평가셋을 고정해 데이터 드리프트를 무시하는 운영
체크리스트
- 품질 지표를 정확도/유용성/안전성/효율성으로 분해해 관리하는가?
- 배포 게이트에 Golden + Adversarial + Drift 세트가 모두 포함되는가?
- 온라인 지표(재질문율, 해결률, 정책 위반률)가 프롬프트 버전과 연결되는가?
- 품질 경보 발생 시 롤백 또는 라우팅 전환 runbook이 준비되어 있는가?
- 프롬프트 변경과 정책 변경의 동시 배포를 통제하는가?
- 주 단위로 실패 샘플을 수집해 평가셋으로 재편입하는가?
- 비용/지연 개선 작업이 품질 회귀를 동반하지 않는지 자동 검증하는가?
요약
프롬프트 튜닝은 품질 개선의 출발점일 수 있지만 종착점이 될 수 없다. 운영 품질은 평가셋 설계, 온라인 피드백 수집, 회귀 탐지, 배포 게이트로 구성된 루프에서 나온다. "좋아 보인다"는 느낌 대신 "왜 좋아졌는지, 언제 나빠지는지"를 설명할 수 있어야 품질이 시스템이 된다.
다음 편 예고
다음 편은 신뢰성(Reliability)을 다룬다. LLM 시스템에서 재시도, 타임아웃, 폴백, 서킷브레이커를 일반 API와 다르게 설계해야 하는 이유를 설명한다. 특히 "재시도가 품질을 올리는 경우"와 "재시도가 장애를 증폭하는 경우"를 구분하는 기준을 실제 운영 시나리오로 정리한다.
이전 편: Part 1. Prompt는 인터페이스다: 시스템 경계와 계약으로 다시 보기
다음 편: Part 3. 신뢰성 설계: Retry, Timeout, Fallback, Circuit Breaker