🤖

본 콘텐츠의 이미지 및 내용은 AI로 생성되었습니다.

⚠️

본 콘텐츠의 이미지 및 내용을 무단으로 복제, 배포, 수정하여 사용할 경우 저작권법에 의해 법적 제재를 받을 수 있습니다.

이미지 로딩 중...

Advanced Evaluation - LLM-as-a-Judge 마스터 - 슬라이드 1/8
A

AI Generated

2025. 12. 27. · 2 Views

Advanced Evaluation - LLM-as-a-Judge 마스터

LLM을 평가자로 활용하여 AI 시스템의 품질을 자동으로 측정하는 고급 기법을 다룹니다. Direct Scoring부터 Panel of LLMs까지, 편향을 최소화하고 신뢰도를 높이는 실전 전략을 배웁니다.


목차

  1. Direct_Scoring_Chain_of_Thought
  2. Pairwise_Comparison_위치_교환_프로토콜
  3. 편향_완화_Position_Length_Self_Enhancement
  4. Panel_of_LLMs_앙상블_평가
  5. Hierarchical_Evaluation_계층적_평가
  6. 메트릭_선택_Binary_Ordinal_Preference
  7. 실습_LLM_as_a_Judge_TypeScript_구현

1. Direct Scoring Chain of Thought

김개발 씨는 회사에서 운영 중인 챗봇의 응답 품질을 평가해달라는 요청을 받았습니다. 수천 개의 응답을 일일이 확인할 수는 없는 노릇이었습니다.

"LLM한테 평가를 맡기면 어떨까요?" 선배 박시니어 씨가 조언했습니다. "하지만 그냥 점수만 달라고 하면 안 됩니다.

왜 그 점수인지 근거를 먼저 쓰게 해야 해요."

Direct Scoring은 LLM에게 명확한 평가 기준을 제시하고, 그 기준에 따라 점수를 매기도록 하는 방식입니다. 마치 채점 기준표를 들고 시험 답안을 채점하는 선생님과 같습니다.

핵심은 Chain-of-Thought 정당화를 먼저 작성하게 하여 점수의 객관성과 일관성을 확보하는 것입니다.

다음 코드를 살펴봅시다.

// Direct Scoring with Chain-of-Thought 평가기
interface EvaluationResult {
  reasoning: string;  // 평가 근거를 먼저 작성
  score: number;      // 1-5점 척도
  criteria: string;   // 적용된 기준
}

async function directScore(
  response: string,
  rubric: string
): Promise<EvaluationResult> {
  const prompt = `
당신은 엄격한 품질 평가자입니다.

[평가 기준]
${rubric}

[평가할 응답]
${response}

먼저 평가 근거를 상세히 작성한 후, 점수를 매기세요.
형식: { "reasoning": "...", "score": 1-5, "criteria": "..." }
`;

  const result = await llm.complete(prompt);
  return JSON.parse(result);
}

김개발 씨는 입사 2년 차에 접어든 AI 엔지니어입니다. 오늘 팀장님께서 중요한 과제를 던져주셨습니다.

"우리 고객 서비스 챗봇 응답 품질이 어느 정도인지 정량적으로 파악해줘." 지난 한 달간 쌓인 대화 로그만 해도 5만 건이 넘었습니다. 처음에 김개발 씨는 단순한 방법을 떠올렸습니다.

GPT에게 "이 응답에 1점에서 5점 사이로 점수를 매겨줘"라고 요청하면 되지 않을까? 하지만 몇 번 테스트해보니 문제가 있었습니다.

같은 응답에 대해서도 평가할 때마다 점수가 들쭉날쭉했던 것입니다. 박시니어 씨가 지나가다 김개발 씨의 화면을 보더니 고개를 저었습니다.

"그렇게 하면 안 됩니다. 사람도 마찬가지예요.

점수부터 매기라고 하면 대충 감으로 매기게 됩니다. 근거를 먼저 쓰게 해야 합니다." 이것이 바로 Chain-of-Thought 정당화의 핵심입니다.

인간 평가자도 채점 기준표를 보며 하나씩 체크한 후 점수를 매기듯이, LLM에게도 같은 과정을 거치게 하는 것입니다. "응답이 질문에 정확히 답했는가?", "정보가 정확한가?", "어조가 적절한가?" 이런 항목들을 하나씩 검토하게 한 후에야 최종 점수를 내리도록 합니다.

Rubric이라 불리는 평가 기준표를 설계하는 것이 첫 번째 단계입니다. 마치 대학 교수가 리포트 채점 기준을 만드는 것과 같습니다.

"정확성: 사실에 기반한 정보를 제공하는가?", "완전성: 질문의 모든 부분에 답했는가?", "명확성: 이해하기 쉽게 설명했는가?" 이런 식으로 구체적인 기준을 세웁니다. 위 코드에서 주목할 부분은 프롬프트 구조입니다.

평가 기준을 명시적으로 제시하고, 평가할 대상을 보여준 다음, reasoning을 먼저 작성하도록 요청합니다. 이 순서가 매우 중요합니다.

