본 콘텐츠의 이미지 및 내용은 AI로 생성되었습니다.
본 콘텐츠의 이미지 및 내용을 무단으로 복제, 배포, 수정하여 사용할 경우 저작권법에 의해 법적 제재를 받을 수 있습니다.
이미지 로딩 중...
AI Generated
2025. 12. 26. · 3 Views
RAG 평가 메트릭 완벽 가이드
RAG 시스템의 성능을 정확하게 평가하는 방법을 배웁니다. Retrieval과 Generation 메트릭을 이해하고, RAGAS 프레임워크를 활용한 자동 평가 시스템을 구축하는 실전 가이드입니다.
목차
- Retrieval Metrics (Precision, Recall, MRR)
- Generation Metrics (Faithfulness, Relevance)
- RAGAS 프레임워크
- 실습: RAG 평가 파이프라인
- 실습: 자동 평가 시스템
1. Retrieval Metrics (Precision, Recall, MRR)
김개발 씨가 RAG 시스템을 운영한 지 한 달이 지났습니다. 어느 날 팀장님이 물어봅니다.
"우리 RAG 시스템, 제대로 작동하고 있나요?" 김개발 씨는 잠시 말문이 막혔습니다. 그저 잘 작동하는 것 같다고만 생각했지, 정확히 얼마나 잘 작동하는지는 몰랐습니다.
Retrieval Metrics는 RAG 시스템이 얼마나 정확하게 관련 문서를 찾아내는지 측정하는 지표입니다. 마치 도서관 사서가 책을 얼마나 정확하게 찾아주는지 평가하는 것과 같습니다.
Precision, Recall, MRR 같은 지표를 통해 검색 품질을 수치로 확인할 수 있습니다.
다음 코드를 살펴봅시다.
from typing import List, Set
def calculate_precision(retrieved: List[str], relevant: Set[str]) -> float:
# 검색된 문서 중 실제로 관련 있는 문서의 비율
relevant_retrieved = set(retrieved) & relevant
return len(relevant_retrieved) / len(retrieved) if retrieved else 0.0
def calculate_recall(retrieved: List[str], relevant: Set[str]) -> float:
# 관련 있는 문서 중 실제로 검색된 문서의 비율
relevant_retrieved = set(retrieved) & relevant
return len(relevant_retrieved) / len(relevant) if relevant else 0.0
def calculate_mrr(retrieved_lists: List[List[str]], relevant_sets: List[Set[str]]) -> float:
# Mean Reciprocal Rank: 첫 번째 관련 문서가 나타나는 순위의 역수 평균
reciprocal_ranks = []
for retrieved, relevant in zip(retrieved_lists, relevant_sets):
for rank, doc in enumerate(retrieved, 1):
if doc in relevant:
reciprocal_ranks.append(1.0 / rank)
break
else:
reciprocal_ranks.append(0.0)
return sum(reciprocal_ranks) / len(reciprocal_ranks) if reciprocal_ranks else 0.0
김개발 씨는 선배 개발자 박시니어 씨에게 도움을 청했습니다. "RAG 시스템이 잘 작동하는지 어떻게 확인하나요?" 박시니어 씨가 웃으며 답했습니다.
"그럴 때 바로 평가 메트릭이 필요하죠. 특히 검색 단계의 성능을 측정하는 Retrieval Metrics부터 알아야 해요." Retrieval Metrics란 무엇일까요? 쉽게 비유하자면, Retrieval Metrics는 도서관 사서의 성적표와 같습니다.
학생이 "머신러닝 입문서를 찾아주세요"라고 요청했을 때, 사서가 추천한 책들이 얼마나 적절했는지 평가하는 것입니다. 어떤 사서는 딱 필요한 책만 골라주고, 어떤 사서는 관련 없는 책까지 잔뜩 가져오기도 합니다.
RAG 시스템도 마찬가지입니다. 사용자의 질문에 대해 벡터 데이터베이스에서 문서를 검색할 때, 얼마나 정확하게 관련된 문서를 찾아내는지 측정해야 합니다.
왜 검색 성능을 측정해야 할까요? 검색 메트릭이 없던 시절에는 어땠을까요? 개발자들은 "음, 이 정도면 괜찮은 것 같은데?"라는 주관적인 느낌으로 시스템을 평가했습니다.
문제는 사용자가 늘어나면서 불만이 쌓이기 시작했다는 점입니다. "왜 이런 엉뚱한 답변을 주나요?"라는 피드백이 늘어났지만, 정확히 어디가 문제인지 파악하기 어려웠습니다.
더 큰 문제는 개선 작업을 해도 실제로 나아졌는지 알 수 없다는 점이었습니다. 임베딩 모델을 바꾸고, 청크 크기를 조정해도, 그저 "조금 나아진 것 같다"는 느낌만 있을 뿐이었습니다.
세 가지 핵심 메트릭 바로 이런 문제를 해결하기 위해 Precision, Recall, MRR이라는 메트릭이 사용됩니다. Precision은 검색 결과의 정확도를 나타냅니다.
"검색된 문서 중에서 실제로 관련 있는 문서의 비율"입니다. 만약 10개 문서를 검색했는데 그중 7개가 관련 있다면, Precision은 0.7입니다.
Recall은 재현율을 나타냅니다. "찾아야 할 문서 중에서 실제로 찾은 문서의 비율"입니다.
관련 문서가 총 10개인데 그중 7개를 찾았다면, Recall은 0.7입니다. MRR(Mean Reciprocal Rank)은 순위를 고려한 지표입니다.
첫 번째 관련 문서가 몇 번째 순위에 나타나는지를 측정합니다. 1등에 나타나면 1.0, 2등이면 0.5, 3등이면 0.33의 점수를 받습니다.
코드로 이해하기 위의 코드를 한 줄씩 살펴보겠습니다. calculate_precision 함수는 검색된 문서와 실제 관련 있는 문서의 교집합을 구합니다.
그리고 이를 검색된 전체 문서 수로 나눕니다. 이렇게 하면 "검색 결과가 얼마나 깨끗한가"를 알 수 있습니다.
calculate_recall 함수는 비슷하지만 분모가 다릅니다. 관련 있는 전체 문서 수로 나누기 때문에 "필요한 문서를 얼마나 놓치지 않았는가"를 측정합니다.
calculate_mrr 함수는 조금 더 복잡합니다. 각 검색 결과를 순회하면서 관련 문서가 처음 나타나는 위치를 찾습니다.
그 위치의 역수를 계산하고, 모든 쿼리에 대한 평균을 구합니다. 실무에서는 어떻게 활용할까요? 예를 들어 기술 문서 검색 시스템을 운영한다고 가정해봅시다.
사용자가 "FastAPI 비동기 처리 방법"을 검색했을 때, 시스템이 반환한 5개 문서 중 4개가 실제로 관련 있다면 Precision은 0.8입니다. 하지만 실제로는 관련 문서가 10개나 있었다면 Recall은 0.4밖에 안 됩니다.
이 경우 검색 개수를 늘리거나 검색 알고리즘을 개선해야 한다는 것을 알 수 있습니다. 또한 첫 번째 결과가 가장 관련성이 높은 문서라면 MRR이 1.0이지만, 세 번째에 나타난다면 0.33입니다.
사용자는 보통 상위 결과만 보기 때문에 MRR이 낮으면 사용자 경험이 나빠집니다. 주의해야 할 점 초보 개발자들이 흔히 하는 실수 중 하나는 Precision만 높이려다 Recall을 낮추는 것입니다.
검색 개수를 줄이면 Precision은 올라가지만, 중요한 문서를 놓칠 수 있습니다. 반대로 Recall만 높이려고 너무 많은 문서를 검색하면 Precision이 떨어집니다.
사용자는 관련 없는 문서까지 받게 되어 불편합니다. 따라서 두 지표의 균형을 맞추는 것이 중요합니다.
많은 기업에서 F1 Score(Precision과 Recall의 조화평균)를 함께 사용하는 이유입니다. 정리하며 다시 김개발 씨의 이야기로 돌아가 봅시다.
박시니어 씨의 설명을 들은 김개발 씨는 바로 자신의 시스템에 이 메트릭들을 적용해봤습니다. "아, 우리 시스템은 Precision은 높은데 Recall이 낮았네요!" 이제 개선 방향이 명확해졌습니다.
검색 개수를 늘리고 임베딩 모델을 개선하여 Recall을 높이는 작업을 시작했습니다.
실전 팁
💡 - Precision과 Recall은 트레이드오프 관계입니다. 균형을 맞추세요.
- MRR은 사용자 경험과 직결됩니다. 상위 결과의 품질에 집중하세요.
- 평가를 위해서는 정답 레이블이 필요합니다. 미리 테스트 데이터셋을 준비하세요.
2. Generation Metrics (Faithfulness, Relevance)
검색 메트릭을 도입한 후 김개발 씨는 안심했습니다. "이제 검색은 완벽해!" 하지만 사용자 불만은 계속됐습니다.
"검색은 잘 되는 것 같은데, 답변이 이상해요." 검색만 평가하고 생성된 답변의 품질은 평가하지 않았던 것입니다.
Generation Metrics는 RAG 시스템이 생성한 답변의 품질을 측정하는 지표입니다. Faithfulness는 답변이 검색된 문서에 충실한지, Relevance는 사용자 질문과 관련 있는지를 평가합니다.
검색이 아무리 좋아도 답변이 형편없다면 의미가 없기 때문입니다.
다음 코드를 살펴봅시다.
from typing import List
import openai
def evaluate_faithfulness(context: str, answer: str) -> float:
# LLM을 사용하여 답변이 문맥에 충실한지 평가
prompt = f"""주어진 문맥과 답변을 평가하세요.
답변의 모든 정보가 문맥에서 나왔나요? 1-5점으로 평가하세요.
문맥: {context}
답변: {answer}
점수 (1-5):"""
response = openai.chat.completions.create(
model="gpt-4",
messages=[{"role": "user", "content": prompt}]
)
score = int(response.choices[0].message.content.strip())
return score / 5.0
def evaluate_relevance(question: str, answer: str) -> float:
# 답변이 질문과 얼마나 관련 있는지 평가
prompt = f"""질문과 답변의 관련성을 평가하세요.
답변이 질문에 직접적으로 답하나요? 1-5점으로 평가하세요.
질문: {question}
답변: {answer}
점수 (1-5):"""
response = openai.chat.completions.create(
model="gpt-4",
messages=[{"role": "user", "content": prompt}]
)
score = int(response.choices[0].message.content.strip())
return score / 5.0
박시니어 씨가 김개발 씨의 화면을 보더니 고개를 끄덕였습니다. "검색 메트릭만으로는 부족해요.
생성된 답변의 품질도 평가해야죠." "그게 무슨 말씀이세요?" 김개발 씨가 고개를 갸웃했습니다. "검색이 완벽해도 LLM이 이상한 답변을 만들 수 있거든요.
특히 할루시네이션 문제가 심각하죠." Generation Metrics란 무엇일까요? 쉽게 비유하자면, Generation Metrics는 작문 시험의 채점 기준과 같습니다. 학생에게 참고 자료를 주고 에세이를 쓰게 했을 때, 두 가지를 확인해야 합니다.
첫째, 참고 자료에 있는 내용만 사용했는가? 둘째, 질문에 제대로 답했는가?
RAG 시스템도 마찬가지입니다. 검색된 문서를 바탕으로 답변을 생성할 때, 문서에 없는 내용을 지어내지는 않았는지, 사용자 질문에 제대로 답하고 있는지 확인해야 합니다.
왜 생성 품질을 측정해야 할까요? 생성 메트릭이 없던 시절에는 어땠을까요? 개발자들은 검색만 잘 되면 모든 게 해결될 거라 생각했습니다.
하지만 현실은 달랐습니다. 검색된 문서는 완벽했지만, LLM이 그 문서를 무시하고 자기 마음대로 답변을 만들어내는 경우가 있었습니다.
더 큰 문제는 할루시네이션이었습니다. LLM이 문서에 없는 통계 수치를 지어내거나, 없던 기능을 있다고 설명하는 일이 빈번했습니다.
사용자는 그걸 믿고 잘못된 결정을 내리기도 했습니다. 두 가지 핵심 메트릭 바로 이런 문제를 해결하기 위해 Faithfulness와 Relevance라는 메트릭이 사용됩니다.
Faithfulness는 충실도를 나타냅니다. 답변의 모든 내용이 검색된 문서에서 나온 것인지 확인합니다.
만약 답변에 "FastAPI는 초당 10만 요청을 처리할 수 있습니다"라고 나왔는데, 원본 문서에는 그런 내용이 없다면 Faithfulness가 낮습니다. Relevance는 관련성을 나타냅니다.
답변이 사용자의 질문에 직접적으로 답하고 있는지 확인합니다. 질문이 "FastAPI 설치 방법"인데 답변이 "FastAPI의 역사"에 대해 설명한다면 Relevance가 낮습니다.
코드로 이해하기 위의 코드를 한 줄씩 살펴보겠습니다. evaluate_faithfulness 함수는 LLM을 평가자로 활용합니다.
원본 문맥과 생성된 답변을 함께 제공하고, LLM에게 답변이 문맥에 충실한지 1-5점으로 평가하게 합니다. 이를 0-1 범위로 정규화하여 반환합니다.
evaluate_relevance 함수도 비슷하지만 평가 기준이 다릅니다. 질문과 답변을 제공하고, 답변이 질문에 얼마나 적절히 답하는지 평가합니다.
이처럼 LLM을 평가자로 사용하는 방법을 LLM-as-a-Judge라고 부릅니다. 사람이 일일이 평가하기에는 시간이 너무 오래 걸리기 때문에, LLM에게 평가를 맡기는 것입니다.
실무에서는 어떻게 활용할까요? 예를 들어 의료 정보 검색 시스템을 운영한다고 가정해봅시다. 사용자가 "당뇨병 초기 증상"을 물어봤을 때, 시스템이 관련 논문을 검색해서 답변을 생성합니다.
이때 답변에 "당뇨병 환자의 95%가 체중 감소를 경험합니다"라는 내용이 있는데, 원본 논문에는 "일부 환자가 체중 감소를 경험할 수 있습니다"라고만 나와 있다면 큰 문제입니다. Faithfulness 평가를 통해 이런 과장이나 왜곡을 발견할 수 있습니다.
또한 답변이 "당뇨병은 인슐린 저항성으로 인해 발생합니다"처럼 원인 설명만 하고 초기 증상은 언급하지 않는다면 Relevance가 낮습니다. 이런 경우 프롬프트를 개선하여 질문에 더 직접적으로 답하도록 유도해야 합니다.
주의해야 할 점 초보 개발자들이 흔히 하는 실수 중 하나는 평가 LLM과 생성 LLM을 동일하게 사용하는 것입니다. 같은 모델은 자신의 실수를 잘 찾지 못할 수 있습니다.
또 다른 실수는 평가 프롬프트를 대충 작성하는 것입니다. "좋은지 나쁜지 평가해"라는 식의 모호한 프롬프트로는 일관된 평가를 받을 수 없습니다.
평가 기준을 명확히 제시해야 합니다. 따라서 평가용으로는 더 강력한 모델(예: GPT-4)을 사용하고, 평가 프롬프트에는 구체적인 기준을 상세히 작성하는 것이 좋습니다.
정리하며 다시 김개발 씨의 이야기로 돌아가 봅시다. 박시니어 씨의 조언을 듣고 김개발 씨는 Generation Metrics를 추가했습니다.
"아, 우리 시스템이 자주 문서에 없는 내용을 지어내고 있었네요!" 프롬프트를 개선하여 "반드시 제공된 문맥만 사용하세요"라는 지침을 추가했더니 Faithfulness가 크게 향상됐습니다.
실전 팁
💡 - LLM-as-a-Judge 방식은 편리하지만, 가끔 샘플링하여 사람이 직접 검증하세요.
- 평가 프롬프트에 구체적인 예시를 포함하면 더 일관된 평가를 받을 수 있습니다.
- Faithfulness가 낮다면 프롬프트에 "문서에 없는 내용은 추측하지 마세요"를 추가하세요.
3. RAGAS 프레임워크
김개발 씨는 이제 여러 메트릭을 계산할 수 있게 됐습니다. 하지만 매번 평가 코드를 직접 작성하는 게 번거로웠습니다.
"이런 걸 자동으로 해주는 도구는 없을까?" 바로 그때 박시니어 씨가 RAGAS를 소개해줬습니다.
RAGAS(RAG Assessment)는 RAG 시스템을 종합적으로 평가하는 오픈소스 프레임워크입니다. Faithfulness, Answer Relevancy, Context Precision, Context Recall 등 다양한 메트릭을 자동으로 계산해주고, 평가 파이프라인을 간단하게 구축할 수 있게 해줍니다.
다음 코드를 살펴봅시다.
from ragas import evaluate
from ragas.metrics import faithfulness, answer_relevancy, context_precision, context_recall
from datasets import Dataset
# 평가 데이터셋 준비
eval_data = {
"question": ["FastAPI 설치 방법은?", "비동기 처리는 어떻게?"],
"answer": ["pip install fastapi로 설치합니다.", "async/await를 사용합니다."],
"contexts": [
["FastAPI는 pip install fastapi 명령으로 설치할 수 있습니다."],
["FastAPI는 async와 await 키워드로 비동기 처리를 지원합니다."]
],
"ground_truth": ["pip install fastapi", "async/await 사용"]
}
dataset = Dataset.from_dict(eval_data)
# RAGAS로 평가 실행
result = evaluate(
dataset,
metrics=[faithfulness, answer_relevancy, context_precision, context_recall]
)
print(f"Faithfulness: {result['faithfulness']:.3f}")
print(f"Answer Relevancy: {result['answer_relevancy']:.3f}")
print(f"Context Precision: {result['context_precision']:.3f}")
print(f"Context Recall: {result['context_recall']:.3f}")
"매번 평가 코드를 짜는 게 너무 힘들어요." 김개발 씨가 한숨을 쉬었습니다. 박시니어 씨가 웃으며 대답했습니다.
"그럴 줄 알았어요. 그래서 RAGAS 같은 프레임워크가 나온 거죠." RAGAS란 무엇일까요? 쉽게 비유하자면, RAGAS는 자동차 종합 검진 시스템과 같습니다.
자동차를 정비소에 가져가면 엔진, 브레이크, 타이어, 배터리 등을 한 번에 점검해주는 것처럼, RAGAS는 RAG 시스템의 모든 측면을 종합적으로 평가해줍니다. 일일이 각 부품을 따로 점검할 필요 없이, 한 번의 명령으로 전체 성능 리포트를 받을 수 있습니다.
어느 부분이 좋고 어느 부분이 나쁜지 한눈에 파악할 수 있습니다. 왜 RAGAS가 필요할까요? RAGAS가 없던 시절에는 어땠을까요?
개발자들은 각 메트릭을 따로따로 계산해야 했습니다. Faithfulness 계산 코드를 짜고, Relevance 계산 코드를 짜고, Precision과 Recall도 따로 짜야 했습니다.
코드가 길어지고 관리가 어려워졌습니다. 더 큰 문제는 일관성이었습니다.
팀원마다 조금씩 다른 방식으로 메트릭을 계산하면, 결과를 비교하기 어려웠습니다. "내 실험에서는 Faithfulness가 0.8인데 당신 실험에서는 0.6이네요?" 같은 혼란이 생겼습니다.
RAGAS의 핵심 메트릭 바로 이런 문제를 해결하기 위해 RAGAS가 등장했습니다. RAGAS는 네 가지 핵심 메트릭을 제공합니다.
Faithfulness는 앞서 배운 것처럼 답변이 문맥에 충실한지 측정합니다. Answer Relevancy는 답변이 질문과 관련 있는지 평가합니다.
Context Precision은 검색된 문서들이 얼마나 정확한지 측정합니다. 상위에 관련 문서가 많이 있을수록 점수가 높습니다.
Context Recall은 답변을 만드는 데 필요한 모든 정보가 검색된 문맥에 포함되어 있는지 확인합니다. 이 네 가지 메트릭을 조합하면 RAG 시스템의 전체 성능을 입체적으로 파악할 수 있습니다.
코드로 이해하기 위의 코드를 한 줄씩 살펴보겠습니다. 먼저 평가 데이터셋을 준비합니다.
question은 사용자 질문, answer는 RAG 시스템이 생성한 답변, contexts는 검색된 문서들, ground_truth는 정답 답변입니다. 이 데이터를 Dataset 객체로 변환한 후, evaluate 함수에 전달합니다.
metrics 파라미터에 평가하고 싶은 메트릭 리스트를 넣으면 됩니다. RAGAS가 자동으로 LLM을 호출하여 각 메트릭을 계산하고, 결과를 딕셔너리 형태로 반환합니다.
이제 result['faithfulness']처럼 간단하게 점수를 확인할 수 있습니다. 실무에서는 어떻게 활용할까요? 예를 들어 고객 지원 챗봇을 운영한다고 가정해봅시다.
매주 새로운 제품 매뉴얼이 추가되고, RAG 시스템을 계속 개선하고 있습니다. 이때 RAGAS를 CI/CD 파이프라인에 통합하면 자동으로 성능을 모니터링할 수 있습니다.
새로운 버전을 배포하기 전에 RAGAS 평가를 실행하고, 점수가 떨어지면 배포를 중단하는 식입니다. 많은 스타트업에서 이런 방식으로 RAG 시스템의 품질을 유지하고 있습니다.
개발자가 임베딩 모델을 바꾸거나 청크 크기를 조정할 때마다 RAGAS가 자동으로 성능 변화를 감지해줍니다. 주의해야 할 점 초보 개발자들이 흔히 하는 실수 중 하나는 ground_truth 없이 평가하려는 것입니다.
RAGAS의 일부 메트릭(특히 Context Recall)은 정답 데이터가 필요합니다. 또 다른 실수는 너무 적은 샘플로 평가하는 것입니다.
2-3개 질문으로는 의미 있는 평가를 할 수 없습니다. 최소 50-100개 이상의 테스트 셋을 준비해야 신뢰할 수 있는 결과를 얻습니다.
따라서 평가용 데이터셋을 미리 충분히 준비하고, 정답 레이블도 함께 작성해두는 것이 중요합니다. 정리하며 다시 김개발 씨의 이야기로 돌아가 봅시다.
RAGAS를 도입한 김개발 씨는 이제 한 줄의 명령으로 전체 시스템을 평가할 수 있게 됐습니다. "와, 이렇게 간단할 수가!" 주간 회의에서 팀장님께 RAGAS 리포트를 보여드렸더니, 팀장님도 만족스러워하셨습니다.
"이제 우리 시스템이 잘 작동하는지 한눈에 알 수 있네요!"
실전 팁
💡 - RAGAS는 LLM API를 많이 호출하므로 비용을 고려하세요. 작은 샘플로 테스트 후 확장하세요.
- CI/CD에 통합하여 자동으로 성능 회귀를 감지하세요.
- 각 메트릭의 의미를 정확히 이해하고 해석하세요. 단순히 점수만 보지 말고 왜 그 점수가 나왔는지 분석하세요.
4. 실습: RAG 평가 파이프라인
이론은 충분히 배웠습니다. 이제 김개발 씨는 실제로 작동하는 RAG 평가 파이프라인을 구축하고 싶었습니다.
"처음부터 끝까지 전체 과정을 한 번 해볼까?" 박시니어 씨가 옆에서 함께 도와주기로 했습니다.
RAG 평가 파이프라인은 데이터 준비, RAG 시스템 실행, 결과 수집, 메트릭 계산, 리포트 생성의 전 과정을 자동화한 시스템입니다. 한 번 구축해두면 언제든지 시스템 성능을 빠르게 평가하고, 개선 사항을 추적할 수 있습니다.
다음 코드를 살펴봅시다.
from ragas import evaluate
from ragas.metrics import faithfulness, answer_relevancy
from langchain_community.vectorstores import FAISS
from langchain_openai import OpenAIEmbeddings, ChatOpenAI
from langchain.chains import RetrievalQA
from datasets import Dataset
import json
# 1. RAG 시스템 초기화
embeddings = OpenAIEmbeddings()
vectorstore = FAISS.load_local("./my_index", embeddings, allow_dangerous_deserialization=True)
llm = ChatOpenAI(model="gpt-3.5-turbo", temperature=0)
qa_chain = RetrievalQA.from_chain_type(llm=llm, retriever=vectorstore.as_retriever(search_kwargs={"k": 3}))
# 2. 테스트 질문으로 RAG 실행 및 결과 수집
test_questions = ["FastAPI 설치 방법은?", "비동기 처리는 어떻게 하나요?"]
results = []
for question in test_questions:
# RAG 시스템 실행
answer = qa_chain.run(question)
# 검색된 문서 가져오기
docs = vectorstore.similarity_search(question, k=3)
contexts = [doc.page_content for doc in docs]
results.append({
"question": question,
"answer": answer,
"contexts": contexts,
"ground_truth": "정답 데이터" # 실제로는 미리 준비된 정답 사용
})
# 3. RAGAS로 평가
dataset = Dataset.from_dict({
"question": [r["question"] for r in results],
"answer": [r["answer"] for r in results],
"contexts": [r["contexts"] for r in results],
"ground_truth": [r["ground_truth"] for r in results]
})
eval_result = evaluate(dataset, metrics=[faithfulness, answer_relevancy])
# 4. 결과 저장
with open("evaluation_report.json", "w", encoding="utf-8") as f:
json.dump({"metrics": eval_result, "details": results}, f, ensure_ascii=False, indent=2)
print("평가 완료! evaluation_report.json 확인하세요.")
"자, 이제 전체 과정을 처음부터 끝까지 해볼까요?" 박시니어 씨가 화이트보드에 그림을 그리기 시작했습니다. 김개발 씨는 노트북을 펼치며 고개를 끄덕였습니다.
"네, 한 번에 다 이해하고 싶어요!" 평가 파이프라인이란 무엇일까요? 쉽게 비유하자면, 평가 파이프라인은 공장의 품질 검사 라인과 같습니다. 제품이 컨베이어 벨트를 타고 이동하면서 크기 측정, 무게 측정, 외관 검사, 기능 테스트를 차례로 거치는 것처럼, RAG 시스템의 출력도 여러 평가 단계를 자동으로 거치게 됩니다.
한 번 라인을 구축해두면, 버튼 하나로 전체 검사가 진행됩니다. 사람이 일일이 개입할 필요가 없습니다.
파이프라인의 필요성 평가 파이프라인이 없던 시절에는 어땠을까요? 개발자들은 매번 수작업으로 평가했습니다.
질문을 하나씩 입력하고, 답변을 복사해서 엑셀에 붙여넣고, 메트릭을 계산기로 두드려 계산했습니다. 하루 종일 해도 몇십 개밖에 평가하지 못했습니다.
더 큰 문제는 실수였습니다. 피곤해지면 같은 질문을 두 번 평가하거나, 계산을 잘못하기도 했습니다.
일주일 전 평가 결과와 오늘 평가 결과를 비교하려고 하면, 어디에 저장했는지 찾을 수조차 없었습니다. 4단계 파이프라인 바로 이런 문제를 해결하기 위해 자동화된 평가 파이프라인이 필요합니다.
첫 번째 단계는 RAG 시스템 초기화입니다. 임베딩 모델, 벡터 데이터베이스, LLM을 로드하고 체인을 구성합니다.
이 부분은 실제 서비스와 동일하게 설정해야 정확한 평가가 가능합니다. 두 번째 단계는 테스트 실행 및 결과 수집입니다.
미리 준비한 테스트 질문들을 RAG 시스템에 입력하고, 답변과 함께 검색된 문서들도 모두 저장합니다. 이 데이터가 평가의 원자료가 됩니다.
세 번째 단계는 메트릭 계산입니다. RAGAS를 사용하여 수집된 데이터를 평가합니다.
여러 메트릭을 동시에 계산하여 다각도로 성능을 분석합니다. 네 번째 단계는 리포트 생성입니다.
평가 결과를 JSON이나 CSV 파일로 저장하여, 나중에 다시 확인하거나 다른 버전과 비교할 수 있게 합니다. 코드로 이해하기 위의 코드를 한 단계씩 살펴보겠습니다.
먼저 FAISS 벡터스토어를 로드하고 OpenAI LLM과 연결하여 RetrievalQA 체인을 만듭니다. 이때 search_kwargs={"k": 3}으로 설정하여 상위 3개 문서를 검색하도록 합니다.
다음으로 테스트 질문 리스트를 순회하면서 각 질문에 대해 RAG 시스템을 실행합니다. qa_chain.run(question)으로 답변을 얻고, similarity_search로 검색된 문서도 가져옵니다.
이 모든 정보를 results 리스트에 저장합니다. 수집된 결과를 RAGAS가 요구하는 형식으로 변환합니다.
Dataset.from_dict를 사용하여 각 필드를 리스트로 분리합니다. 마지막으로 evaluate 함수로 평가하고, 결과를 JSON 파일로 저장합니다.
이렇게 하면 언제든지 결과를 다시 확인하거나, Git에 커밋하여 버전별 성능 변화를 추적할 수 있습니다. 실무에서는 어떻게 활용할까요? 예를 들어 법률 자문 AI 서비스를 운영한다고 가정해봅시다.
매주 새로운 판례와 법령이 추가되고, RAG 시스템도 계속 업데이트됩니다. 이때 평가 파이프라인을 GitHub Actions에 연결하면, 코드가 푸시될 때마다 자동으로 평가가 실행됩니다.
성능이 이전보다 떨어지면 Pull Request에 경고가 표시되어, 문제를 즉시 발견할 수 있습니다. 또한 매주 월요일 아침마다 크론잡으로 평가를 실행하여, 주간 성능 리포트를 이메일로 받을 수도 있습니다.
팀장님께서는 엑셀 차트로 Faithfulness 추이를 확인하며 개선 여부를 모니터링합니다. 주의해야 할 점 초보 개발자들이 흔히 하는 실수 중 하나는 테스트 데이터를 학습 데이터와 섞는 것입니다.
평가용 질문이 이미 벡터 데이터베이스에 들어있다면, 평가 결과가 부풀려져 나옵니다. 또 다른 실수는 너무 오래 걸리는 파이프라인을 만드는 것입니다.
1000개 질문을 평가하는 데 3시간이 걸린다면, 개발 속도가 느려집니다. 적절한 샘플 크기를 찾아야 합니다.
따라서 빠른 피드백을 위한 작은 테스트 셋(10-20개)과, 정확한 평가를 위한 큰 테스트 셋(100-200개)을 분리하여 운영하는 것이 좋습니다. 정리하며 다시 김개발 씨의 이야기로 돌아가 봅시다.
박시니어 씨와 함께 파이프라인을 구축한 김개발 씨는 이제 자신감이 생겼습니다. "이제 언제든지 시스템 성능을 확인할 수 있어요!" 다음 날부터 김개발 씨는 매일 아침 평가 파이프라인을 실행하여, 어제와 오늘의 성능을 비교하기 시작했습니다.
실전 팁
💡 - 테스트 데이터는 별도 파일로 관리하고 Git에 커밋하세요. 버전 관리가 중요합니다.
- 평가 결과도 JSON으로 저장하여 시간에 따른 성능 변화를 추적하세요.
- 처음에는 작은 테스트 셋으로 빠르게 반복하고, 나중에 크기를 키우세요.
5. 실습: 자동 평가 시스템
평가 파이프라인을 구축한 후, 김개발 씨는 한 가지 더 욕심이 생겼습니다. "매번 수동으로 실행하는 게 귀찮은데, 아예 자동으로 돌아가게 할 수 없을까?" 박시니어 씨가 웃으며 말했습니다.
"당연하죠! CI/CD에 통합하면 됩니다."
자동 평가 시스템은 코드 변경, 스케줄, 트리거 이벤트에 따라 자동으로 RAG 시스템을 평가하고 결과를 리포팅하는 시스템입니다. GitHub Actions, Airflow, 크론잡 등을 활용하여 지속적으로 품질을 모니터링하고, 성능 저하를 즉시 감지할 수 있습니다.
다음 코드를 살펴봅시다.
# .github/workflows/rag-evaluation.yml
name: RAG Evaluation
on:
push:
branches: [main]
schedule:
- cron: '0 9 * * 1' # 매주 월요일 오전 9시
workflow_dispatch: # 수동 실행 가능
jobs:
evaluate:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: '3.10'
- name: Install dependencies
run: |
pip install -r requirements.txt
- name: Run RAG evaluation
env:
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
run: |
python evaluate_rag.py
- name: Upload results
uses: actions/upload-artifact@v3
with:
name: evaluation-results
path: evaluation_report.json
- name: Check performance threshold
run: |
python check_threshold.py --min-faithfulness 0.7 --min-relevancy 0.75
- name: Comment PR
if: github.event_name == 'pull_request'
uses: actions/github-script@v6
with:
script: |
const fs = require('fs');
const results = JSON.parse(fs.readFileSync('evaluation_report.json'));
github.rest.issues.createComment({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
body: `## RAG Evaluation Results\n\nFaithfulness: ${results.metrics.faithfulness.toFixed(3)}\nRelevancy: ${results.metrics.answer_relevancy.toFixed(3)}`
});
"CI/CD에 통합한다는 게 무슨 뜻이에요?" 김개발 씨가 물었습니다. 박시니어 씨가 화면을 가리키며 설명했습니다.
"코드를 푸시하면 자동으로 테스트가 돌아가는 것처럼, RAG 평가도 자동으로 실행되게 하는 거예요." 자동 평가 시스템이란 무엇일까요? 쉽게 비유하자면, 자동 평가 시스템은 건물의 자동 방재 시스템과 같습니다. 화재 감지기가 24시간 연기를 감시하다가 이상이 생기면 즉시 경보를 울리는 것처럼, 자동 평가 시스템도 RAG 성능을 지속적으로 모니터링하다가 문제가 생기면 알림을 보냅니다.
사람이 매번 확인할 필요 없이, 시스템이 알아서 돌아갑니다. 개발자는 문제가 생겼을 때만 개입하면 됩니다.
왜 자동화가 필요할까요? 자동 평가 시스템이 없던 시절에는 어땠을까요? 개발자들은 기억에 의존했습니다.
"어, 이번 주에 평가 안 돌렸네? 다음 주에 하지 뭐." 그렇게 한두 주가 지나고, 어느새 한 달이 지났습니다.
그사이 성능이 크게 떨어졌지만, 아무도 눈치채지 못했습니다. 더 큰 문제는 배포 후 발견되는 성능 저하였습니다.
새로운 임베딩 모델을 적용하고 배포했는데, 며칠 뒤 사용자 불만이 폭주했습니다. "이전보다 답변이 이상해졌어요!" 그제야 서둘러 평가해보니, Faithfulness가 0.9에서 0.6으로 떨어져 있었습니다.
자동화의 세 가지 트리거 바로 이런 문제를 해결하기 위해 자동 평가 시스템이 필요합니다. 첫 번째 트리거는 코드 변경입니다.
개발자가 main 브랜치에 푸시하거나 Pull Request를 만들 때마다 자동으로 평가가 실행됩니다. 성능이 떨어지면 PR에 경고가 표시되어, 배포 전에 문제를 발견할 수 있습니다.
두 번째 트리거는 스케줄입니다. 매주 월요일 오전 9시처럼 정해진 시간에 평가가 실행됩니다.
코드를 바꾸지 않아도 데이터가 바뀌거나 외부 API 성능이 변할 수 있기 때문에, 주기적인 모니터링이 중요합니다. 세 번째 트리거는 수동 실행입니다.
급하게 확인하고 싶을 때 버튼 하나로 평가를 시작할 수 있습니다. GitHub Actions로 구현하기 위의 코드를 한 단계씩 살펴보겠습니다.
on 섹션에서 세 가지 트리거를 정의합니다. push는 코드가 main에 푸시될 때, schedule은 크론 표현식으로 주기를 정의하고, workflow_dispatch는 수동 실행을 가능하게 합니다.
jobs 섹션에서 실제 작업을 정의합니다. Python을 설치하고, 의존성을 설치한 후, evaluate_rag.py 스크립트를 실행합니다.
이때 OPENAI_API_KEY는 GitHub Secrets에서 안전하게 가져옵니다. 평가 결과를 아티팩트로 업로드하여 나중에 다운로드할 수 있게 합니다.
그리고 check_threshold.py 스크립트로 성능이 임계값을 넘는지 확인합니다. 만약 Faithfulness가 0.7 미만이면 워크플로우가 실패하여, PR이 머지되지 않습니다.
마지막으로 PR에 평가 결과를 코멘트로 남겨, 리뷰어가 한눈에 성능 변화를 확인할 수 있게 합니다. 실무에서는 어떻게 활용할까요? 예를 들어 뉴스 요약 서비스를 운영한다고 가정해봅시다.
매일 새로운 뉴스 기사가 벡터 데이터베이스에 추가되고, RAG 시스템은 이를 바탕으로 요약을 제공합니다. 이때 자동 평가 시스템을 구축하면, 매일 새벽 3시에 자동으로 평가가 실행됩니다.
만약 어제 추가된 데이터가 품질이 낮아서 전체 성능이 떨어졌다면, 아침에 출근한 팀원들이 Slack으로 경고를 받습니다. 또한 개발자가 새로운 프롬프트를 적용하는 PR을 만들면, 자동으로 평가가 돌아가서 "Faithfulness: 0.85 → 0.88 (개선!)"이라는 코멘트가 달립니다.
리뷰어는 이를 보고 안심하고 머지할 수 있습니다. 주의해야 할 점 초보 개발자들이 흔히 하는 실수 중 하나는 API 비용을 고려하지 않는 것입니다.
RAGAS는 LLM API를 많이 호출하므로, 큰 테스트 셋으로 매 커밋마다 평가하면 비용이 폭발합니다. 또 다른 실수는 너무 엄격한 임계값을 설정하는 것입니다.
Faithfulness가 0.95 미만이면 실패하게 하면, 거의 모든 PR이 실패합니다. 현실적인 목표를 설정해야 합니다.
따라서 PR에서는 작은 샘플로 빠르게 평가하고, 주간 평가에서는 큰 샘플로 정확하게 평가하는 식으로 분리하는 것이 좋습니다. 정리하며 다시 김개발 씨의 이야기로 돌아가 봅시다.
자동 평가 시스템을 구축한 김개발 씨는 이제 편안해졌습니다. "이제 제가 신경 쓰지 않아도 시스템이 알아서 모니터링하네요!" 어느 날 PR을 만들었는데, 자동으로 평가 결과가 코멘트로 달렸습니다.
"Faithfulness: 0.80 → 0.75 (저하!)" 김개발 씨는 즉시 문제를 발견하고 수정할 수 있었습니다. 배포 전에 막을 수 있었던 것입니다.
팀장님도 매우 만족스러워했습니다. "이제 안심하고 배포할 수 있겠어요!"
실전 팁
💡 - API 비용을 고려하여 테스트 셋 크기를 조절하세요. PR용 작은 셋, 주간용 큰 셋을 분리하세요.
- 임계값은 현재 성능의 90% 정도로 설정하여, 급격한 저하만 감지하도록 하세요.
- Slack이나 Discord와 연동하여 팀원 모두가 성능 변화를 실시간으로 확인할 수 있게 하세요.
이상으로 학습을 마칩니다. 위 내용을 직접 코드로 작성해보면서 익혀보세요!
댓글 (0)
함께 보면 좋은 카드 뉴스
ReAct 패턴 마스터 완벽 가이드
LLM이 생각하고 행동하는 ReAct 패턴을 처음부터 끝까지 배웁니다. Thought-Action-Observation 루프로 똑똑한 에이전트를 만들고, 실전 예제로 웹 검색과 계산을 결합한 강력한 AI 시스템을 구축합니다.
AI 에이전트의 모든 것 - 개념부터 실습까지
AI 에이전트란 무엇일까요? 단순한 LLM 호출과 어떻게 다를까요? 초급 개발자를 위해 에이전트의 핵심 개념부터 실제 구현까지 이북처럼 술술 읽히는 스타일로 설명합니다.
프로덕션 RAG 시스템 완벽 가이드
검색 증강 생성(RAG) 시스템을 실제 서비스로 배포하기 위한 확장성, 비용 최적화, 모니터링 전략을 다룹니다. AWS/GCP 배포 실습과 대시보드 구축까지 프로덕션 환경의 모든 것을 담았습니다.
RAG 캐싱 전략 완벽 가이드
RAG 시스템의 성능을 획기적으로 개선하는 캐싱 전략을 배웁니다. 쿼리 캐싱부터 임베딩 캐싱, Redis 통합까지 실무에서 바로 적용할 수 있는 최적화 기법을 다룹니다.
실시간으로 답변하는 RAG 시스템 만들기
사용자가 질문하면 즉시 답변이 스트리밍되는 RAG 시스템을 구축하는 방법을 배웁니다. 실시간 응답 생성부터 청크별 스트리밍, 사용자 경험 최적화까지 실무에서 바로 적용할 수 있는 완전한 가이드입니다.