사람 대신 다른 LLM이 출력 품질을 평가하는 방법입니다. 대규모 평가에서 비용과 시간을 크게 절약합니다.
Copy
def llm_as_judge(task, response_a, response_b, model="gpt-4o"): """LLM-as-Judge: GPT-4o가 두 답변을 비교 평가""" judge_prompt = f"""당신은 AI 출력 품질을 평가하는 전문 심사위원입니다.## 태스크{task}## 답변 A{response_a}## 답변 B{response_b}## 평가 기준1. **정확성** (1~5): 사실적으로 정확한가?2. **완전성** (1~5): 태스크의 모든 요구사항을 충족했는가?3. **명확성** (1~5): 이해하기 쉽고 구조적인가?4. **유용성** (1~5): 실제로 도움이 되는가?## 출력 형식 (JSON){{ "answer_a": {{"정확성": N, "완전성": N, "명확성": N, "유용성": N, "총점": N}}, "answer_b": {{"정확성": N, "완전성": N, "명확성": N, "유용성": N, "총점": N}}, "winner": "A 또는 B 또는 무승부", "reasoning": "판단 근거 (2~3문장)"}}""" response = client.chat.completions.create( model=model, response_format={"type": "json_object"}, messages=[ {"role": "system", "content": "반드시 JSON 형식으로 답변하세요."}, {"role": "user", "content": judge_prompt}, ], temperature=0, ) return json.loads(response.choices[0].message.content)# 사용 예시judgment = llm_as_judge( task="Python에서 리스트 컴프리헨션을 초보자에게 설명하세요", response_a="리스트 컴프리헨션은 [expr for item in iterable] 형태의 구문입니다.", response_b="""리스트 컴프리헨션은 기존 리스트에서 새 리스트를 만드는 간결한 방법입니다.예시:```python# 일반 for 루프squares = []for x in range(5): squares.append(x**2)# 리스트 컴프리헨션 (같은 결과)squares = [x**2 for x in range(5)]
핵심: [표현식 for 변수 in 반복가능한것]""",
)print(f”승자: 답변 ”)
print(f”근거: “)
Copy
**LLM-as-Judge의 편향(Bias) 주의**:| 편향 유형 | 설명 | 완화 방법 ||----------|------|----------|| **위치 편향** | 먼저 나온 답변을 선호 | A/B 순서를 무작위로 바꿔 2회 평가 || **장문 편향** | 긴 답변을 더 좋게 평가 | 평가 기준에 "간결성" 포함 || **자기 편향** | 자신이 생성한 스타일 선호 | 다른 모델로 평가 || **형식 편향** | 마크다운, 코드블록 선호 | 내용 중심 평가 기준 명시 |```pythondef unbiased_llm_judge(task, response_a, response_b): """순서 편향을 제거한 LLM-as-Judge""" # 1회차: A 먼저 result_1 = llm_as_judge(task, response_a, response_b) # 2회차: B 먼저 (순서 뒤바꿈) result_2 = llm_as_judge(task, response_b, response_a) # 양쪽 모두 같은 답변을 승자로 선택했는지 확인 winner_1 = result_1["winner"] winner_2 = "B" if result_2["winner"] == "A" else ( "A" if result_2["winner"] == "B" else "무승부" ) if winner_1 == winner_2: return {"winner": winner_1, "confidence": "높음"} else: return {"winner": "무승부", "confidence": "낮음 (순서 편향 감지)"}
class PromptEvaluator: """프롬프트 종합 평가 도구""" def __init__(self, model="gpt-4o-mini"): self.client = OpenAI() self.model = model def evaluate(self, prompt, test_data, metrics=None): """종합 평가 실행""" if metrics is None: metrics = ["accuracy", "consistency", "format_compliance"] results = {} if "accuracy" in metrics: results["accuracy"] = self._eval_accuracy(prompt, test_data) if "consistency" in metrics: # 테스트 데이터의 처음 5개로 일관성 측정 results["consistency"] = self._eval_consistency( prompt, test_data[:5] ) if "format_compliance" in metrics: results["format_compliance"] = self._eval_format( prompt, test_data ) results["overall_score"] = np.mean(list(results.values())) return results def _eval_accuracy(self, prompt, test_data): """정확도 평가""" correct = 0 for item in test_data: response = self.client.chat.completions.create( model=self.model, messages=[ {"role": "system", "content": prompt}, {"role": "user", "content": item["input"]}, ], temperature=0, ) if evaluate_single( response.choices[0].message.content, item["expected_output"] ): correct += 1 return correct / len(test_data) def _eval_consistency(self, prompt, test_data, n_runs=5): """일관성 평가""" scores = [] for item in test_data: result = measure_consistency( prompt, item["input"], n_runs=n_runs ) scores.append(result["consistency"]) return np.mean(scores) def _eval_format(self, prompt, test_data): """형식 준수율 평가""" valid = 0 for item in test_data: response = self.client.chat.completions.create( model=self.model, messages=[ {"role": "system", "content": prompt}, {"role": "user", "content": item["input"]}, ], temperature=0, ) try: json.loads(response.choices[0].message.content) valid += 1 except json.JSONDecodeError: pass return valid / len(test_data)# 사용 예시evaluator = PromptEvaluator()results = evaluator.evaluate( prompt="리뷰 감성을 JSON으로 분류하세요.", test_data=evaluation_dataset,)print(f"정확도: {results['accuracy']:.2%}")print(f"일관성: {results['consistency']:.2%}")print(f"형식 준수: {results['format_compliance']:.2%}")print(f"종합 점수: {results['overall_score']:.2%}")
평가 데이터셋은 몇 개가 적절한가요?
최소 50개, 이상적으로는 100~200개입니다. 너무 적으면 통계적 유의성이 낮고, 너무 많으면 API 비용이 증가합니다. 카테고리(일반/경계/어려움)별로 균등하게 배분하세요. 시작은 30개로 하고, 실패 사례를 분석하여 점진적으로 확대하는 것이 효율적입니다.
LLM-as-Judge의 결과를 신뢰할 수 있나요?
GPT-4 수준의 모델을 평가자로 사용하면 인간 평가와 80~85% 일치율을 보입니다. 완벽하지는 않지만, 대규모 평가에서 비용 대비 충분히 유용합니다. 중요한 결정에는 LLM-as-Judge 결과를 1차 필터로 사용하고, 최종 판단은 사람이 하는 하이브리드 접근을 추천합니다.
프롬프트 A/B 테스트에서 통계적 유의성은 어떻게 확인하나요?
McNemar’s test 또는 paired t-test를 사용합니다. scipy.stats.ttest_rel(results_a, results_b) 로 p-value를 구하고, p < 0.05이면 통계적으로 유의미한 차이가 있다고 판단합니다. 표본 수가 적을 때는 bootstrap 방법을 사용할 수도 있습니다.
프롬프트 버전 관리는 어떻게 하나요?
Git으로 관리: 프롬프트를 별도 파일(.txt, .yaml)로 저장하고 버전 관리합니다. 2. 평가 결과 기록: 각 버전의 평가 점수를 스프레드시트에 추적합니다. 3. 변경 이유 문서화: 왜 변경했는지, 어떤 문제를 해결했는지 기록합니다. LangSmith, Weights & Biases 같은 도구도 프롬프트 버전 관리에 활용할 수 있습니다.