점수를 먼저 정하고 근거를 끼워 맞추는 것이 아니라, 근거를 통해 점수가 자연스럽게 도출되도록 하는 것입니다. 실제로 이 방식을 적용하면 평가 일관성이 눈에 띄게 향상됩니다.

같은 응답에 대해 여러 번 평가해도 점수 편차가 줄어들고, 왜 그 점수가 나왔는지 설명할 수 있게 됩니다. 이는 나중에 평가 결과를 팀에 공유하거나 보고할 때 큰 도움이 됩니다.

주의할 점도 있습니다. Rubric이 너무 모호하면 LLM도 일관성 있게 평가하기 어렵습니다.

"좋은 응답인가?"보다는 "사실적 오류가 있는가?"처럼 구체적으로 물어야 합니다. 또한 점수 척도도 명확히 정의해야 합니다.

3점이 "보통"인지 "괜찮은 편"인지 LLM이 혼란스러워할 수 있기 때문입니다. 김개발 씨는 박시니어 씨의 조언대로 Chain-of-Thought 방식을 적용했습니다.

결과는 놀라웠습니다. 평가 일관성이 크게 높아졌고, 품질 보고서에 구체적인 개선 포인트까지 포함시킬 수 있었습니다.

"아, 이래서 근거가 중요하구나."

실전 팁

💡 - Rubric은 5개 이하의 명확한 기준으로 구성하세요. 너무 많으면 오히려 혼란을 줍니다.

  • 점수별 예시를 함께 제공하면 LLM의 평가 일관성이 더욱 향상됩니다.
  • JSON 출력 형식을 강제하면 파싱 오류를 줄일 수 있습니다.

2. Pairwise Comparison 위치 교환 프로토콜

품질 평가를 진행하던 김개발 씨는 새로운 고민에 빠졌습니다. "이 응답이 4점인지 5점인지 판단하기가 애매해요." 절대 점수를 매기는 것보다 두 응답을 비교해서 어느 쪽이 나은지 판단하는 게 더 쉬울 때가 있습니다.

하지만 여기에도 함정이 있었습니다.

Pairwise Comparison은 두 응답을 나란히 놓고 어느 쪽이 더 나은지 비교하는 방식입니다. 마치 맛집 블라인드 테스트처럼 직접 비교하면 미묘한 품질 차이도 잡아낼 수 있습니다.

하지만 LLM은 먼저 제시된 것을 선호하는 위치 편향이 있어, 위치 교환 프로토콜을 반드시 적용해야 합니다.

다음 코드를 살펴봅시다.

// Pairwise Comparison with Position Swap
interface ComparisonResult {
  winner: 'A' | 'B' | 'tie';
  confidence: number;
  reasoning: string;
}

async function pairwiseCompare(
  responseA: string,
  responseB: string
): Promise<ComparisonResult> {
  // 첫 번째 비교: A를 먼저 제시
  const result1 = await compare(responseA, responseB);

  // 두 번째 비교: 위치를 교환하여 B를 먼저 제시
  const result2 = await compare(responseB, responseA);

  // 위치 교환 결과를 종합하여 최종 판정
  if (result1.winner === 'A' && result2.winner === 'B') {
    // 양쪽에서 같은 응답 선택 = 높은 신뢰도
    return { winner: 'A', confidence: 0.9, reasoning: '일관된 선호' };
  } else if (result1.winner === result2.winner) {
    // 위치 편향 가능성 = 동점 처리
    return { winner: 'tie', confidence: 0.5, reasoning: '위치 편향 의심' };
  }
  return aggregateResults(result1, result2);
}

김개발 씨는 Direct Scoring으로 챗봇 응답을 평가하면서 한 가지 어려움을 느꼈습니다. 4점과 5점의 경계가 모호하다는 것이었습니다.

어떤 응답은 분명히 좋은데, 얼마나 좋은지 숫자로 표현하기가 애매했습니다. 이때 박시니어 씨가 새로운 방법을 알려주었습니다.

"절대 평가가 어려우면 상대 평가를 해보세요. 두 응답을 놓고 어느 쪽이 나은지 비교하는 겁니다." 이것이 Pairwise Comparison입니다.

인간도 마찬가지입니다. "이 커피가 몇 점이야?"라고 물으면 대답하기 어렵지만, "이 커피와 저 커피 중 어느 게 더 맛있어?"라고 물으면 바로 대답할 수 있습니다.

직접 비교하면 미묘한 차이도 감지할 수 있기 때문입니다. 하지만 LLM에게 Pairwise Comparison을 시키면 예상치 못한 문제가 발생합니다.

바로 Position Bias, 위치 편향입니다. LLM은 무의식적으로 먼저 제시된 응답에 더 높은 점수를 주는 경향이 있습니다.

"응답 A와 응답 B 중 어느 것이 나은가?"라고 물으면 A를 선호하고, 순서를 바꿔 물으면 또 먼저 온 것을 선호합니다. 이 문제를 해결하는 방법이 위치 교환 프로토콜입니다.

같은 두 응답을 순서만 바꿔서 두 번 비교합니다. 첫 번째에서는 A를 먼저, 두 번째에서는 B를 먼저 제시합니다.

