Split View: RAG 품질 평가와 실패 패턴 분석: 검색 증강 생성의 진단과 개선
RAG 품질 평가와 실패 패턴 분석: 검색 증강 생성의 진단과 개선
- 서론: RAG는 왜 평가가 어려운가
- RAG 파이프라인 구성 요소 이해
- 핵심 평가 지표(Metrics)
- 평가 프레임워크 비교
- 주요 실패 패턴 분석
- 실패 패턴 타임라인과 심각도
- 체계적 디버깅 워크플로우
- 실전 권장 사항
- FAQ
- 참고 자료
- 마무리: 실전 핵심 요약
서론: RAG는 왜 평가가 어려운가
대규모 언어 모델(LLM)의 한계를 보완하기 위해 등장한 RAG(Retrieval-Augmented Generation)는 이제 엔터프라이즈 AI 시스템의 핵심 아키텍처로 자리 잡았습니다. 그러나 RAG 시스템을 프로덕션에 배포한 후 "왜 답변 품질이 나쁜가?"라는 질문에 체계적으로 답하기란 쉽지 않습니다.
RAG의 품질 문제는 단일 원인이 아닌 파이프라인 전체에 걸쳐 발생할 수 있기 때문입니다. 검색 단계에서 잘못된 문서를 가져왔을 수도 있고, 올바른 문서를 가져왔지만 LLM이 그 내용을 무시하고 환각(hallucination)을 생성했을 수도 있습니다.
이 글에서는 RAG 파이프라인의 각 컴포넌트별 실패 모드를 분석하고, 체계적인 평가 방법론과 디버깅 전략을 소개합니다.
RAG 파이프라인 구성 요소 이해
RAG 시스템은 크게 세 가지 핵심 컴포넌트로 구성됩니다.
1. Retriever (검색기)
사용자 쿼리를 받아 관련 문서 청크를 벡터 데이터베이스나 검색 엔진에서 가져오는 역할을 합니다.
- Dense Retrieval: 임베딩 모델을 사용하여 의미적 유사도 기반 검색 (예: OpenAI
text-embedding-3-small, Cohereembed-v3) - Sparse Retrieval: BM25 등 키워드 기반 검색
- Hybrid Retrieval: Dense + Sparse를 결합한 방식
# Dense Retrieval 예시
from langchain.vectorstores import Chroma
from langchain.embeddings import OpenAIEmbeddings
embeddings = OpenAIEmbeddings(model="text-embedding-3-small")
vectorstore = Chroma.from_documents(documents, embeddings)
retriever = vectorstore.as_retriever(search_kwargs={"k": 5})
# 쿼리에 대한 관련 문서 검색
results = retriever.get_relevant_documents("RAG 평가 방법은?")
2. Reranker (재순위기)
초기 검색 결과를 더 정밀하게 재순위화합니다. Cross-encoder 모델이 대표적입니다.
# Cohere Reranker 예시
from cohere import Client
co = Client(api_key="...")
reranked = co.rerank(
model="rerank-v3.5",
query="RAG 평가 방법",
documents=retrieved_docs,
top_n=3
)
3. Generator (생성기)
검색된 컨텍스트를 기반으로 최종 답변을 생성하는 LLM입니다.
# 컨텍스트 기반 답변 생성
prompt = f"""다음 컨텍스트를 기반으로 질문에 답하세요.
컨텍스트:
{context}
질문: {query}
답변:"""
response = llm.generate(prompt)
핵심 평가 지표(Metrics)
RAG 시스템의 품질을 측정하는 지표는 검색 성능 지표와 생성 성능 지표로 나뉩니다.
검색 성능 지표 (Retrieval Metrics)
| 지표 | 설명 | 수식/개념 | 적용 시점 |
|---|---|---|---|
| Recall@K | 상위 K개 결과에 정답 문서가 포함된 비율 | 검색된 정답 수 / 전체 정답 수 | 검색 누락 진단 |
| Precision@K | 상위 K개 결과 중 정답 문서의 비율 | 정답 문서 수 / K | 노이즈 문서 진단 |
| MRR (Mean Reciprocal Rank) | 첫 번째 정답 문서의 순위 역수 평균 | 1/rank of first correct | 순위 품질 측정 |
| NDCG (Normalized DCG) | 순위를 고려한 검색 품질 점수 | DCG / Ideal DCG | 전체 순위 품질 |
| Hit Rate | 정답 문서가 하나라도 검색된 쿼리 비율 | 성공 쿼리 / 전체 쿼리 | 전체적 검색 성공률 |
# Recall@K 계산 예시
def recall_at_k(retrieved_ids, relevant_ids, k):
retrieved_set = set(retrieved_ids[:k])
relevant_set = set(relevant_ids)
return len(retrieved_set & relevant_set) / len(relevant_set)
# MRR 계산 예시
def mrr(retrieved_ids, relevant_ids):
for i, doc_id in enumerate(retrieved_ids):
if doc_id in relevant_ids:
return 1.0 / (i + 1)
return 0.0
생성 성능 지표 (Generation Metrics)
| 지표 | 설명 | 평가 방법 |
|---|---|---|
| Faithfulness (충실도) | 답변이 컨텍스트에 근거하는 정도 | LLM-as-judge로 각 문장의 근거 확인 |
| Answer Relevancy (답변 관련성) | 답변이 질문에 적절한 정도 | 답변에서 역으로 질문 생성 후 유사도 비교 |
| Context Relevancy (컨텍스트 관련성) | 검색된 컨텍스트가 질문에 관련된 정도 | 컨텍스트 내 관련 문장 비율 |
| Answer Correctness (답변 정확도) | 답변이 정답과 일치하는 정도 | Ground truth와의 비교 |
| Hallucination Rate (환각률) | 컨텍스트에 없는 정보를 생성한 비율 | 답변 내 비근거 정보 탐지 |
평가 프레임워크 비교
RAGAS (Retrieval Augmented Generation Assessment)
RAGAS는 RAG 시스템 평가에 특화된 오픈소스 프레임워크로, LLM을 활용한 자동 평가를 지원합니다.
from ragas import evaluate
from ragas.metrics import (
faithfulness,
answer_relevancy,
context_precision,
context_recall,
)
from datasets import Dataset
# 평가 데이터 준비
eval_data = {
"question": ["RAG란 무엇인가?"],
"answer": ["RAG는 검색 증강 생성으로..."],
"contexts": [["RAG(Retrieval-Augmented Generation)는..."]],
"ground_truth": ["RAG는 외부 지식을 검색하여..."]
}
dataset = Dataset.from_dict(eval_data)
results = evaluate(
dataset,
metrics=[faithfulness, answer_relevancy, context_precision, context_recall]
)
print(results)
# {'faithfulness': 0.92, 'answer_relevancy': 0.87, ...}
DeepEval
DeepEval은 유닛 테스트 스타일로 LLM 애플리케이션을 평가할 수 있는 프레임워크입니다.
from deepeval import evaluate
from deepeval.metrics import (
FaithfulnessMetric,
AnswerRelevancyMetric,
ContextualRelevancyMetric,
HallucinationMetric,
)
from deepeval.test_case import LLMTestCase
test_case = LLMTestCase(
input="RAG의 주요 평가 지표는?",
actual_output="RAG의 주요 평가 지표로는 faithfulness, relevancy...",
expected_output="Faithfulness, Answer Relevancy, Context Precision...",
retrieval_context=["RAG 평가에는 다양한 지표가 사용됩니다..."]
)
faithfulness_metric = FaithfulnessMetric(threshold=0.7)
relevancy_metric = AnswerRelevancyMetric(threshold=0.7)
evaluate([test_case], [faithfulness_metric, relevancy_metric])
LlamaIndex Evaluation
LlamaIndex는 자체 평가 모듈을 제공하며, RAG 파이프라인과 긴밀하게 통합됩니다.
from llama_index.core.evaluation import (
FaithfulnessEvaluator,
RelevancyEvaluator,
CorrectnessEvaluator,
BatchEvalRunner,
)
from llama_index.llms.openai import OpenAI
llm = OpenAI(model="gpt-4o")
faithfulness_evaluator = FaithfulnessEvaluator(llm=llm)
relevancy_evaluator = RelevancyEvaluator(llm=llm)
# 배치 평가
runner = BatchEvalRunner(
{"faithfulness": faithfulness_evaluator, "relevancy": relevancy_evaluator},
workers=4,
)
eval_results = await runner.aevaluate_queries(query_engine, queries=queries)
Custom LLM-as-Judge
특정 도메인에 맞는 맞춤형 평가 기준을 적용할 때 LLM을 판정자로 사용합니다.
JUDGE_PROMPT = """다음 RAG 시스템의 답변을 평가하세요.
[질문]: {question}
[컨텍스트]: {context}
[답변]: {answer}
평가 기준:
1. 충실도 (1-5): 답변이 컨텍스트에 근거하는가?
2. 완전성 (1-5): 질문의 모든 측면을 다루는가?
3. 간결성 (1-5): 불필요한 정보 없이 핵심만 전달하는가?
JSON 형식으로 점수와 근거를 출력하세요."""
def evaluate_with_judge(question, context, answer, judge_llm):
prompt = JUDGE_PROMPT.format(
question=question, context=context, answer=answer
)
result = judge_llm.generate(prompt)
return json.loads(result)
프레임워크 비교표
| 기능 | RAGAS | DeepEval | LlamaIndex Eval | Custom LLM-as-Judge |
|---|---|---|---|---|
| 설치 용이성 | 높음 | 높음 | 중간 (LlamaIndex 필요) | 직접 구현 |
| 지원 지표 | 6개+ | 10개+ | 5개+ | 무제한 (커스텀) |
| CI/CD 통합 | 가능 | 우수 (pytest 스타일) | 가능 | 직접 구현 |
| 비용 | LLM API 비용 | LLM API 비용 | LLM API 비용 | LLM API 비용 |
| 도메인 커스터마이징 | 중간 | 높음 | 중간 | 최고 |
| 대시보드 | Confidence AI 연동 | DeepEval 클라우드 | 없음 | 직접 구현 |
| 오픈소스 | Yes | Yes (코어) | Yes | N/A |
| Ground Truth 필요 | 선택적 | 선택적 | 선택적 | 설계에 따라 |
주요 실패 패턴 분석
실패 패턴 1: 잘못된 청크 검색 (Retrieval Failure)
가장 기본적이면서도 가장 흔한 실패입니다. 사용자 질문과 관련 없는 문서 청크가 검색되는 경우입니다.
원인 분석:
- 청킹 전략 문제: 의미 단위가 아닌 고정 길이로 자를 때 문맥이 분리됨
- 임베딩 모델의 도메인 불일치
- 메타데이터 필터링 부재
예시:
질문: "2024년 4분기 매출은 얼마인가?"
검색된 청크: "2023년 4분기 매출은 150억원을 기록했습니다..."
→ 연도가 다른 문서가 검색됨 (메타데이터 필터링 부재)
기대 청크: "2024년 4분기 매출은 200억원으로 전년 대비 33% 성장..."
해결 방법:
# 메타데이터 필터를 활용한 검색
results = vectorstore.similarity_search(
query="4분기 매출",
filter={"year": 2024, "quarter": "Q4"},
k=5
)
# Semantic Chunking 적용
from langchain.text_splitter import SemanticChunker
splitter = SemanticChunker(
embeddings=embeddings,
breakpoint_threshold_type="percentile",
breakpoint_threshold_amount=90,
)
chunks = splitter.split_documents(documents)
실패 패턴 2: 컨텍스트 윈도우 오버플로우
너무 많은 문서를 검색하여 컨텍스트 윈도우를 초과하거나, 반대로 너무 적게 검색하여 정보가 부족한 경우입니다.
너무 많은 경우의 문제:
- 토큰 제한 초과로 인한 잘림(truncation)
- 노이즈 문서가 LLM의 주의를 분산
- 비용 증가
너무 적은 경우의 문제:
- 답변에 필요한 정보 부족
- 불완전한 답변 생성
# 적응형 K 선택 전략
def adaptive_retrieval(query, retriever, min_k=3, max_k=10, threshold=0.7):
"""유사도 임계값을 기반으로 동적으로 K를 조절"""
results = retriever.similarity_search_with_score(query, k=max_k)
filtered = [
(doc, score) for doc, score in results
if score >= threshold
]
if len(filtered) < min_k:
return [doc for doc, _ in results[:min_k]]
return [doc for doc, _ in filtered]
실패 패턴 3: 올바른 검색에도 불구한 환각 (Hallucination Despite Correct Retrieval)
정확한 문서가 검색되었음에도 LLM이 컨텍스트에 없는 정보를 만들어내는 경우입니다. 이것은 RAG에서 가장 위험한 실패 패턴 중 하나입니다.
원인 분석:
- LLM의 사전 학습 지식이 컨텍스트와 충돌
- 프롬프트에서 "컨텍스트만 사용하라"는 지시가 불충분
- 컨텍스트에 부분적 정보만 있어 LLM이 나머지를 채움
# 환각 방지를 위한 강화된 프롬프트
ANTI_HALLUCINATION_PROMPT = """당신은 주어진 컨텍스트만을 기반으로 답변하는 어시스턴트입니다.
규칙:
1. 반드시 컨텍스트에 있는 정보만 사용하세요.
2. 컨텍스트에 없는 정보는 "제공된 문서에서 해당 정보를 찾을 수 없습니다"라고 답하세요.
3. 추측하거나 사전 지식을 사용하지 마세요.
4. 답변의 각 문장 끝에 [출처: 문서N]을 표시하세요.
컨텍스트:
{context}
질문: {question}
답변:"""
실패 패턴 4: Lost-in-the-Middle 문제
2023년 Stanford 연구에서 밝혀진 현상으로, LLM이 긴 컨텍스트의 중간에 위치한 정보를 효과적으로 활용하지 못하는 문제입니다.
현상:
- 컨텍스트의 시작과 끝에 있는 정보는 잘 활용
- 중간에 위치한 정보는 무시하거나 놓침
- 검색된 문서가 많을수록 심해짐
# Lost-in-the-Middle 완화 전략: 중요 문서를 양 끝에 배치
def reorder_for_lost_in_middle(documents, scores):
"""가장 관련성 높은 문서를 시작과 끝에 배치"""
sorted_docs = sorted(
zip(documents, scores), key=lambda x: x[1], reverse=True
)
reordered = []
for i, (doc, score) in enumerate(sorted_docs):
if i % 2 == 0:
reordered.insert(0, doc) # 앞에 삽입
else:
reordered.append(doc) # 뒤에 삽입
return reordered
실패 패턴 5: 임베딩 모델 미스매치
쿼리의 분포와 문서의 분포가 달라 임베딩 공간에서 의미적 유사도가 제대로 반영되지 않는 경우입니다.
원인 분석:
- 일반 목적 임베딩 모델로 전문 도메인 문서를 임베딩
- 쿼리 스타일(짧은 질문)과 문서 스타일(긴 설명문)의 차이
- 다국어 문서에 영어 전용 임베딩 모델 사용
# 쿼리에 instruction prefix를 추가하여 미스매치 완화
# (Instructor 계열 임베딩 모델 활용)
from InstructorEmbedding import INSTRUCTOR
model = INSTRUCTOR("hkunlp/instructor-xl")
# 쿼리용 임베딩
query_embedding = model.encode(
[["Represent the question for retrieving supporting documents:", query]]
)
# 문서용 임베딩
doc_embedding = model.encode(
[["Represent the technical document for retrieval:", document]]
)
실패 패턴 6: 오래된 지식 베이스 (Stale Knowledge Base)
지식 베이스의 문서가 최신 정보를 반영하지 못해 답변이 현실과 동떨어지는 경우입니다.
해결 전략:
# 문서 신선도 관리 시스템
class KnowledgeBaseFreshnessManager:
def __init__(self, vectorstore, max_age_days=30):
self.vectorstore = vectorstore
self.max_age_days = max_age_days
def check_staleness(self):
"""오래된 문서 탐지"""
cutoff = datetime.now() - timedelta(days=self.max_age_days)
stale_docs = self.vectorstore.query(
filter={"updated_at": {"$lt": cutoff.isoformat()}}
)
return stale_docs
def incremental_update(self, new_documents):
"""증분 업데이트: 변경된 문서만 재임베딩"""
for doc in new_documents:
existing = self.vectorstore.get(
filter={"source_id": doc.metadata["source_id"]}
)
if existing and self._content_changed(existing, doc):
self.vectorstore.delete(ids=[existing.id])
self.vectorstore.add_documents([doc])
elif not existing:
self.vectorstore.add_documents([doc])
def add_temporal_boost(self, results, recency_weight=0.1):
"""최신 문서에 가산점 부여"""
now = datetime.now()
boosted = []
for doc, score in results:
age_days = (now - doc.metadata["updated_at"]).days
recency_score = max(0, 1 - age_days / 365)
final_score = score + recency_weight * recency_score
boosted.append((doc, final_score))
return sorted(boosted, key=lambda x: x[1], reverse=True)
실패 패턴 타임라인과 심각도
| 실패 패턴 | 발생 빈도 | 사용자 영향 | 진단 난이도 | 수정 난이도 |
|---|---|---|---|---|
| 잘못된 청크 검색 | 매우 높음 | 높음 | 중간 | 중간 |
| 컨텍스트 윈도우 오버플로우 | 높음 | 중간 | 낮음 | 낮음 |
| 정상 검색 + 환각 | 중간 | 매우 높음 | 높음 | 높음 |
| Lost-in-the-Middle | 중간 | 중간 | 높음 | 중간 |
| 임베딩 미스매치 | 중간 | 높음 | 높음 | 높음 |
| 오래된 지식 베이스 | 높음 | 높음 | 낮음 | 중간 |
체계적 디버깅 워크플로우
RAG 시스템의 품질 이슈를 진단할 때 다음 워크플로우를 따르면 효율적으로 원인을 파악할 수 있습니다.
단계 1: 문제 재현 및 분류
def classify_failure(question, retrieved_docs, generated_answer, ground_truth):
"""RAG 실패를 체계적으로 분류"""
# 1단계: 검색 품질 확인
retrieval_recall = calculate_recall(retrieved_docs, ground_truth_docs)
if retrieval_recall < 0.5:
return "RETRIEVAL_FAILURE"
# 2단계: 컨텍스트 관련성 확인
context_relevancy = evaluate_context_relevancy(question, retrieved_docs)
if context_relevancy < 0.5:
return "CONTEXT_NOISE"
# 3단계: 답변 충실도 확인
faithfulness = evaluate_faithfulness(generated_answer, retrieved_docs)
if faithfulness < 0.7:
return "HALLUCINATION"
# 4단계: 답변 정확도 확인
correctness = evaluate_correctness(generated_answer, ground_truth)
if correctness < 0.7:
return "GENERATION_QUALITY"
return "ACCEPTABLE"
단계 2: 컴포넌트별 심층 분석
# 검색 단계 디버깅
def debug_retrieval(query, vectorstore, k=10):
results = vectorstore.similarity_search_with_score(query, k=k)
print(f"Query: {query}")
print(f"{'='*60}")
for i, (doc, score) in enumerate(results):
print(f"\n[{i+1}] Score: {score:.4f}")
print(f"Source: {doc.metadata.get('source', 'unknown')}")
print(f"Content: {doc.page_content[:200]}...")
print(f"Metadata: {doc.metadata}")
# 쿼리 임베딩 분석
query_embedding = embeddings.embed_query(query)
print(f"\nQuery embedding norm: {np.linalg.norm(query_embedding):.4f}")
print(f"Query embedding dim: {len(query_embedding)}")
return results
단계 3: A/B 테스트와 반복 개선
# RAG 설정 A/B 테스트 프레임워크
class RAGABTest:
def __init__(self, test_queries, ground_truths):
self.test_queries = test_queries
self.ground_truths = ground_truths
def run_experiment(self, config_a, config_b, metrics):
results_a = self._evaluate_config(config_a, metrics)
results_b = self._evaluate_config(config_b, metrics)
comparison = {}
for metric_name in metrics:
score_a = np.mean(results_a[metric_name])
score_b = np.mean(results_b[metric_name])
improvement = (score_b - score_a) / score_a * 100
comparison[metric_name] = {
"config_a": score_a,
"config_b": score_b,
"improvement_pct": improvement,
}
return comparison
# 사용 예시
ab_test = RAGABTest(test_queries, ground_truths)
result = ab_test.run_experiment(
config_a={"chunk_size": 512, "k": 5, "model": "gpt-4o-mini"},
config_b={"chunk_size": 1024, "k": 3, "model": "gpt-4o"},
metrics=["faithfulness", "answer_relevancy", "recall"]
)
실전 권장 사항
청킹 전략 선택 가이드
문서 유형에 따른 청킹 전략:
1. 기술 문서 / API 문서
→ Markdown Header 기반 분할 + 작은 청크 (256-512 토큰)
2. 법률/규정 문서
→ 조항 단위 분할 + 계층적 인덱싱
3. 대화 로그 / FAQ
→ 질문-답변 쌍 단위 분할
4. 학술 논문
→ 섹션 기반 분할 + 초록/결론 별도 인덱싱
5. 일반 텍스트
→ Semantic Chunking (의미 단위 분할)
프로덕션 모니터링 체크리스트
- 일일 모니터링: 검색 히트율, 평균 유사도 점수, 답변 길이 분포
- 주간 모니터링: 사용자 피드백(thumbs up/down) 추이, 환각률 샘플링
- 월간 모니터링: 전체 테스트셋 대상 RAGAS 평가, 임베딩 드리프트 분석
# 프로덕션 모니터링 대시보드 지표
monitoring_metrics = {
"retrieval": {
"avg_similarity_score": 0.82,
"hit_rate": 0.94,
"avg_retrieved_docs": 4.2,
"empty_retrieval_rate": 0.02,
},
"generation": {
"avg_faithfulness": 0.89,
"avg_answer_length": 245,
"refusal_rate": 0.05,
"avg_latency_ms": 1200,
},
"user_feedback": {
"thumbs_up_rate": 0.78,
"escalation_rate": 0.08,
}
}
FAQ
Q1: RAG 평가에 Ground Truth 데이터가 반드시 필요한가요?
아닙니다. RAGAS의 faithfulness나 context relevancy 같은 지표는 Ground Truth 없이도 측정 가능합니다. 다만 Answer Correctness나 Recall@K 같은 지표는 Ground Truth가 필요합니다. 초기에는 Ground Truth 없는 지표로 시작하고, 점진적으로 골든 데이터셋을 구축하는 것을 추천합니다.
Q2: 평가용 LLM은 생성용 LLM과 같은 것을 써야 하나요?
일반적으로 다른 모델을 사용하는 것이 권장됩니다. 같은 모델을 사용하면 편향(bias)이 발생할 수 있습니다. 예를 들어, GPT-4o로 생성한 답변을 GPT-4o로 평가하면 자기 평가 편향이 생길 수 있습니다. Claude나 다른 계열의 모델을 교차 사용하면 더 객관적인 평가가 가능합니다.
Q3: Retrieval과 Generation 중 어느 쪽을 먼저 개선해야 하나요?
대부분의 경우 Retrieval을 먼저 개선하는 것이 효과적입니다. "Garbage in, garbage out" 원칙이 적용되기 때문입니다. 검색 품질이 낮으면 아무리 좋은 LLM을 사용해도 답변 품질이 제한됩니다. Retrieval Recall이 0.8 이상이 되면 Generation 쪽 최적화로 넘어가는 것을 추천합니다.
Q4: 청크 크기(Chunk Size)는 얼마가 적절한가요?
단일 정답은 없지만, 일반적인 가이드라인은 다음과 같습니다:
- 256-512 토큰: 짧은 사실 기반 QA에 적합
- 512-1024 토큰: 설명이 필요한 일반적인 질문에 적합
- 1024-2048 토큰: 복잡한 분석이 필요한 질문에 적합
청크 크기가 너무 작으면 문맥이 손실되고, 너무 크면 노이즈가 증가합니다. 최적 크기는 실험으로 결정해야 합니다.
Q5: Reranker는 항상 사용해야 하나요?
Reranker는 초기 검색 결과의 정밀도를 높이는 데 매우 효과적이지만, 추가 레이턴시와 비용이 발생합니다. 다음 경우에 특히 권장됩니다:
- 검색 결과의 상위 문서가 자주 관련 없는 경우
- 쿼리가 복잡하거나 다의적인 경우
- Retrieval Precision이 낮은 경우
Q6: 다국어 RAG 시스템에서 특별히 주의할 점은?
다국어 환경에서는 다음을 고려하세요:
- 다국어 임베딩 모델 사용 (예:
multilingual-e5-large) - 언어별 청킹 전략 차별화 (한국어는 형태소 기반, 일본어는 분절 기반)
- 교차 언어 검색 테스트 (한국어 질문 → 영어 문서 검색 등)
참고 자료
- RAGAS 공식 문서 및 논문: https://docs.ragas.io/ - RAG 평가 프레임워크의 공식 문서
- "Lost in the Middle" 논문: https://arxiv.org/abs/2307.03172 - Liu et al., 2023. LLM이 긴 컨텍스트의 중간 정보를 놓치는 현상 분석
- "Retrieval-Augmented Generation for Large Language Models: A Survey": https://arxiv.org/abs/2312.10997 - Gao et al., 2024. RAG 기술 전반의 종합 서베이
- DeepEval 공식 문서: https://docs.confident-ai.com/ - LLM 평가 프레임워크
- LlamaIndex 평가 가이드: https://docs.llamaindex.ai/en/stable/module_guides/evaluating/ - LlamaIndex 내장 평가 모듈
- "Benchmarking Large Language Models in Retrieval-Augmented Generation": https://arxiv.org/abs/2309.01431 - Chen et al., 2023. RAG 벤치마킹 연구
- MTEB Benchmark: https://huggingface.co/spaces/mteb/leaderboard - 임베딩 모델 성능 비교 리더보드
마무리: 실전 핵심 요약
RAG 시스템의 품질 관리는 단순히 LLM을 교체하는 것으로 해결되지 않습니다. 체계적인 평가 프레임워크를 도입하고, 각 컴포넌트별 실패 모드를 이해하며, 지속적인 모니터링을 통해 점진적으로 개선하는 것이 핵심입니다.
가장 중요한 세 가지 실천 사항을 정리하면:
- 평가 데이터셋부터 구축하세요: 최소 50-100개의 질문-답변 쌍으로 시작하여 지속적으로 확장합니다.
- Retrieval을 먼저 최적화하세요: 검색 품질이 전체 파이프라인의 상한선을 결정합니다.
- 자동화된 평가 파이프라인을 CI/CD에 통합하세요: 모든 변경사항이 품질 회귀를 일으키지 않는지 자동으로 검증합니다.
이 세 가지를 실천하면 RAG 시스템의 품질을 예측 가능하고 지속적으로 개선할 수 있습니다.
RAG Quality Evaluation and Failure Pattern Analysis: Diagnosing and Improving Retrieval-Augmented Generation
- Introduction: Why RAG Evaluation Is Challenging
- Understanding RAG Pipeline Components
- Key Evaluation Metrics
- Evaluation Framework Comparison
- Major Failure Pattern Analysis
- Failure Pattern Timeline and Severity
- Systematic Debugging Workflow
- Practical Recommendations
- FAQ
- Q1: Is ground truth data always necessary for RAG evaluation?
- Q2: Should the evaluation LLM be the same as the generation LLM?
- Q3: Should I improve retrieval or generation first?
- Q4: What is the appropriate chunk size?
- Q5: Should a reranker always be used?
- Q6: What should be specifically considered for multilingual RAG systems?
- References
- Conclusion: Key Takeaways for Practice
- Quiz
Introduction: Why RAG Evaluation Is Challenging
RAG (Retrieval-Augmented Generation), developed to address the limitations of large language models (LLMs), has become a core architecture in enterprise AI systems. However, systematically answering the question "Why is the answer quality poor?" after deploying a RAG system to production is far from straightforward.
Quality issues in RAG can arise across the entire pipeline, not from a single source. The retrieval stage may have fetched the wrong documents, or the LLM may have ignored correctly retrieved content and produced hallucinations instead.
This article analyzes failure modes for each RAG pipeline component and introduces systematic evaluation methodologies and debugging strategies.
Understanding RAG Pipeline Components
A RAG system consists of three core components.
1. Retriever
The retriever takes a user query and fetches relevant document chunks from a vector database or search engine.
- Dense Retrieval: Semantic similarity-based search using embedding models (e.g., OpenAI
text-embedding-3-small, Cohereembed-v3) - Sparse Retrieval: Keyword-based search such as BM25
- Hybrid Retrieval: Combining Dense + Sparse approaches
# Dense Retrieval example
from langchain.vectorstores import Chroma
from langchain.embeddings import OpenAIEmbeddings
embeddings = OpenAIEmbeddings(model="text-embedding-3-small")
vectorstore = Chroma.from_documents(documents, embeddings)
retriever = vectorstore.as_retriever(search_kwargs={"k": 5})
# Retrieve relevant documents for a query
results = retriever.get_relevant_documents("How to evaluate RAG?")
2. Reranker
The reranker refines the initial search results by re-ranking them with higher precision. Cross-encoder models are the most common approach.
# Cohere Reranker example
from cohere import Client
co = Client(api_key="...")
reranked = co.rerank(
model="rerank-v3.5",
query="RAG evaluation methods",
documents=retrieved_docs,
top_n=3
)
3. Generator
The LLM that produces the final answer based on the retrieved context.
# Context-based answer generation
prompt = f"""Answer the question based on the following context.
Context:
{context}
Question: {query}
Answer:"""
response = llm.generate(prompt)
Key Evaluation Metrics
Metrics for measuring RAG system quality fall into two categories: retrieval performance metrics and generation performance metrics.
Retrieval Performance Metrics
| Metric | Description | Formula/Concept | When to Use |
|---|---|---|---|
| Recall@K | Proportion of relevant documents found in top K results | Retrieved relevant / Total relevant | Diagnosing retrieval misses |
| Precision@K | Proportion of relevant documents among top K results | Relevant docs / K | Diagnosing noisy results |
| MRR (Mean Reciprocal Rank) | Average reciprocal rank of the first relevant document | 1/rank of first correct | Measuring ranking quality |
| NDCG (Normalized DCG) | Rank-aware retrieval quality score | DCG / Ideal DCG | Overall ranking quality |
| Hit Rate | Proportion of queries where at least one relevant doc is retrieved | Successful queries / Total queries | Overall retrieval success rate |
# Recall@K calculation example
def recall_at_k(retrieved_ids, relevant_ids, k):
retrieved_set = set(retrieved_ids[:k])
relevant_set = set(relevant_ids)
return len(retrieved_set & relevant_set) / len(relevant_set)
# MRR calculation example
def mrr(retrieved_ids, relevant_ids):
for i, doc_id in enumerate(retrieved_ids):
if doc_id in relevant_ids:
return 1.0 / (i + 1)
return 0.0
Generation Performance Metrics
| Metric | Description | Evaluation Method |
|---|---|---|
| Faithfulness | How well the answer is grounded in the context | LLM-as-judge verifies evidence for each sentence |
| Answer Relevancy | How appropriate the answer is to the question | Generate questions from answer and compare similarity |
| Context Relevancy | How relevant the retrieved context is to the question | Proportion of relevant sentences in context |
| Answer Correctness | How well the answer matches the ground truth | Comparison with ground truth |
| Hallucination Rate | Proportion of information generated without context support | Detection of unsupported information in answer |
Evaluation Framework Comparison
RAGAS (Retrieval Augmented Generation Assessment)
RAGAS is an open-source framework specialized for RAG system evaluation, supporting automated evaluation using LLMs.
from ragas import evaluate
from ragas.metrics import (
faithfulness,
answer_relevancy,
context_precision,
context_recall,
)
from datasets import Dataset
# Prepare evaluation data
eval_data = {
"question": ["What is RAG?"],
"answer": ["RAG is Retrieval-Augmented Generation that..."],
"contexts": [["RAG (Retrieval-Augmented Generation) is..."]],
"ground_truth": ["RAG retrieves external knowledge to..."]
}
dataset = Dataset.from_dict(eval_data)
results = evaluate(
dataset,
metrics=[faithfulness, answer_relevancy, context_precision, context_recall]
)
print(results)
# {'faithfulness': 0.92, 'answer_relevancy': 0.87, ...}
DeepEval
DeepEval is a framework that enables unit-test-style evaluation of LLM applications.
from deepeval import evaluate
from deepeval.metrics import (
FaithfulnessMetric,
AnswerRelevancyMetric,
ContextualRelevancyMetric,
HallucinationMetric,
)
from deepeval.test_case import LLMTestCase
test_case = LLMTestCase(
input="What are the key RAG evaluation metrics?",
actual_output="Key RAG evaluation metrics include faithfulness, relevancy...",
expected_output="Faithfulness, Answer Relevancy, Context Precision...",
retrieval_context=["Various metrics are used for RAG evaluation..."]
)
faithfulness_metric = FaithfulnessMetric(threshold=0.7)
relevancy_metric = AnswerRelevancyMetric(threshold=0.7)
evaluate([test_case], [faithfulness_metric, relevancy_metric])
LlamaIndex Evaluation
LlamaIndex provides its own evaluation module, tightly integrated with the RAG pipeline.
from llama_index.core.evaluation import (
FaithfulnessEvaluator,
RelevancyEvaluator,
CorrectnessEvaluator,
BatchEvalRunner,
)
from llama_index.llms.openai import OpenAI
llm = OpenAI(model="gpt-4o")
faithfulness_evaluator = FaithfulnessEvaluator(llm=llm)
relevancy_evaluator = RelevancyEvaluator(llm=llm)
# Batch evaluation
runner = BatchEvalRunner(
{"faithfulness": faithfulness_evaluator, "relevancy": relevancy_evaluator},
workers=4,
)
eval_results = await runner.aevaluate_queries(query_engine, queries=queries)
Custom LLM-as-Judge
When applying domain-specific evaluation criteria, use an LLM as the judge.
JUDGE_PROMPT = """Evaluate the following RAG system response.
[Question]: {question}
[Context]: {context}
[Answer]: {answer}
Evaluation Criteria:
1. Faithfulness (1-5): Is the answer grounded in the context?
2. Completeness (1-5): Does it address all aspects of the question?
3. Conciseness (1-5): Does it deliver key information without unnecessary details?
Output scores and reasoning in JSON format."""
def evaluate_with_judge(question, context, answer, judge_llm):
prompt = JUDGE_PROMPT.format(
question=question, context=context, answer=answer
)
result = judge_llm.generate(prompt)
return json.loads(result)
Framework Comparison Table
| Feature | RAGAS | DeepEval | LlamaIndex Eval | Custom LLM-as-Judge |
|---|---|---|---|---|
| Ease of Setup | High | High | Medium (requires LlamaIndex) | Manual implementation |
| Supported Metrics | 6+ | 10+ | 5+ | Unlimited (custom) |
| CI/CD Integration | Possible | Excellent (pytest-style) | Possible | Manual implementation |
| Cost | LLM API cost | LLM API cost | LLM API cost | LLM API cost |
| Domain Customization | Medium | High | Medium | Best |
| Dashboard | Confident AI integration | DeepEval Cloud | None | Manual implementation |
| Open Source | Yes | Yes (core) | Yes | N/A |
| Ground Truth Required | Optional | Optional | Optional | Depends on design |
Major Failure Pattern Analysis
Failure Pattern 1: Wrong Chunks Retrieved (Retrieval Failure)
The most fundamental and most common failure. Irrelevant document chunks are retrieved for the user's question.
Root Cause Analysis:
- Chunking strategy issues: context is split when using fixed-length instead of semantic boundaries
- Domain mismatch in embedding model
- Lack of metadata filtering
Example:
Question: "What was the Q4 2024 revenue?"
Retrieved chunk: "Q4 2023 revenue recorded $150 million..."
→ Document from wrong year retrieved (missing metadata filter)
Expected chunk: "Q4 2024 revenue was $200 million, 33% YoY growth..."
Solution:
# Retrieval with metadata filters
results = vectorstore.similarity_search(
query="Q4 revenue",
filter={"year": 2024, "quarter": "Q4"},
k=5
)
# Apply Semantic Chunking
from langchain.text_splitter import SemanticChunker
splitter = SemanticChunker(
embeddings=embeddings,
breakpoint_threshold_type="percentile",
breakpoint_threshold_amount=90,
)
chunks = splitter.split_documents(documents)
Failure Pattern 2: Context Window Overflow
Too many documents are retrieved, exceeding the context window, or conversely, too few are retrieved, resulting in insufficient information.
Too many documents:
- Token limit exceeded, causing truncation
- Noisy documents distract the LLM's attention
- Increased cost
Too few documents:
- Insufficient information for answering
- Incomplete answers generated
# Adaptive K selection strategy
def adaptive_retrieval(query, retriever, min_k=3, max_k=10, threshold=0.7):
"""Dynamically adjust K based on similarity threshold"""
results = retriever.similarity_search_with_score(query, k=max_k)
filtered = [
(doc, score) for doc, score in results
if score >= threshold
]
if len(filtered) < min_k:
return [doc for doc, _ in results[:min_k]]
return [doc for doc, _ in filtered]
Failure Pattern 3: Hallucination Despite Correct Retrieval
The LLM generates information not present in the context even when accurate documents were retrieved. This is one of the most dangerous failure patterns in RAG.
Root Cause Analysis:
- LLM's pre-trained knowledge conflicts with the context
- Insufficient instruction to "use only the context" in the prompt
- Context contains only partial information, and the LLM fills in the rest
# Enhanced prompt to prevent hallucination
ANTI_HALLUCINATION_PROMPT = """You are an assistant that answers based ONLY on the given context.
Rules:
1. Use ONLY information present in the context.
2. If information is not in the context, respond: "This information is not available in the provided documents."
3. Do not speculate or use prior knowledge.
4. Cite sources at the end of each statement as [Source: Doc N].
Context:
{context}
Question: {question}
Answer:"""
Failure Pattern 4: Lost-in-the-Middle Problem
Discovered in a 2023 Stanford study, this phenomenon shows that LLMs fail to effectively utilize information located in the middle of long contexts.
Symptoms:
- Information at the beginning and end of the context is well-utilized
- Information in the middle is ignored or missed
- Worsens with more retrieved documents
# Lost-in-the-Middle mitigation: place important documents at the edges
def reorder_for_lost_in_middle(documents, scores):
"""Place most relevant documents at the beginning and end"""
sorted_docs = sorted(
zip(documents, scores), key=lambda x: x[1], reverse=True
)
reordered = []
for i, (doc, score) in enumerate(sorted_docs):
if i % 2 == 0:
reordered.insert(0, doc) # Insert at front
else:
reordered.append(doc) # Append at end
return reordered
Failure Pattern 5: Embedding Model Mismatch
The query distribution and document distribution differ, causing semantic similarity to not be properly reflected in the embedding space.
Root Cause Analysis:
- General-purpose embedding model used for specialized domain documents
- Difference between query style (short questions) and document style (long explanations)
- Using English-only embedding model for multilingual documents
# Mitigate mismatch by adding instruction prefix to queries
# (Using Instructor-family embedding models)
from InstructorEmbedding import INSTRUCTOR
model = INSTRUCTOR("hkunlp/instructor-xl")
# Query embedding
query_embedding = model.encode(
[["Represent the question for retrieving supporting documents:", query]]
)
# Document embedding
doc_embedding = model.encode(
[["Represent the technical document for retrieval:", document]]
)
Failure Pattern 6: Stale Knowledge Base
The knowledge base documents do not reflect the latest information, causing answers to be out of touch with reality.
Solution Strategy:
# Knowledge base freshness management system
class KnowledgeBaseFreshnessManager:
def __init__(self, vectorstore, max_age_days=30):
self.vectorstore = vectorstore
self.max_age_days = max_age_days
def check_staleness(self):
"""Detect stale documents"""
cutoff = datetime.now() - timedelta(days=self.max_age_days)
stale_docs = self.vectorstore.query(
filter={"updated_at": {"$lt": cutoff.isoformat()}}
)
return stale_docs
def incremental_update(self, new_documents):
"""Incremental update: re-embed only changed documents"""
for doc in new_documents:
existing = self.vectorstore.get(
filter={"source_id": doc.metadata["source_id"]}
)
if existing and self._content_changed(existing, doc):
self.vectorstore.delete(ids=[existing.id])
self.vectorstore.add_documents([doc])
elif not existing:
self.vectorstore.add_documents([doc])
def add_temporal_boost(self, results, recency_weight=0.1):
"""Give bonus score to recent documents"""
now = datetime.now()
boosted = []
for doc, score in results:
age_days = (now - doc.metadata["updated_at"]).days
recency_score = max(0, 1 - age_days / 365)
final_score = score + recency_weight * recency_score
boosted.append((doc, final_score))
return sorted(boosted, key=lambda x: x[1], reverse=True)
Failure Pattern Timeline and Severity
| Failure Pattern | Frequency | User Impact | Diagnostic Difficulty | Fix Difficulty |
|---|---|---|---|---|
| Wrong chunk retrieval | Very High | High | Medium | Medium |
| Context window overflow | High | Medium | Low | Low |
| Correct retrieval + hallucination | Medium | Very High | High | High |
| Lost-in-the-Middle | Medium | Medium | High | Medium |
| Embedding mismatch | Medium | High | High | High |
| Stale knowledge base | High | High | Low | Medium |
Systematic Debugging Workflow
Follow this workflow to efficiently diagnose quality issues in RAG systems.
Step 1: Reproduce and Classify the Problem
def classify_failure(question, retrieved_docs, generated_answer, ground_truth):
"""Systematically classify RAG failures"""
# Step 1: Check retrieval quality
retrieval_recall = calculate_recall(retrieved_docs, ground_truth_docs)
if retrieval_recall < 0.5:
return "RETRIEVAL_FAILURE"
# Step 2: Check context relevancy
context_relevancy = evaluate_context_relevancy(question, retrieved_docs)
if context_relevancy < 0.5:
return "CONTEXT_NOISE"
# Step 3: Check answer faithfulness
faithfulness = evaluate_faithfulness(generated_answer, retrieved_docs)
if faithfulness < 0.7:
return "HALLUCINATION"
# Step 4: Check answer correctness
correctness = evaluate_correctness(generated_answer, ground_truth)
if correctness < 0.7:
return "GENERATION_QUALITY"
return "ACCEPTABLE"
Step 2: Deep Dive into Each Component
# Retrieval stage debugging
def debug_retrieval(query, vectorstore, k=10):
results = vectorstore.similarity_search_with_score(query, k=k)
print(f"Query: {query}")
print(f"{'='*60}")
for i, (doc, score) in enumerate(results):
print(f"\n[{i+1}] Score: {score:.4f}")
print(f"Source: {doc.metadata.get('source', 'unknown')}")
print(f"Content: {doc.page_content[:200]}...")
print(f"Metadata: {doc.metadata}")
# Query embedding analysis
query_embedding = embeddings.embed_query(query)
print(f"\nQuery embedding norm: {np.linalg.norm(query_embedding):.4f}")
print(f"Query embedding dim: {len(query_embedding)}")
return results
Step 3: A/B Testing and Iterative Improvement
# RAG configuration A/B testing framework
class RAGABTest:
def __init__(self, test_queries, ground_truths):
self.test_queries = test_queries
self.ground_truths = ground_truths
def run_experiment(self, config_a, config_b, metrics):
results_a = self._evaluate_config(config_a, metrics)
results_b = self._evaluate_config(config_b, metrics)
comparison = {}
for metric_name in metrics:
score_a = np.mean(results_a[metric_name])
score_b = np.mean(results_b[metric_name])
improvement = (score_b - score_a) / score_a * 100
comparison[metric_name] = {
"config_a": score_a,
"config_b": score_b,
"improvement_pct": improvement,
}
return comparison
# Usage example
ab_test = RAGABTest(test_queries, ground_truths)
result = ab_test.run_experiment(
config_a={"chunk_size": 512, "k": 5, "model": "gpt-4o-mini"},
config_b={"chunk_size": 1024, "k": 3, "model": "gpt-4o"},
metrics=["faithfulness", "answer_relevancy", "recall"]
)
Practical Recommendations
Chunking Strategy Selection Guide
Chunking strategies by document type:
1. Technical documentation / API docs
→ Markdown header-based splitting + small chunks (256-512 tokens)
2. Legal/regulatory documents
→ Article/clause-based splitting + hierarchical indexing
3. Conversation logs / FAQ
→ Question-answer pair-based splitting
4. Academic papers
→ Section-based splitting + separate indexing for abstract/conclusion
5. General text
→ Semantic Chunking (meaning-based splitting)
Production Monitoring Checklist
- Daily Monitoring: Retrieval hit rate, average similarity scores, answer length distribution
- Weekly Monitoring: User feedback (thumbs up/down) trends, hallucination rate sampling
- Monthly Monitoring: Full RAGAS evaluation against test set, embedding drift analysis
# Production monitoring dashboard metrics
monitoring_metrics = {
"retrieval": {
"avg_similarity_score": 0.82,
"hit_rate": 0.94,
"avg_retrieved_docs": 4.2,
"empty_retrieval_rate": 0.02,
},
"generation": {
"avg_faithfulness": 0.89,
"avg_answer_length": 245,
"refusal_rate": 0.05,
"avg_latency_ms": 1200,
},
"user_feedback": {
"thumbs_up_rate": 0.78,
"escalation_rate": 0.08,
}
}
FAQ
Q1: Is ground truth data always necessary for RAG evaluation?
No. Metrics like RAGAS faithfulness and context relevancy can be measured without ground truth. However, metrics like Answer Correctness and Recall@K do require ground truth. It is recommended to start with ground-truth-free metrics initially and gradually build a golden dataset.
Q2: Should the evaluation LLM be the same as the generation LLM?
Generally, using a different model is recommended. Using the same model can introduce bias. For example, evaluating GPT-4o-generated answers with GPT-4o can create self-evaluation bias. Cross-using different model families like Claude provides more objective evaluation.
Q3: Should I improve retrieval or generation first?
In most cases, improving retrieval first is more effective. The "garbage in, garbage out" principle applies. If retrieval quality is low, even the best LLM will have limited answer quality. Once Retrieval Recall reaches 0.8 or above, it is recommended to shift focus to generation-side optimization.
Q4: What is the appropriate chunk size?
There is no single answer, but general guidelines are:
- 256-512 tokens: Suitable for short factual QA
- 512-1024 tokens: Suitable for general questions requiring explanations
- 1024-2048 tokens: Suitable for questions requiring complex analysis
If chunks are too small, context is lost; if too large, noise increases. The optimal size must be determined experimentally.
Q5: Should a reranker always be used?
Rerankers are highly effective at improving the precision of initial search results, but they add latency and cost. They are especially recommended when:
- Top-ranked retrieval results frequently contain irrelevant documents
- Queries are complex or ambiguous
- Retrieval Precision is low
Q6: What should be specifically considered for multilingual RAG systems?
In multilingual environments, consider the following:
- Use multilingual embedding models (e.g.,
multilingual-e5-large) - Differentiate chunking strategies by language (morpheme-based for Korean, segmentation-based for Japanese)
- Test cross-language retrieval (e.g., Korean question retrieving English documents)
References
- RAGAS Official Documentation and Paper: https://docs.ragas.io/ - Official documentation for the RAG evaluation framework
- "Lost in the Middle" Paper: https://arxiv.org/abs/2307.03172 - Liu et al., 2023. Analysis of how LLMs miss information in the middle of long contexts
- "Retrieval-Augmented Generation for Large Language Models: A Survey": https://arxiv.org/abs/2312.10997 - Gao et al., 2024. Comprehensive survey of RAG techniques
- DeepEval Official Documentation: https://docs.confident-ai.com/ - LLM evaluation framework
- LlamaIndex Evaluation Guide: https://docs.llamaindex.ai/en/stable/module_guides/evaluating/ - LlamaIndex built-in evaluation module
- "Benchmarking Large Language Models in Retrieval-Augmented Generation": https://arxiv.org/abs/2309.01431 - Chen et al., 2023. RAG benchmarking research
- MTEB Benchmark: https://huggingface.co/spaces/mteb/leaderboard - Embedding model performance comparison leaderboard
Conclusion: Key Takeaways for Practice
Managing RAG system quality cannot be solved by simply swapping out the LLM. The key is to adopt a systematic evaluation framework, understand failure modes for each component, and continuously improve through ongoing monitoring.
The three most important action items:
- Start by building an evaluation dataset: Begin with a minimum of 50-100 question-answer pairs and continuously expand.
- Optimize retrieval first: Retrieval quality determines the upper bound of the entire pipeline.
- Integrate automated evaluation pipelines into CI/CD: Automatically verify that every change does not cause quality regression.
By implementing these three practices, you can make RAG system quality predictable and continuously improvable.
Quiz
Q1: What is the main topic covered in "RAG Quality Evaluation and Failure Pattern Analysis:
Diagnosing and Improving Retrieval-Augmented Generation"?
A systematic guide to evaluating RAG (Retrieval-Augmented Generation) system quality and analyzing common failure patterns.
Q2: What is Understanding RAG Pipeline Components?
A RAG system consists of
three core components. 1. Retriever The retriever takes a user query and fetches relevant document
chunks from a vector database or search engine.
Q3: Explain the core concept of Key Evaluation Metrics.
Metrics for measuring RAG system quality fall into two categories: retrieval performance metrics
and generation performance metrics. Retrieval Performance Metrics Generation Performance Metrics
Q4: What are the key aspects of Evaluation Framework Comparison?
RAGAS (Retrieval Augmented Generation Assessment) RAGAS is an open-source framework specialized
for RAG system evaluation, supporting automated evaluation using LLMs. DeepEval DeepEval is a
framework that enables unit-test-style evaluation of LLM applications.
Q5: How does Major Failure Pattern Analysis work?
Failure Pattern 1: Wrong Chunks Retrieved (Retrieval Failure) The most fundamental and most common
failure. Irrelevant document chunks are retrieved for the user's question.