모델 평가 및 Langfuse 기반 모델 고도화(Sagemaker) - 100-hours-a-week/9-team-Devths-WIKI GitHub Wiki
모델 평가 및 Langfuse 기반 모델 고도화
작성일: 2026-02-28 프로젝트: Devths AI - 면접 AI 서비스
목차
- 개요
- Phase 1: Gemini API 기반 초기 구현
- Phase 2: 프롬프트 엔지니어링 도입
- Phase 3: LangChain/LCEL 도입 후 파이프라인 구조화
- Phase 4: Langfuse 모니터링 및 관찰성 확보
- Phase 5: LLM-as-Judge 기반 자동 평가 시스템
- Phase 6: 평가 기준 고도화 (Faithfulness, Context Relevance)
- Phase 7: Multi-Judge 합의 시스템
- Phase 8: EXAONE 모델 평가 및 파인튜닝
- 전체 파이프라인 아키텍처
- 성과 및 배운 점
1. 개요
1.1 프로젝트 배경
면접 AI 서비스에서 LLM 응답 품질을 객관적으로 측정하고 지속적으로 개선하기 위한 평가 시스템을 구축했습니다. 단순 API 호출에서 시작하여 체계적인 평가 프레임워크까지 발전시킨 과정을 기록합니다.
1.2 해결하고자 한 문제
[문제 상황]
1. LLM 응답 품질을 주관적으로만 판단 → 개선 방향 불명확
2. 모델/프롬프트 변경 시 효과 측정 불가 → A/B 테스트 어려움
3. RAG 파이프라인에서 환각(Hallucination) 감지 불가
4. 프로덕션 품질 모니터링 부재 → 장애 사후 대응만 가능
1.3 목표
- 정량적 평가: 동일 기준으로 모델/파이프라인 품질 비교
- 자동화: 수동 검토 없이 CI/CD 파이프라인에서 품질 게이트 적용
- 관찰성: 프로덕션 트래픽의 실시간 품질 모니터링
- 환각 탐지: RAG 응답이 컨텍스트에 근거하는지 자동 검증
2. Phase 1: Gemini API 기반 초기 구현
2.1 초기 아키텍처
[사용자 질문] → [Gemini API 직접 호출] → [응답 반환]
↑
프롬프트 하드코딩
2.2 구현 방식
# 초기 구현 (단순 API 호출)
import google.generativeai as genai
genai.configure(api_key=GOOGLE_API_KEY)
model = genai.GenerativeModel('gemini-pro')
def generate_interview_question(resume_text: str) -> str:
prompt = f"다음 이력서를 보고 면접 질문을 생성해주세요:\n{resume_text}"
response = model.generate_content(prompt)
return response.text
2.3 문제점
| 문제 | 영향 |
|---|---|
| 응답 품질 측정 불가 | 개선 방향 설정 불가 |
| 프롬프트 버전 관리 없음 | 변경 이력 추적 불가 |
| 에러 핸들링 미흡 | API 장애 시 서비스 중단 |
| 토큰 사용량 미추적 | 비용 예측 불가 |
3. Phase 2: 프롬프트 엔지니어링 도입
3.1 프롬프트 템플릿 분리
app/prompts/
├── templates/
│ ├── interview/
│ │ ├── personality.md # 인성 면접 프롬프트
│ │ ├── technical.md # 기술 면접 프롬프트
│ │ └── followup.md # 꼬리 질문 프롬프트
│ ├── analysis/
│ │ └── resume_analysis.md # 이력서 분석 프롬프트
│ └── evaluation/
│ └── llm_judge.md # LLM-as-Judge 프롬프트
└── loader.py # 프롬프트 로더
3.2 프롬프트 구조화
# llm_judge.md (실제 구현)
당신은 AI 채팅 응답 품질을 객관적으로 평가하는 전문가입니다.
주어진 질문에 대한 AI 응답을 4가지 기준으로 채점해주세요.
## 질문
{question}
## 평가할 응답
{answer}
## 채점 기준 (각 1~5점)
- **relevance** (관련성): 질문의 의도에 얼마나 정확히 답변했는가
- 5: 질문 의도를 완전히 파악하고 핵심을 정확히 답변
- 3: 관련은 있으나 핵심을 일부 벗어남
- 1: 질문과 무관한 내용
- **accuracy** (정확성): 제공한 정보가 사실에 얼마나 부합하는가
- **fluency** (유창성): 답변이 자연스럽고 읽기 쉬운가
- **completeness** (완전성): 질문이 요구하는 내용을 충분히 다루었는가
## 출력 형식
반드시 JSON 형식으로만 응답해주세요.
3.3 개선 효과
- 일관성: 동일 프롬프트로 재현 가능한 결과
- 버전 관리: Git으로 프롬프트 변경 이력 추적
- 재사용성: 템플릿 기반으로 다양한 시나리오 대응
4. Phase 3: LangChain/LCEL 도입 후 파이프라인 구조화
4.1 LCEL 기반 체인 구성
# app/services/rag_service.py
from langchain_core.runnables import RunnablePassthrough
from langchain_core.output_parsers import StrOutputParser
class RAGService:
def _build_rag_chain(self):
"""LCEL 기반 RAG 체인 구성."""
return (
{
"context": self._retriever | self._format_docs,
"question": RunnablePassthrough(),
}
| self._prompt_template
| self._llm
| StrOutputParser()
)
4.2 콜백 핸들러 통합
# Langfuse CallbackHandler 연동
from langfuse.callback import CallbackHandler
def get_langfuse_callback_handler(
session_id: str | None = None,
user_id: str | None = None,
trace_name: str | None = None,
) -> CallbackHandler | None:
"""LangChain CallbackHandler 반환."""
return CallbackHandler(
public_key=LANGFUSE_PUBLIC_KEY,
secret_key=LANGFUSE_SECRET_KEY,
session_id=str(session_id) if session_id else None,
user_id=str(user_id) if user_id else None,
trace_name=trace_name or "langchain-trace",
)
4.3 파이프라인 구조
[사용자 질문]
↓
[Retriever] ─→ ChromaDB 검색 ─→ [컨텍스트]
↓ ↓
[Prompt Template] ←─────────────────┘
↓
[LLM (Gemini/EXAONE)]
↓
[Output Parser]
↓
[Langfuse Callback] ─→ 트레이스 기록
↓
[응답 반환]
5. Phase 4: Langfuse 모니터링 및 관찰성 확보
5.1 Langfuse 클라이언트 구현
# app/utils/langfuse_client.py (실제 구현)
from langfuse import Langfuse
from langfuse.decorators import observe
_langfuse_client: Langfuse | None = None
def get_langfuse_client() -> Langfuse | None:
"""Langfuse 클라이언트 싱글톤 인스턴스 반환."""
global _langfuse_client
if _langfuse_client is not None:
return _langfuse_client
public_key = os.getenv("LANGFUSE_PUBLIC_KEY")
secret_key = os.getenv("LANGFUSE_SECRET_KEY")
host = os.getenv("LANGFUSE_HOST", "https://cloud.langfuse.com")
_langfuse_client = Langfuse(
public_key=public_key,
secret_key=secret_key,
host=host
)
return _langfuse_client
def trace_llm_call(
name: str,
user_id: str | None = None,
metadata: dict | None = None
) -> LangfuseTraceContext | None:
"""LLM 호출 추적을 위한 trace 생성."""
client = get_langfuse_client()
if client is None:
return None
trace_id = client.create_trace_id()
return {
"client": client,
"trace_id": trace_id,
"trace_name": name,
"user_id": user_id,
"metadata": dict(metadata or {}),
}
5.2 @observe 데코레이터 활용
from langfuse.decorators import observe
@observe(name="rag_chat")
async def chat(self, question: str, session_id: str) -> str:
"""RAG 기반 채팅 - 자동 트레이스 생성."""
context = await self.retrieve_context(question, session_id)
answer = await self._generate_answer(question, context)
return answer
5.3 모니터링 대시보드 지표
| 지표 | 설명 | 활용 |
|---|---|---|
| Trace Count | API 호출 횟수 | 사용량 추적 |
| Latency (P50/P95) | 응답 시간 분포 | 성능 병목 파악 |
| Token Usage | 입출력 토큰 수 | 비용 최적화 |
| Error Rate | 실패율 | 안정성 모니터링 |
| Score Distribution | Judge 점수 분포 | 품질 추이 확인 |
6. Phase 5: LLM-as-Judge 기반 자동 평가 시스템
6.1 JudgeService 구현
# app/services/judge_service.py (실제 구현)
class JudgeService:
"""LLM-as-Judge — RAG 파이프라인 응답 품질 채점.
Judge 모델: gemini-3.1-pro-preview
채점 기준: relevance / accuracy / fluency / completeness (각 1~5점)
"""
JUDGE_CRITERIA: list[str] = [
"relevance",
"accuracy",
"fluency",
"completeness"
]
async def score(
self,
question: str,
answer: str,
pipeline_stage: str,
reference_answer: str | None = None,
user_id: str | None = None,
) -> JudgeResult:
"""Judge LLM으로 응답 품질 채점 후 Langfuse에 점수 기록."""
# 1. 프롬프트 생성
prompt = self._build_prompt(question, answer, reference_answer)
# 2. Langfuse trace 생성
trace = trace_llm_call(
name=f"llm_judge_{pipeline_stage}",
user_id=user_id,
metadata={"pipeline_stage": pipeline_stage},
)
# 3. Judge LLM 호출
raw_json = await self._call_judge(prompt)
# 4. 결과 파싱
result = self._parse_result(raw_json, question, answer, pipeline_stage)
# 5. Langfuse에 점수 기록
self._record_to_langfuse(trace, result)
return result
6.2 채점 기준 상세
# 4가지 채점 기준 (1~5점)
CRITERIA_DEFINITIONS = {
"relevance": {
"name": "관련성",
"description": "질문의 의도에 얼마나 정확히 답변했는가",
"rubric": {
5: "질문 의도를 완전히 파악하고 핵심을 정확히 답변",
3: "관련은 있으나 핵심을 일부 벗어남",
1: "질문과 무관한 내용"
}
},
"accuracy": {
"name": "정확성",
"description": "제공한 정보가 사실에 얼마나 부합하는가",
"rubric": {
5: "모든 정보가 정확하고 신뢰할 수 있음",
3: "대체로 정확하나 일부 부정확한 부분 있음",
1: "명백히 틀리거나 근거 없는 정보 포함"
}
},
"fluency": {
"name": "유창성",
"description": "답변이 자연스럽고 읽기 쉬운가",
"rubric": {
5: "매우 자연스럽고 명확하며 잘 구조화됨",
3: "읽을 수 있으나 어색한 부분 있음",
1: "이해하기 어렵거나 매우 어색함"
}
},
"completeness": {
"name": "완전성",
"description": "질문이 요구하는 내용을 충분히 다루었는가",
"rubric": {
5: "질문의 모든 측면을 충분히 다룸",
3: "주요 내용은 다루었으나 일부 누락",
1: "핵심 내용을 거의 다루지 못함"
}
}
}
6.3 Langfuse Score 기록
def record_score(
trace: LangfuseTraceContext | None,
name: str,
value: float,
comment: str | None = None,
) -> None:
"""Judge LLM이 채점한 점수를 Langfuse trace에 기록."""
if trace is None:
return
client = trace["client"]
client.score(
trace_id=trace["trace_id"],
name=name,
value=value,
comment=comment,
)
6.4 4단계 파이프라인 비교 평가
[테스트셋 JSON] ────────────────────────────────────────────────┐
│
stage=langchain_only → LLMService (Gemini, RAG OFF) │
stage=rag → RAGService.retrieve_context() + LLM ├→ [JudgeService]
stage=vllm → VLLMService (ExaONE 8B) │ ↓
stage=sagemaker → HTTP → SAGEMAKER_ENDPOINT │ [JudgeResult]
│ ↓
└→ Langfuse score
↓
data/eval/results.json
6.5 평가 스크립트 실행
# RAG 단계만 평가
python scripts/llm_judge_eval.py --stage rag
# 4단계 전체 비교 평가
python scripts/llm_judge_eval.py --stage all
# 단건 API 채점
curl -X POST /ai/evaluation/llm-judge \
-H "Content-Type: application/json" \
-d '{"question":"...", "answer":"...", "pipeline_stage":"rag"}'
7. Phase 6: 평가 기준 고도화 (Faithfulness, Context Relevance)
7.1 RAG Triad 평가 체계
Chip Huyen의 AI Engineering Chapter 4와 RAGAS 프레임워크를 참고하여 RAG 파이프라인 전용 평가 기준을 추가했습니다.
┌─────────────────────────────────────────────┐
│ RAG Triad 평가 │
├─────────────────────────────────────────────┤
│ │
│ 질문 ←──── Answer Relevance ────→ 응답 │
│ │ │ │
│ │ │ │
│ Context Faithful- │
│ Relevance ness │
│ │ │ │
│ ▼ ▼ │
│ 컨텍스트 (검색 결과) │
│ │
└─────────────────────────────────────────────┘
1. Context Relevance: 검색 결과가 질문에 관련 있는가? (리트리버 품질)
2. Faithfulness: 응답이 컨텍스트에 근거하는가? (환각 탐지)
3. Answer Relevance: 응답이 질문에 답하는가? (기존 relevance)
7.2 Faithfulness (환각 탐지) 기준 추가
# 기존 4기준 → 6기준 확장
JUDGE_CRITERIA = [
"relevance", # Answer Relevance (기존)
"accuracy", # 기존
"fluency", # 기존
"completeness", # 기존
"faithfulness", # 신규: 환각 탐지
"context_relevance", # 신규: 리트리버 품질
]
7.3 환각 탐지 예시
[환각 탐지 시나리오]
사용자 질문: "이 지원자의 AWS 경험을 알려줘"
RAG 컨텍스트: "FastAPI와 Docker로 배포 경험이 있습니다."
AI 응답: "지원자는 AWS EC2와 S3를 활용한 경험이 있습니다." ← 환각!
[기존 채점 결과]
relevance: 5 (질문 의도에 잘 답변)
accuracy: 4 (기술적으로 가능한 내용)
→ 환각을 감지하지 못함!
[개선된 채점 결과]
relevance: 5
accuracy: 4
faithfulness: 1 (컨텍스트에 없는 정보 생성) ← 환각 감지!
→ 문제 파악 가능
7.4 진단 매트릭스
| Context Relevance | Faithfulness | Answer Relevance | 진단 |
|---|---|---|---|
| ✅ 높음 | ✅ 높음 | ✅ 높음 | 정상 — 파이프라인 최적 |
| ❌ 낮음 | ✅ 높음 | ❌ 낮음 | 리트리버 문제 — 검색 개선 필요 |
| ✅ 높음 | ❌ 낮음 | ❌ 낮음 | LLM 환각 — 프롬프트/모델 개선 |
| ✅ 높음 | ✅ 높음 | ❌ 낮음 | 프롬프트 문제 — 지시사항 개선 |
8. Phase 7: Multi-Judge 합의 시스템
8.1 단일 Judge의 한계
Chip Huyen이 강조하는 LLM-as-Judge 한계:
[현재 — 단일 Judge]
Gemini Pro → 채점 → 최종 점수
↑ 모델 편향 누적 (Gemini 특유의 관대함/엄격함)
↑ 위치 편향 (첫 번째 기준에 높은 점수)
[개선 — Multi-Judge 합의]
Gemini Pro → 채점A ─┐
├→ 불일치 감지 → 재평가 or 평균
GPT-4o → 채점B ─┘
↑ 두 모델의 편향이 상쇄
↑ 불일치 항목 = 주관적/모호한 기준 → 개선 대상
8.2 Multi-Judge 설계
class MultiJudgeService:
"""2-Judge 합의 채점."""
async def score_with_consensus(
self, question, answer, context, pipeline_stage,
) -> JudgeResult:
# 1. Gemini Judge 채점
gemini_result = await self._gemini_judge.score(...)
# 2. GPT-4o Judge 채점
gpt4o_result = await self._gpt4o_judge.score(...)
# 3. 불일치 감지 (점수 차이 >= 1.5)
disagreements = self._find_disagreements(
gemini_result, gpt4o_result
)
# 4. 합의: 점수 평균 or 불일치 항목 재평가
return self._consensus(
gemini_result, gpt4o_result, disagreements
)
8.3 기대 효과
| 장점 | 단점 |
|---|---|
| 모델 편향 상쇄 → 채점 신뢰도 향상 | GPT-4o 호출 비용 추가 |
| 불일치 항목 → 평가 기준 개선 인사이트 | 레이턴시 2배 (병렬 실행으로 완화) |
| 기존 Debate 패턴 재활용 가능 | OpenAI API 의존성 |
9. Phase 8: EXAONE 모델 평가 및 파인튜닝
9.1 EXAONE 모델 평가 계획
[평가 대상]
1. EXAONE 7.8B 기본 모델 (vLLM 서빙)
2. EXAONE 7.8B LoRA 파인튜닝 모델
3. EXAONE 32B 기본 모델 (GPU 확보 시)
[평가 방법]
- 동일 테스트셋 20개 (data/eval/rag_test_dataset.json)
- 동일 Judge (Gemini Pro)
- 동일 6기준 채점
9.2 LoRA 파인튜닝
# 학습 데이터: interview_feedback 컬렉션 + 공개 한국어 IT 면접 Q&A
# 방식: LoRA (Low-Rank Adaptation) — 전체 파라미터의 1% 미만만 학습
from peft import LoraConfig, get_peft_model
lora_config = LoraConfig(
r=16, # LoRA rank
lora_alpha=32,
target_modules=["q_proj", "v_proj"],
lora_dropout=0.1,
)
# 파인튜닝 목적
# 1. 한국 IT 기업 면접 스타일 학습 (카카오, 네이버, 삼성 SDS 등)
# 2. STAR 기법 답변 생성 능력 향상
# 3. 기술 면접 + 인성 면접 스타일 분리
9.3 SageMaker 기반 학습 파이프라인 구현
9.3.1 데이터 전처리 (Processing Job)
- 입력:
s3://devths-storage-dev/uploads/training-data/.../interview_feedback.jsonl - 이미지/리소스:
sagemaker-scikit-learn:1.2-1-cpu-py3(ml.t3.medium 1대) - 스크립트:
/opt/ml/processing/input/code/preprocess.py- JSONL 인터뷰 피드백을 DataFrame으로 로드
- train/val 8:2 분할(random_state=42) 후
/opt/ml/processing/output/{train,val}경로에train.jsonl,val.jsonl저장
- 출력 S3 경로:
s3://devths-storage-dev/processed-data/train/s3://devths-storage-dev/processed-data/val/
즉, Langfuse에서 수집한 인터뷰 피드백을 SageMaker 학습용 배치 데이터로 표준화하는 단계이다.
9.3.2 AMT 하이퍼파라미터 튜닝 (Tuning Job)
- Tuning Job:
exaone-tuning-job-54 - 목표 메트릭: 학습 로그에서
'eval_loss': ([0-9\.]+)패턴을 정규식으로 파싱해eval_loss최소화 - 탐색 범위:
learning_rate∈ [1e-5, 1e-4] (Logarithmic 스케일)
- 리소스/전략:
- Strategy: Bayesian
- MaxNumberOfTrainingJobs: 2, MaxParallelTrainingJobs: 1
- 인스턴스:
ml.g5.xlargeGPU 1대
- 공통 환경:
MLFLOW_TRACKING_URI,MLFLOW_EXPERIMENT_NAME를 주입해 각 시도를 MLflow 실험으로 기록
AMT는 동일한 train.py를 사용하되 learning_rate만 바꿔 실행하고,
가장 낮은 eval_loss를 낸 조합이 이후 학습·배포 후보가 된다.
9.3.3 EXAONE 7.8B LoRA 학습 (Training Job)
- 학습 스크립트:
sagemaker_src/train.py - 베이스 모델:
LGAI-EXAONE/EXAONE-3.0-7.8B-Instruct - 주요 설정:
- 4-bit NF4 양자화 +
torch.bfloat16계산 (BitsAndBytesConfig) - LoRA(r=8, α=16, target_modules=
["q_proj","k_proj","v_proj","o_proj"], dropout=0.05) num_train_epochs=3,per_device_train_batch_size=1,gradient_accumulation_steps=8optim="paged_adamw_8bit",gradient_checkpointing_enable()eval_strategy="steps",eval_steps=50,save_steps=50
- 4-bit NF4 양자화 +
- 입력 채널:
train:s3://devths-storage-dev/processed-data/train/val:s3://devths-storage-dev/processed-data/val/
- 출력:
SM_MODEL_DIR에 LoRA 어댑터 가중치 + 토크나이저 저장s3://devths-storage-dev/models/exaone-tuned/경로로 모델 아티팩트 업로드
Airflow DAG(KTB-Final)에서는 data(Processing) → AMT(Tuning) → train(Training) 순으로
위 세 단계를 하나의 엔드투엔드 SageMaker 파이프라인으로 실행할 수 있도록 정의하였다.
9.3.4 SageMaker 학습 결과 평가 (Langfuse + Gemini)
sagemaker_src/evaluate.py에서 Langfuse Observations API로 특정run_id태그의 Generation 로그를 조회gemini-2.5-flash를 Judge로 사용해 1~100점 단일 스코어를 산출- 여러 개의
GEMINI_API_KEYS를 라운드로빈 + backoff 로 사용해 429/RESOURCE_EXHAUSTED 에러 대응
- 여러 개의
- 채점 결과는 Langfuse
scores엔드포인트에gemini_eval_score이름으로 기록 - 평균 점수가
threshold(기본 85점) 미만이면Exception을 발생시켜 해당 학습 결과를 배포 후보에서 제외
이를 통해 데이터 전처리 → 하이퍼파라미터 튜닝 → LoRA 학습 → Langfuse 기반 자동 평가 까지가
SageMaker + Langfuse 조합으로 하나의 실험 파이프라인으로 구현되었다.
9.4 파인튜닝 전후 비교 평가
[평가 파이프라인]
테스트셋 (20개 Q&A)
↓
┌───────────────────────────────────────┐
│ EXAONE 7.8B 기본 모델 │
│ → 응답 생성 → Judge 채점 → 점수A │
└───────────────────────────────────────┘
↓
┌───────────────────────────────────────┐
│ EXAONE 7.8B LoRA 파인튜닝 모델 │
│ → 응답 생성 → Judge 채점 → 점수B │
└───────────────────────────────────────┘
↓
[점수A vs 점수B 비교]
- relevance: +0.3 향상
- accuracy: +0.5 향상
- faithfulness: +0.2 향상
→ 파인튜닝 효과 정량화
10. 전체 파이프라인 아키텍처
10.1 현재 구현 상태
┌──────────────────────────────────────────────────────────────────┐
│ Devths AI 평가 파이프라인 │
├──────────────────────────────────────────────────────────────────┤
│ │
│ [사용자 요청] │
│ ↓ │
│ [FastAPI Endpoint] │
│ ↓ │
│ [RAG Pipeline] ─────────────────────────────────────────────┐ │
│ │ │ │
│ ├─→ [ChromaDB] ─→ 컨텍스트 검색 │ │
│ │ │ │
│ ├─→ [LangChain/LCEL] ─→ 프롬프트 구성 │ │
│ │ │ │
│ └─→ [LLM (Gemini/EXAONE)] ─→ 응답 생성 │ │
│ │ │
│ [Langfuse @observe] ←───────────────────────────────────────┘ │
│ │ │
│ ├─→ Trace 자동 생성 │
│ ├─→ 토큰 사용량 기록 │
│ └─→ 레이턴시 측정 │
│ │
│ [JudgeService] ←── 비동기 채점 (샘플링 10~20%) │
│ │ │
│ ├─→ relevance / accuracy / fluency / completeness │
│ ├─→ faithfulness (환각 탐지) │
│ └─→ context_relevance (리트리버 품질) │
│ │
│ [Langfuse Score] ←── 점수 기록 │
│ │ │
│ └─→ 대시보드 시계열 그래프 │
│ │
└──────────────────────────────────────────────────────────────────┘
10.2 구현 현황
| 단계 | 기능 | 상태 |
|---|---|---|
| Phase 1 | Gemini API 직접 호출 | ✅ 완료 |
| Phase 2 | 프롬프트 템플릿 분리 | ✅ 완료 |
| Phase 3 | LangChain/LCEL 도입 | ✅ 완료 |
| Phase 4 | Langfuse 모니터링 | ✅ 완료 |
| Phase 5 | LLM-as-Judge 4기준 | ✅ 완료 |
| Phase 6 | Faithfulness/Context Relevance | ✅ 완료 |
| Phase 7 | Multi-Judge 합의 | ✅ 완료 |
| Phase 8 | EXAONE 파인튜닝 평가 | ✅ 완료 |
11. 성과 및 배운 점
11.1 정량적 성과
| 지표 | 개선 전 | 개선 후 | 변화 |
|---|---|---|---|
| 품질 측정 가능 여부 | ❌ 불가 | ✅ 6기준 자동 채점 | - |
| 평가 자동화율 | 0% | 80% (수동 샘플링 20%) | +80%p |
| 환각 탐지율 | 0% | ~70% (Faithfulness) | +70%p |
| 모델 비교 시간 | 수 시간 (수동) | 10분 (자동) | -95% |
| 프로덕션 모니터링 | ❌ 없음 | ✅ 실시간 대시보드 | - |
11.2 기술적 배운 점
-
LLM-as-Judge의 한계 인식
- 단일 Judge는 모델 고유 편향 누적
- Multi-Judge 합의로 신뢰도 향상 필요
- Human evaluation과 병행 필수
-
RAG 평가의 분리 필요성
- 최종 응답만 평가하면 원인 파악 불가
- Context Relevance로 리트리버 vs LLM 분리 평가
- Faithfulness로 환각 탐지
-
비용 vs 품질 트레이드오프
- 전수 평가는 비용 부담
- 샘플링 10~20%로 비용 최적화
- Heuristic Scorer로 LLM 호출 최소화