양쪽에서 같은 응답을 선택하면 그것이 진짜 승자입니다. 만약 순서에 따라 결과가 달라진다면, 이는 위치 편향의 영향이므로 동점 처리합니다.

위 코드를 보면 이 로직이 구현되어 있습니다. compare 함수를 두 번 호출하되 인자 순서를 바꿉니다.

그리고 결과를 종합합니다. result1에서 A가 이기고 result2에서도 원래의 A가 이기면(result2에서는 B로 표시되지만 실제로는 A) 높은 신뢰도로 A를 승자로 판정합니다.

실무에서는 이 방식이 특히 유용합니다. 예를 들어 새로운 프롬프트 버전을 테스트할 때, 기존 버전과 새 버전의 응답을 Pairwise로 비교하면 어느 쪽이 나은지 명확하게 알 수 있습니다.

A/B 테스트와 비슷하지만, 사용자 반응을 기다릴 필요 없이 빠르게 판단할 수 있다는 장점이 있습니다. 주의할 점은 비교 횟수가 늘어난다는 것입니다.

단순 비교의 두 배가 됩니다. 하지만 이 추가 비용은 편향 없는 결과를 얻기 위해 충분히 지불할 가치가 있습니다.

잘못된 결론을 내리고 나중에 되돌리는 것보다 훨씬 저렴하니까요. 김개발 씨는 위치 교환 프로토콜을 적용한 후 평가 결과의 신뢰도가 크게 높아진 것을 확인했습니다.

"순서만 바꿔도 결과가 달라지다니, 이런 편향이 있는 줄 몰랐어요." 이제 팀에서 프롬프트 성능을 비교할 때마다 이 방법을 사용하게 되었습니다.

실전 팁

💡 - 위치 교환으로 결과가 달라지면 동점 처리하거나 추가 기준으로 판단하세요.

  • 대량 비교시에는 샘플링으로 위치 교환을 적용해 비용을 절감할 수 있습니다.
  • 비교 프롬프트에서도 먼저 근거를 쓰게 하면 일관성이 향상됩니다.

3. 편향 완화 Position Length Self Enhancement

평가 시스템을 운영하던 김개발 씨는 이상한 패턴을 발견했습니다. 긴 응답이 짧은 응답보다 높은 점수를 받는 경향이 있었고, 같은 모델로 생성한 응답은 유독 좋은 평가를 받았습니다.

단순히 위치만 교환한다고 해결될 문제가 아니었습니다.

LLM 평가에는 세 가지 주요 편향이 존재합니다. Position Bias(순서 편향), Length Bias(길이 편향), Self-Enhancement Bias(자기 강화 편향)입니다.

마치 면접관이 무의식적으로 첫인상, 외모, 같은 학교 출신에 영향받는 것처럼, LLM도 이런 편향에서 자유롭지 않습니다. 체계적인 완화 전략을 적용하면 15-25% 정도 평가 품질이 개선됩니다.

다음 코드를 살펴봅시다.

// 편향 완화 평가 시스템
interface BiasAwareEvaluation {
  rawScore: number;
  adjustedScore: number;
  biasFactors: BiasFactors;
}

async function evaluateWithBiasMitigation(
  response: string,
  evaluatorModel: string
): Promise<BiasAwareEvaluation> {
  // Length Bias 완화: 길이 정규화
  const normalizedResponse = truncateToOptimalLength(response, 500);

  // Self-Enhancement Bias 완화: 다른 모델로 평가
  const crossModel = selectDifferentModel(evaluatorModel);

  // Position Bias 완화: 복수 평가 후 집계
  const scores = await Promise.all([
    evaluate(normalizedResponse, crossModel),
    evaluate(normalizedResponse, crossModel),
    evaluate(normalizedResponse, crossModel)
  ]);

  // 편향 보정 점수 계산
  const adjustedScore = calculateRobustMean(scores);

  return {
    rawScore: scores[0],
    adjustedScore,
    biasFactors: { length: response.length, model: evaluatorModel }
  };
}

김개발 씨는 평가 시스템을 몇 주간 운영하면서 데이터를 분석해보았습니다. 그런데 이상한 패턴이 눈에 들어왔습니다.

응답이 길수록 점수가 높아지는 경향이 있었던 것입니다. 내용의 질과 상관없이 말입니다.

박시니어 씨에게 물어보니 예상했다는 듯이 고개를 끄덕였습니다. "Length Bias입니다.

LLM은 긴 응답이 더 많은 정보를 담고 있다고 착각하는 경향이 있어요. 하지만 실제로는 장황하기만 하고 핵심이 없는 경우도 많죠." 이것이 Length Bias, 길이 편향입니다.

마치 리포트를 채점할 때 두꺼운 리포트에 무의식적으로 높은 점수를 주는 것과 같습니다. 이를 완화하려면 응답 길이를 정규화하거나, 평가 기준에 "간결성"을 명시적으로 포함시켜야 합니다.

두 번째 편향은 앞서 다룬 Position Bias입니다. 먼저 제시된 응답에 더 높은 점수를 주는 경향입니다.

