logo
aillmevaluation

LLM 평가 - 답변 품질을 어떻게 측정할까

프롬프트를 고치고, RAG를 붙이고, 모델을 바꿉니다. 그런데 정말 나아진 걸까요? 일반 소프트웨어라면 테스트가 초록불이면 됩니다. 하지만 LLM은 같은 질문에 매번 다른 문장으로 답하고, 정답도 하나가 아닙니다.

이 글에서는 왜 LLM 평가가 어려운지, 그리고 실무에서 품질을 수치로 관리하는 방법을 정리합니다.


1. 왜 평가가 어려운가

전통적인 테스트는 expected === actual 로 끝납니다. LLM은 그게 안 됩니다.

  • 정답이 여럿이다. "환불 방법 알려줘"에 대한 좋은 답은 수십 가지 표현이 가능합니다. 문자열 일치로는 못 잡습니다.
  • 비결정적이다. temperature가 0이 아니면 같은 입력에도 출력이 흔들립니다.
  • 품질이 다차원이다. 정확성, 관련성, 완결성, 말투, 안전성, 형식 준수 — 하나만 봐선 안 됩니다.
  • "그럴듯함"이 함정. 문장은 매끄러운데 사실은 틀린(할루시네이션) 답을 사람이 대충 보면 통과시키기 쉽습니다.

비유하자면, 객관식 채점이 아니라 논술 채점입니다. 채점 기준(루브릭)을 먼저 정하지 않으면 평가 자체가 흔들립니다.


2. 먼저 오프라인 평가셋을 만든다

개선의 출발점은 항상 고정된 평가셋(evaluation set) 입니다. "질문 – 기대 답변/근거 – 채점 기준"을 모아 둔 데이터입니다.

[평가 항목 예시] question: "결제 취소는 며칠 안에 가능한가요?" reference: "결제 후 7일 이내" context: "환불 정책 문서 §3" criteria: "7일이라는 숫자를 정확히 포함할 것"
  • 실제 사용 로그에서 뽑는다. 상상한 질문이 아니라 진짜 들어온 질문이어야 의미가 있습니다.
  • 엣지 케이스를 일부러 넣는다. 답할 수 없는 질문, 애매한 질문, 민감한 질문을 섞어야 안전성까지 봅니다.
  • 작게 시작해도 된다. 50~100개만 잘 골라도 회귀를 잡는 데 충분합니다. 커버리지는 나중에 늘립니다.

이 평가셋이 곧 LLM판 테스트 스위트입니다.


3. 규칙 기반 평가

가능한 부분은 결정적 규칙으로 채점하는 게 가장 싸고 안정적입니다. LLM에 물어보기 전에 이걸로 걸러낼 수 있는 건 다 거릅니다.