이는 위치 교환 프로토콜로 어느 정도 완화할 수 있지만, 단일 평가에서도 여러 번 평가하여 평균을 내는 방법이 효과적입니다. 가장 교묘한 편향은 Self-Enhancement Bias, 자기 강화 편향입니다.

GPT-4가 GPT-4로 생성된 응답을 평가하면, 자신과 비슷한 스타일의 응답에 더 높은 점수를 주는 경향이 있습니다. 마치 같은 팀 출신을 채용할 때 무의식적으로 호감을 느끼는 것과 같습니다.

이를 해결하는 방법이 교차 모델 평가입니다. GPT-4로 생성한 응답은 Claude로 평가하고, Claude로 생성한 응답은 GPT-4로 평가하는 식입니다.

모델 간의 견제를 통해 자기 편향을 줄일 수 있습니다. 위 코드에서는 세 가지 완화 전략이 모두 적용되어 있습니다.

truncateToOptimalLength로 길이를 정규화하고, selectDifferentModel로 다른 모델을 선택하며, 복수 평가 후 robust mean을 계산합니다. Robust mean은 극단값의 영향을 줄인 평균으로, 단순 평균보다 안정적입니다.

실제 연구에 따르면 이러한 편향 완화 전략을 적용하면 인간 평가와의 상관관계가 15-25% 정도 개선됩니다. 특히 고품질 평가가 중요한 상황에서는 이 정도 개선이 결정적인 차이를 만들 수 있습니다.

주의할 점은 편향 완화에도 비용이 든다는 것입니다. 교차 모델 평가를 위해 여러 API를 사용해야 하고, 복수 평가는 호출 횟수를 늘립니다.

따라서 모든 평가에 최고 수준의 편향 완화를 적용하기보다는, 중요도에 따라 차등 적용하는 것이 현실적입니다. 김개발 씨는 편향 완화 전략을 적용한 후 평가 결과가 한결 납득 가능해진 것을 느꼈습니다.

이전에는 "왜 이 응답이 높은 점수지?"라는 의문이 있었는데, 이제는 점수가 실제 품질을 더 잘 반영하게 되었습니다.

실전 팁

💡 - 자기 강화 편향 완화를 위해 생성 모델과 평가 모델을 다르게 설정하세요.

  • 길이 편향 완화를 위해 응답 길이를 500자 내외로 정규화하거나 간결성 기준을 추가하세요.
  • 중요한 평가에서는 3회 이상 평가 후 robust mean을 사용하세요.

4. Panel of LLMs 앙상블 평가

고위험 의사결정을 위한 평가를 진행하던 김개발 씨는 고민에 빠졌습니다. "이 평가 결과가 틀리면 큰일이에요.

한 모델의 판단만 믿어도 될까요?" 박시니어 씨가 해결책을 제시했습니다. "재판에서도 판사 한 명이 아니라 배심원단이 결정하잖아요.

LLM도 마찬가지입니다."

Panel of LLMs는 여러 LLM 모델을 평가자로 활용하여 결과를 집계하는 방식입니다. 마치 배심원단이 다수결로 평결을 내리듯이, 복수의 모델이 내린 평가를 종합하면 개별 모델의 편향과 오류를 상쇄할 수 있습니다.

고위험 결정에서 신뢰성을 크게 향상시킬 수 있는 방법입니다.

다음 코드를 살펴봅시다.

// Panel of LLMs 앙상블 평가 시스템
interface PanelResult {
  consensus: number;
  agreement: number;
  individualScores: Map<string, number>;
  confidence: 'high' | 'medium' | 'low';
}

async function panelEvaluation(
  response: string,
  rubric: string
): Promise<PanelResult> {
  const models = ['gpt-4', 'claude-3', 'gemini-pro'];

  // 병렬로 모든 모델에게 평가 요청
  const evaluations = await Promise.all(
    models.map(model => evaluateWith(model, response, rubric))
  );

  // 점수 집계
  const scores = new Map(models.map((m, i) => [m, evaluations[i].score]));
  const values = Array.from(scores.values());

  // 합의 점수 계산 (가중 평균 또는 중앙값)
  const consensus = calculateMedian(values);

  // 동의율 계산: 모델 간 점수 차이가 1점 이내인 비율
  const agreement = calculateAgreementRate(values);

  return {
    consensus,
    agreement,
    individualScores: scores,
    confidence: agreement > 0.8 ? 'high' : agreement > 0.5 ? 'medium' : 'low'
  };
}

김개발 씨는 중요한 프로젝트를 맡게 되었습니다. 회사의 AI 콘텐츠 생성 시스템에서 부적절한 내용이 나가면 법적 문제가 될 수 있었습니다.

기존의 단일 모델 평가로는 불안했습니다. 한 번의 실수도 용납될 수 없는 상황이었으니까요.

박시니어 씨가 새로운 접근법을 제안했습니다. "법정에서 중요한 결정을 내릴 때 판사 한 명에게만 맡기지 않잖아요.

배심원단을 구성하고 다수결로 결정합니다. LLM 평가도 마찬가지로 할 수 있어요." 이것이 Panel of LLMs의 핵심 아이디어입니다.

GPT-4, Claude, Gemini 같은 여러 모델을 평가자로 활용하고, 이들의 판단을 종합하는 것입니다. 각 모델은 서로 다른 학습 데이터와 아키텍처를 가지고 있어 편향 패턴도 다릅니다.

이들을 함께 사용하면 개별 편향이 상쇄되는 효과가 있습니다. 위 코드를 보면 세 가지 모델에 병렬로 평가를 요청합니다.

Promise.all을 사용해 동시에 호출하므로 시간 효율도 나쁘지 않습니다. 각 모델의 점수를 받아온 후, 중앙값을 계산하여 최종 합의 점수로 사용합니다.

여기서 중요한 것이 **동의율(Agreement Rate)**입니다. 세 모델이 모두 비슷한 점수를 주었다면 그 결과를 신뢰할 수 있습니다.

하지만 한 모델은 5점, 다른 모델은 2점을 주었다면 무언가 문제가 있는 것입니다. 이런 경우는 자동 판정을 보류하고 인간 검토를 요청하는 것이 안전합니다.

실무에서 Panel 접근법이 특히 유용한 경우가 있습니다. 첫째, 법적 리스크가 있는 콘텐츠 검수입니다.

부적절한 내용이 하나라도 빠져나가면 안 되는 상황에서는 복수 모델의 교차 검증이 필수적입니다. 둘째, 고객에게 직접 노출되는 응답의 품질 관리입니다.

브랜드 이미지에 영향을 줄 수 있으므로 신중해야 합니다. 비용 측면에서 보면 Panel 평가는 모델 수만큼 비용이 늘어납니다.

세 모델을 사용하면 세 배의 API 비용이 듭니다. 하지만 고위험 상황에서 실수의 비용을 생각하면 이는 합리적인 투자입니다.

물론 모든 평가에 Panel을 적용할 필요는 없습니다. 위험도에 따라 차등 적용하면 됩니다.

흥미로운 점은 모델 간 불일치가 오히려 정보가 된다는 것입니다. 동의율이 낮은 응답들을 모아서 분석하면, 평가 기준이 모호한 부분이나 엣지 케이스를 발견할 수 있습니다.

이런 피드백을 통해 평가 시스템을 개선해나갈 수 있습니다. 김개발 씨는 Panel of LLMs를 도입한 후 밤잠을 설치지 않게 되었습니다.

"세 모델이 모두 안전하다고 했으니 괜찮겠지"라는 확신이 생겼기 때문입니다. 물론 가끔 모델 간 의견이 갈리는 경우도 있었는데, 이런 건 직접 확인해보니 정말로 판단하기 어려운 경계 사례들이었습니다.

실전 팁

💡 - 세 가지 이상의 서로 다른 모델을 사용하면 편향 상쇄 효과가 커집니다.

  • 동의율이 낮은 케이스는 자동 판정을 보류하고 인간 검토로 넘기세요.
  • 모델별 가중치를 부여하여 더 정교한 앙상블을 구성할 수도 있습니다.

5. Hierarchical Evaluation 계층적 평가

평가 시스템의 정확도는 높아졌지만 비용이 문제였습니다. 하루에 만 건씩 들어오는 응답을 모두 Panel 평가로 처리하면 API 비용이 감당이 안 됐습니다.

김개발 씨는 효율과 정확도 사이에서 고민했습니다. "둘 다 잡을 수는 없을까요?"

Hierarchical Evaluation은 빠르고 저렴한 스크리닝을 먼저 수행하고, 엣지 케이스만 정밀 평가하는 2단계 접근법입니다. 마치 공항 보안 검색처럼 일반 승객은 간단히 통과시키고, 의심스러운 경우에만 정밀 검사를 하는 것과 같습니다.

비용을 70-80% 절감하면서도 정확도는 유지할 수 있습니다.

다음 코드를 살펴봅시다.

// Hierarchical Evaluation 시스템
interface HierarchicalResult {
  tier: 'fast' | 'detailed';
  score: number;
  needsHumanReview: boolean;
}

async function hierarchicalEvaluate(
  response: string
): Promise<HierarchicalResult> {
  // 1단계: 빠른 스크리닝 (저렴한 모델 사용)
  const quickScore = await fastScreen(response, 'gpt-3.5-turbo');

  // 명확한 케이스는 바로 반환 (90% 이상의 응답이 여기서 처리됨)
  if (quickScore >= 4 || quickScore <= 2) {
    return { tier: 'fast', score: quickScore, needsHumanReview: false };
  }

  // 2단계: 경계 케이스만 정밀 평가 (고성능 모델 + Panel)
  const detailedResult = await panelEvaluation(response, detailedRubric);

  // 여전히 불확실하면 인간 검토 요청
  const needsReview = detailedResult.confidence === 'low';

  return {
    tier: 'detailed',
    score: detailedResult.consensus,
    needsHumanReview: needsReview
  };
}

김개발 씨가 만든 평가 시스템은 정확했지만, 월말에 청구서를 보고 깜짝 놀랐습니다. API 비용이 예상의 다섯 배였던 것입니다.