def evaluate_rule_based(answer: str, item: dict) -> dict: return { # 반드시 포함해야 할 키워드 "has_keyword": item["reference"] in answer, # JSON 형식을 지켰는가 "valid_json": is_valid_json(answer) if item["expect_json"] else True, # 금칙어가 없는가 "no_forbidden": not any(w in answer for w in item["forbidden"]), # 길이 제한 "within_limit": len(answer) <= item["max_len"], }

규칙 기반이 강한 지점:

  • 형식 준수 (JSON, 특정 필드 포함 여부)
  • 필수 키워드/숫자 포함 (금액, 기간처럼 정답이 명확한 값)
  • 금칙어·안전 필터

단점은 명확합니다. "말투가 친절한가", "설명이 충분한가" 같은 의미·품질 판단은 못 합니다.


4. LLM-as-a-judge

규칙으로 안 되는 품질 평가는, 다른 LLM에게 채점을 시킵니다. 사람 대신 심판(judge) 역할을 맡기는 것입니다.

[채점 프롬프트] 아래 [질문]에 대한 [답변]을 [기준]에 따라 1~5점으로 채점하세요. 근거 문서에 없는 내용을 지어내면 감점하세요. 반드시 다음 JSON으로만 답하세요: {"score": <1-5>, "reason": "<이유>"} [질문] {question} [근거] {context} [답변] {answer} [기준] {criteria}

효과적으로 쓰는 법:

  • 루브릭을 구체적으로. "좋은가?"가 아니라 "숫자를 정확히 포함했는가, 근거 밖 내용을 지어냈는가"처럼 쪼갭니다.
  • 점수 + 이유를 함께. 이유를 요구하면 채점이 더 일관되고, 디버깅도 쉽습니다.
  • 한 번에 한 기준. 정확성·말투를 한꺼번에 매기지 말고 축을 분리합니다.

주의할 점도 있습니다. judge 모델에도 편향이 있습니다. 긴 답을 선호하거나, 자기 계열 모델을 후하게 주기도 합니다. 그래서 judge 결과는 사람 채점 샘플과 정기적으로 대조해 신뢰도를 검증해야 합니다.


5. RAG라면 검색 지표를 따로 본다

RAG 시스템은 답변 품질만 보면 원인을 못 찾습니다. 검색이 틀렸는지, 생성이 틀렸는지를 분리해야 합니다. 이때 정보검색 지표를 씁니다.

  • Recall@k: 정답 근거 문서가 상위 k개 검색 결과 안에 들어왔는지. RAG에서 가장 중요합니다. 애초에 안 뽑히면 생성이 잘할 방법이 없습니다.
  • Precision@k: 뽑힌 k개 중 실제로 관련 있는 것의 비율. 노이즈가 많은지 봅니다.
  • MRR(평균 역순위): 정답이 몇 번째에 나오는지. 상위 노출을 중시할 때.
def recall_at_k(retrieved_ids, gold_ids, k=5): top_k = retrieved_ids[:k] hit = len(set(top_k) & set(gold_ids)) return hit / len(gold_ids)

실무 감각: 답이 이상하면 십중팔구 검색(Recall)이 문제입니다. 생성 프롬프트부터 만지지 말고 검색 지표를 먼저 확인하세요.


6. 회귀 방지 - CI에 붙이기

평가는 한 번 하고 끝이 아닙니다. 프롬프트 한 줄, 모델 버전 하나 바뀌면 조용히 나빠질 수 있습니다. 그래서 평가를 자동화 파이프라인에 넣습니다.

프롬프트/모델 변경 → 평가셋 전체 실행 → 지표 집계 → 기준선(baseline) 대비 하락하면 경고/차단
  • 기준선을 저장한다. 이전 버전의 점수를 기록해 두고 비교합니다.
  • 임계값을 정한다. "Recall@5 0.8 미만이면 배포 중단"처럼 게이트를 겁니다.
  • 개별 항목 회귀를 본다. 평균은 그대로인데 특정 유형만 무너지는 경우가 있으니, 항목별 diff를 남깁니다.

7. 실무 절차 요약

정리하면 다음 순서를 권합니다.

  1. 실제 로그에서 평가셋을 뽑는다 (50~100개, 엣지 케이스 포함).
  2. 규칙 기반으로 걸러낼 수 있는 것부터 자동화한다 (형식·키워드·안전).
  3. RAG라면 검색 지표(Recall@k)를 먼저 세운다.
  4. 품질 판단은 LLM-as-a-judge로 하되, 사람 샘플로 신뢰도를 점검한다.
  5. 전체를 CI에 붙여 기준선 대비 회귀를 차단한다.
  6. 로그에서 실패 사례를 계속 모아 평가셋을 키운다.

마무리

LLM 평가의 핵심은 "주관적 느낌을 재현 가능한 수치로 바꾸는 것" 입니다. 정답이 여럿이라 완벽한 자동 채점은 불가능하지만, 규칙 기반 + LLM 심판 + 검색 지표를 조합하면 개선이 진짜 개선인지를 판단할 근거가 생깁니다.

"체감상 좋아진 것 같아요"가 아니라 "Recall@5가 0.72에서 0.85로 올랐습니다" 라고 말할 수 있을 때, 비로소 LLM 시스템을 엔지니어링하고 있는 것입니다.

me

강경원

Backend Developer

성능과 안정성으로 신뢰받는 백엔드 개발자 · 요즘은 AI/LLM을 서비스에 접목합니다

이력서 보기