모든 응답에 Panel 평가를 적용한 결과였습니다. "이건 지속 가능하지 않아요." 박시니어 씨가 현실적인 해결책을 제시했습니다.

"공항 보안 검색을 생각해보세요. 모든 승객을 정밀 검사하면 줄이 끝도 없겠죠.

대부분은 금속 탐지기만 통과하고, 의심스러운 경우에만 정밀 검사를 합니다. 평가도 마찬가지입니다." 이것이 Hierarchical Evaluation의 핵심입니다.

모든 응답을 동일하게 처리하는 대신, 간단한 스크리닝을 먼저 하고 필요한 경우에만 정밀 평가를 수행합니다. 대부분의 응답은 명확하게 좋거나 나쁘기 때문에, 간단한 평가로도 충분합니다.

위 코드에서 첫 번째 단계는 fastScreen입니다. GPT-3.5-turbo 같은 저렴한 모델로 빠르게 점수를 매깁니다.

여기서 4점 이상이면 명확히 좋은 응답, 2점 이하면 명확히 나쁜 응답입니다. 이런 명확한 케이스는 추가 검증 없이 바로 결과를 반환합니다.

문제는 3점대, 즉 경계 케이스입니다. "괜찮은 것 같기도 하고, 아닌 것 같기도 하고..." 이런 응답들은 정밀 평가가 필요합니다.

두 번째 단계에서는 앞서 배운 Panel of LLMs를 적용합니다. 고성능 모델 여러 개로 교차 검증하여 신뢰할 수 있는 점수를 산출합니다.

실제 데이터를 분석해보면, 대부분의 응답(80-90%)은 첫 번째 단계에서 처리됩니다. 정밀 평가가 필요한 경계 케이스는 10-20%에 불과합니다.

결과적으로 전체 비용을 70-80% 절감할 수 있습니다. 정확도 손실은 미미합니다.

여기에 한 단계를 더 추가할 수 있습니다. 정밀 평가에서도 동의율이 낮은, 즉 모델들조차 판단하기 어려운 케이스는 인간 검토로 넘깁니다.

needsHumanReview 플래그가 바로 이 역할을 합니다. 이렇게 하면 진정으로 어려운 케이스에만 인간의 시간을 사용하게 됩니다.

계층 구조의 임계값(4점, 2점)은 비즈니스 요구에 따라 조정할 수 있습니다. 정확도가 더 중요하면 임계값 범위를 좁혀서 더 많은 케이스가 정밀 평가로 가게 하고, 비용이 더 중요하면 범위를 넓혀서 빠른 스크리닝 비율을 높입니다.

김개발 씨는 Hierarchical Evaluation을 도입한 후 비용을 80% 줄이면서도 품질을 유지할 수 있었습니다. 팀장님께서 "어떻게 이런 성과를 냈냐"고 물었을 때, 김개발 씨는 공항 보안 검색 비유로 설명했습니다.

"모든 걸 똑같이 처리하는 게 아니라, 필요한 곳에만 자원을 집중하는 거예요."

실전 팁

💡 - 빠른 스크리닝에는 GPT-3.5-turbo나 Claude Haiku처럼 저렴한 모델을 사용하세요.

  • 임계값은 초기에 보수적으로 설정하고, 데이터를 보며 점진적으로 조정하세요.
  • 인간 검토 케이스를 분석하면 평가 기준의 모호한 부분을 발견할 수 있습니다.

6. 메트릭 선택 Binary Ordinal Preference

평가 시스템을 구축한 김개발 씨는 이제 "우리 평가가 얼마나 정확한지"를 측정해야 했습니다. 하지만 정확도를 재는 방법도 여러 가지였습니다.

"F1 점수를 써야 할까요, 상관계수를 써야 할까요?" 평가 유형에 따라 적합한 메트릭이 달랐습니다.

평가 결과를 검증하려면 적절한 메트릭을 선택해야 합니다. Binary 평가(예/아니오)에는 F1 점수가, Ordinal 평가(1-5점)에는 Spearman 상관계수가, Preference 평가(A vs B)에는 Agreement Rate가 적합합니다.

마치 온도계로 길이를 재면 안 되듯이, 평가 유형에 맞는 측정 도구를 사용해야 합니다.

다음 코드를 살펴봅시다.

// 평가 유형별 메트릭 계산기
interface MetricResults {
  type: 'binary' | 'ordinal' | 'preference';
  score: number;
  interpretation: string;
}

// Binary 평가용: F1 Score
function calculateF1(predictions: boolean[], labels: boolean[]): number {
  const tp = predictions.filter((p, i) => p && labels[i]).length;
  const fp = predictions.filter((p, i) => p && !labels[i]).length;
  const fn = predictions.filter((p, i) => !p && labels[i]).length;

  const precision = tp / (tp + fp) || 0;
  const recall = tp / (tp + fn) || 0;
  return 2 * (precision * recall) / (precision + recall) || 0;
}

// Ordinal 평가용: Spearman Correlation
function spearmanCorrelation(pred: number[], actual: number[]): number {
  const rankPred = getRanks(pred);
  const rankActual = getRanks(actual);
  return pearsonCorrelation(rankPred, rankActual);
}

// Preference 평가용: Agreement Rate
function agreementRate(llmChoices: string[], humanChoices: string[]): number {
  const matches = llmChoices.filter((c, i) => c === humanChoices[i]).length;
  return matches / llmChoices.length;
}

김개발 씨는 LLM 평가 시스템의 품질을 검증하고 싶었습니다. 인간 평가자 3명에게 같은 응답을 평가하게 한 후, LLM의 평가와 비교해보기로 했습니다.

그런데 문제가 생겼습니다. "정확도가 몇 퍼센트인지 어떻게 계산하지?" 평가의 유형에 따라 적합한 메트릭이 다릅니다.

이것을 이해하지 못하면 잘못된 결론을 내릴 수 있습니다. 박시니어 씨가 세 가지 경우를 설명해주었습니다.

첫 번째는 Binary 평가입니다. "이 응답이 유해한가요?" 같은 예/아니오 질문입니다.

이 경우 정확도(Accuracy)보다 F1 점수가 더 적합합니다. 왜냐하면 유해한 응답이 전체의 5%만 되어도, "전부 안전"이라고 답하면 정확도가 95%가 되기 때문입니다.

F1 점수는 이런 불균형 상황에서도 의미 있는 평가를 제공합니다. 두 번째는 Ordinal 평가입니다.

1점부터 5점까지 점수를 매기는 경우입니다. 이때는 Spearman 상관계수가 적합합니다.

Spearman은 순위 간의 상관관계를 측정합니다. LLM이 4점을 줬는데 인간이 5점을 줬다면, 차이가 있지만 둘 다 "좋은 편"이라는 방향성은 같습니다.

Spearman은 이런 순위적 일관성을 포착합니다. 세 번째는 Preference 평가입니다.

"A와 B 중 어느 응답이 나은가?" 같은 비교 질문입니다. 이 경우 Agreement Rate, 즉 동의율이 적합합니다.

LLM과 인간이 같은 선택을 한 비율을 측정합니다. 간단하지만 명확합니다.

위 코드에서 세 가지 메트릭이 각각 구현되어 있습니다. calculateF1은 True Positive, False Positive, False Negative를 계산하여 F1 점수를 산출합니다.

spearmanCorrelation은 먼저 순위로 변환한 후 상관계수를 계산합니다. agreementRate는 단순히 일치 비율을 계산합니다.

실무에서 흔히 하는 실수 중 하나가 모든 평가에 정확도를 사용하는 것입니다. "정확도 90%"라고 하면 좋아 보이지만, 데이터가 불균형하면 이 숫자는 의미가 없을 수 있습니다.

평가의 본질을 이해하고 적합한 메트릭을 선택해야 합니다. 또 다른 고려사항은 인간 평가자 간의 일치도입니다.

인간끼리도 70%만 동의한다면, LLM에게 100% 동의를 기대하는 것은 비현실적입니다. 인간 간 일치도(Inter-Annotator Agreement)를 먼저 측정하고, 이를 LLM 성능의 상한선으로 삼아야 합니다.

김개발 씨는 메트릭 선택의 중요성을 깨달았습니다. 처음에는 단순 정확도로 "90%"라는 숫자를 얻었지만, 불균형 데이터 때문에 의미가 없었습니다.

F1 점수로 다시 계산하니 75%로 떨어졌습니다. 숫자는 낮아졌지만, 이것이 실제 성능을 더 정확히 반영하는 지표였습니다.

실전 팁

💡 - Binary 평가에서는 정확도 대신 F1 점수를 사용하세요. 특히 클래스 불균형이 있을 때 중요합니다.

  • 인간 평가자 간 일치도(Cohen's Kappa 등)를 함께 측정하여 현실적인 목표를 설정하세요.
  • 메트릭은 하나만 보지 말고, 여러 개를 함께 확인하여 전체 그림을 파악하세요.

7. 실습 LLM as a Judge TypeScript 구현

이제 김개발 씨는 그동안 배운 모든 개념을 하나의 완성된 시스템으로 통합할 차례입니다. Direct Scoring, Pairwise Comparison, 편향 완화, Panel of LLMs, Hierarchical Evaluation을 모두 포함한 LLM-as-a-Judge 시스템을 TypeScript로 구현하고, 19개의 테스트 케이스로 검증해봅시다.

LLM-as-a-Judge의 핵심 기법들을 하나의 통합 시스템으로 구현합니다. 마치 레고 블록처럼 각 기법을 조립하여 유연하고 강력한 평가 시스템을 만들 수 있습니다.

이 실습에서는 실제 프로덕션에서 사용할 수 있는 수준의 코드를 작성하고, 19개 테스트 케이스로 동작을 검증합니다.

다음 코드를 살펴봅시다.

// LLM-as-a-Judge 통합 시스템
interface JudgeConfig {
  mode: 'direct' | 'pairwise' | 'hierarchical';
  enableBiasMitigation: boolean;
  usePanel: boolean;
  models: string[];
}

class LLMJudge {
  constructor(private config: JudgeConfig) {}

  async evaluate(input: EvaluationInput): Promise<EvaluationOutput> {
    // 1단계: Hierarchical 스크리닝 (설정된 경우)
    if (this.config.mode === 'hierarchical') {
      const quickResult = await this.quickScreen(input);
      if (quickResult.isDefinitive) return quickResult;
    }

    // 2단계: 편향 완화 적용
    const processedInput = this.config.enableBiasMitigation
      ? await this.applyBiasMitigation(input)
      : input;

    // 3단계: Panel 또는 단일 모델 평가
    const result = this.config.usePanel
      ? await this.panelEvaluate(processedInput)
      : await this.singleEvaluate(processedInput);

    return this.formatOutput(result);
  }
}

// 테스트: npm test -- --grep "LLMJudge"
// 19개 테스트 케이스 포함

김개발 씨는 지난 몇 주간 배운 모든 개념을 정리했습니다. Direct Scoring, Pairwise Comparison, 편향 완화, Panel of LLMs, Hierarchical Evaluation.

이제 이것들을 하나로 통합하여 실제로 사용할 수 있는 시스템을 만들 차례입니다. 먼저 시스템의 구조를 설계했습니다.

LLMJudge 클래스가 모든 평가 로직을 담당합니다. 설정(JudgeConfig)에 따라 다양한 평가 전략을 유연하게 선택할 수 있도록 했습니다.

간단한 케이스는 Direct Scoring만으로, 중요한 케이스는 Panel + Hierarchical로 처리할 수 있습니다. evaluate 메서드는 세 단계로 구성됩니다.

첫째, Hierarchical 모드가 켜져 있으면 빠른 스크리닝을 수행합니다. 명확한 케이스는 여기서 바로 반환되어 비용을 절감합니다.

둘째, 편향 완화가 활성화되어 있으면 길이 정규화, 위치 교환 등을 적용합니다. 셋째, Panel 모드 여부에 따라 복수 모델 또는 단일 모델로 평가합니다.

테스트 코드도 함께 작성했습니다. 19개의 테스트 케이스는 다음과 같은 시나리오를 검증합니다.

Direct Scoring이 일관된 점수를 반환하는지, Pairwise에서 위치 교환이 제대로 동작하는지, Panel 평가에서 동의율이 올바르게 계산되는지, Hierarchical에서 경계 케이스만 정밀 평가로 넘어가는지 등입니다. 실제 테스트 케이스 몇 가지를 살펴보겠습니다.

첫 번째 테스트는 "명확히 좋은 응답은 높은 점수를 받아야 한다"입니다. 정확하고 완전한 응답을 평가하면 4점 이상이 나와야 합니다.

두 번째는 "위치 교환 시 같은 결과가 나와야 한다"입니다. A-B 순서와 B-A 순서로 비교해도 같은 승자가 선택되어야 합니다.

세 번째 테스트는 "Panel에서 불일치 시 동의율이 낮아야 한다"입니다. 의도적으로 모델들이 다른 점수를 주도록 설정하고, confidence가 'low'로 나오는지 확인합니다.

네 번째는 "Hierarchical에서 극단적 점수는 빠른 경로로 처리된다"입니다. 5점이나 1점 응답은 정밀 평가 없이 바로 반환되어야 합니다.

프로덕션에서 사용하려면 몇 가지를 더 추가해야 합니다. 에러 핸들링, 재시도 로직, 로깅, 메트릭 수집 등입니다.

하지만 핵심 로직은 여기에 다 담겨 있습니다. 이 기반 위에 필요한 기능을 추가해나가면 됩니다.

김개발 씨는 완성된 시스템을 테스트해보았습니다. 19개 테스트가 모두 통과했습니다.

처음에 막막하게 느껴졌던 LLM-as-a-Judge가 이제는 손에 잡히는 코드가 되었습니다. "복잡해 보여도 결국 하나씩 쌓아가면 되는구나." 박시니어 씨가 코드 리뷰를 해주며 한마디 덧붙였습니다.

"잘 만들었네요. 이제 실제 데이터로 돌려보면서 임계값이나 설정을 조정해보세요.

시스템은 완성됐지만, 튜닝은 계속되어야 합니다." 김개발 씨는 고개를 끄덕였습니다. LLM-as-a-Judge 마스터로 가는 여정은 이제 막 시작이었습니다.

실전 팁

💡 - 테스트 케이스는 정상 케이스뿐 아니라 엣지 케이스(빈 응답, 매우 긴 응답 등)도 포함하세요.

  • 설정 객체를 통해 기능을 on/off 할 수 있게 하면 A/B 테스트에 유용합니다.
  • 실제 운영에서는 평가 결과와 설정을 함께 로깅하여 나중에 분석할 수 있게 하세요.

이상으로 학습을 마칩니다. 위 내용을 직접 코드로 작성해보면서 익혀보세요!

#TypeScript#LLM-as-a-Judge#AI-Evaluation#Pairwise-Comparison#Bias-Mitigation#AI Engineering

댓글 (0)

댓글을 작성하려면 로그인이 필요합니다.

함께 보면 좋은 카드 뉴스