RAG(Retrieval-Augmented Generation) 시스템의 품질을 체계적으로 평가하는 것은 프로덕션 배포 전 반드시 필요한 과정입니다. 본 튜토리얼에서는 RAGAS(RAG Assessment) 프레임워크를 HolySheep AI에接入하고, 핵심 품질 지표를 모니터링하는 방법을 상세히 설명합니다.
HolySheep AI vs 공식 API vs 기타 게이트웨이 비교
| 항목 | HolySheep AI | 공식 OpenAI API | 기타 게이트웨이 |
|---|---|---|---|
| 결제 방식 | 로컬 결제 지원 (해외 신용카드 불필요) | 해외 신용카드 필수 | 다양함 (일부 로컬 지원) |
| 지원 모델 | GPT-4.1, Claude, Gemini, DeepSeek 등 | OpenAI 모델만 | 제한적 모델 지원 |
| 단일 API 키 | ✓ 모든 모델 통합 | ✗ 모델별 분리 | 부분 지원 |
| GPT-4.1 가격 | $8/MTok | $2.5/MTok | $3~15/MTok |
| Claude Sonnet 4 | $15/MTok | $3/MTok | $8~20/MTok |
| Gemini 2.5 Flash | $2.50/MTok | $0.125/MTok | $1~5/MTok |
| DeepSeek V3.2 | $0.42/MTok | 미지원 | 제한적 |
| 평가 파이프라인 통합 | ✓ LangChain, RAGAS 완벽 호환 | 별도 연동 필요 | 혼합 호환성 |
RAGAS 프레임워크 소개
RAGAS(RAG Assessment)는 RAG 시스템의 Retrieval과 Generation 품질을 정량적으로 평가하는 오픈소스 프레임워크입니다. 주요 지표는 다음과 같습니다:
- Faithfulness: 생성된 답변이 retrieved context에 얼마나 충실한지
- Answer Relevance: 생성된 답변이 질문에 얼마나 관련성 있는지
- Context Precision: 검색된 컨텍스트의 관련성 및 순서
- Context Recall: 필요한 정보가 검색되었는지
- Context Entity Recall: 컨텍스트 내 주요 엔티티의 검색률
환경 설정
# 필요한 패키지 설치
pip install ragas openai langchain langchain-community chromadb
환경 변수 설정
export HOLYSHEEP_API_KEY="YOUR_HOLYSHEEP_API_KEY"
export HOLYSHEEP_BASE_URL="https://api.holysheep.ai/v1"
HolySheep AI를 통한 RAG 시스템 구현
먼저 HolySheep AI를 백엔드로 사용하여 RAG 파이프라인을 구축합니다. HolySheep AI의 지금 가입하면 다양한 모델을 단일 API 키로 활용할 수 있습니다.
import os
from langchain_openai import OpenAIEmbeddings, ChatOpenAI
from langchain_community.vectorstores import Chroma
from langchain.text_splitter import RecursiveCharacterTextSplitter
from langchain_community.document_loaders import TextLoader
HolySheep AI 설정
⚠️ base_url은 반드시 https://api.holysheep.ai/v1 사용
os.environ["OPENAI_API_KEY"] = os.getenv("HOLYSHEEP_API_KEY")
os.environ["OPENAI_API_BASE"] = "https://api.holysheep.ai/v1"
임베딩 모델 설정 (HolySheep AI 사용)
embeddings = OpenAIEmbeddings(
model="text-embedding-3-small",
openai_api_key=os.getenv("HOLYSHEEP_API_KEY"),
openai_api_base="https://api.holysheep.ai/v1"
)
LLM 설정 (HolySheep AI - GPT-4.1 사용)
llm = ChatOpenAI(
model="gpt-4.1",
openai_api_key=os.getenv("HOLYSHEEP_API_KEY"),
openai_api_base="https://api.holysheep.ai/v1",
temperature=0.3
)
문서 로드 및 분할
loader = TextLoader("documents/knowledge_base.txt")
documents = loader.load()
text_splitter = RecursiveCharacterTextSplitter(
chunk_size=1000,
chunk_overlap=200
)
docs = text_splitter.split_documents(documents)
벡터 스토어 생성
vectorstore = Chroma.from_documents(
documents=docs,
embedding=embeddings,
persist_directory="./chroma_db"
)
RAG 체인 생성
retriever = vectorstore.as_retriever(search_kwargs={"k": 3})
def rag_chain(query: str) -> dict:
"""RAG 체인 실행"""
# 검색 단계
retrieved_docs = retriever.get_relevant_documents(query)
context = "\n\n".join([doc.page_content for doc in retrieved_docs])
# 생성 단계
prompt = f"""Based on the following context, answer the question.
Context:
{context}
Question: {query}
Answer:"""
response = llm.invoke(prompt)
return {
"answer": response.content,
"contexts": [doc.page_content for doc in retrieved_docs],
"question": query
}
테스트 실행
result = rag_chain("What is the capital of France?")
print(f"Answer: {result['answer']}")
RAGAS 평가 파이프라인 구축
from ragas import evaluate
from ragas.metrics import (
faithfulness,
answer_relevancy,
context_precision,
context_recall,
context_entity_recall
)
from datasets import Dataset
import os
HolySheep AI 평가용 LLM 설정
eval_llm = ChatOpenAI(
model="gpt-4.1",
openai_api_key=os.getenv("HOLYSHEEP_API_KEY"),
openai_api_base="https://api.holysheep.ai/v1",
temperature=0.0 # 평가 시 deterministic 결과
)
eval_embeddings = OpenAIEmbeddings(
model="text-embedding-3-small",
openai_api_key=os.getenv("HOLYSHEEP_API_KEY"),
openai_api_base="https://api.holysheep.ai/v1"
)
평가 데이터셋 정의
eval_data = {
"user_input": [
"What is the capital of France?",
"How does photosynthesis work?",
"What are the main benefits of exercise?"
],
"retrieved_contexts": [
["Paris is the capital and largest city of France.",
"France is a country in Western Europe."],
["Photosynthesis is the process by which plants convert light energy into chemical energy.",
"Chlorophyll absorbs sunlight during photosynthesis."],
["Regular exercise improves cardiovascular health and mental well-being.",
"Physical activity reduces the risk of chronic diseases."]
],
"response": [
"Paris is the capital city of France, located in Western Europe.",
"Photosynthesis is the process by which plants convert light energy into chemical energy using chlorophyll.",
"Exercise provides multiple benefits including improved cardiovascular health, better mental well-being, and reduced risk of chronic diseases."
],
"reference": [
"The capital of France is Paris.",
"Photosynthesis converts light energy to chemical energy in plants.",
"Exercise benefits include cardiovascular health and mental well-being."
]
}
Dataset 생성
dataset = Dataset.from_dict(eval_data)
평가 지표 설정
metrics = [
faithfulness, # 충실도
answer_relevancy, # 답변 관련성
context_precision, # 컨텍스트 정밀도
context_recall, # 컨텍스트 재현율
]
RAGAS 평가 실행
result = evaluate(
dataset=dataset,
metrics=metrics,
llm=eval_llm,
embeddings=eval_embeddings
)
결과 출력
print("\n" + "="*50)
print("RAGAS 평가 결과")
print("="*50)
print(result)
print("\n개별 지표:")
for score_dict in result.scores:
print(f" - Faithfulness: {score_dict['faithfulness']:.3f}")
print(f" - Answer Relevance: {score_dict['answer_relevancy']:.3f}")
print(f" - Context Precision: {score_dict['context_precision']:.3f}")
print(f" - Context Recall: {score_dict['context_recall']:.3f}")
품질 지표 대시보드 구현
import json
from datetime import datetime
from typing import List, Dict
from dataclasses import dataclass, asdict
@dataclass
class RAGEvaluation:
"""RAG 평가 결과 데이터 클래스"""
timestamp: str
question: str
answer: str
contexts: List[str]
faithfulness: float
answer_relevancy: float
context_precision: float
context_recall: float
overall_score: float
def __post_init__(self):
"""전체 점수 계산"""
self.overall_score = (
self.faithfulness * 0.25 +
self.answer_relevancy * 0.30 +
self.context_precision * 0.20 +
self.context_recall * 0.25
)
def to_dict(self) -> dict:
return asdict(self)
class RAGQualityMonitor:
"""RAG 품질 모니터링 클래스"""
def __init__(self, threshold: float = 0.7):
self.threshold = threshold
self.history: List[RAGEvaluation] = []
self.alerts: List[Dict] = []
def add_evaluation(self, eval_result: RAGEvaluation):
"""평가 결과 추가"""
self.history.append(eval_result)
# 임계값 미만 알림
if eval_result.overall_score < self.threshold:
self.alerts.append({
"timestamp": eval_result.timestamp,
"question": eval_result.question,
"score": eval_result.overall_score,
"severity": "high" if eval_result.overall_score < 0.5 else "medium"
})
def get_statistics(self) -> Dict:
"""통계 요약 반환"""
if not self.history:
return {"error": "No evaluation data available"}
scores = [e.overall_score for e in self.history]
return {
"total_evaluations": len(self.history),
"average_score": sum(scores) / len(scores),
"min_score": min(scores),
"max_score": max(scores),
"pass_rate": len([s for s in scores if s >= self.threshold]) / len(scores),
"alert_count": len(self.alerts)
}
def get_score_distribution(self) -> Dict[str, int]:
"""점수 분포 반환"""
distribution = {"excellent": 0, "good": 0, "fair": 0, "poor": 0}
for eval_result in self.history:
score = eval_result.overall_score
if score >= 0.9:
distribution["excellent"] += 1
elif score >= 0.7:
distribution["good"] += 1
elif score >= 0.5:
distribution["fair"] += 1
else:
distribution["poor"] += 1
return distribution
def export_report(self, filepath: str):
"""JSON 리포트 내보내기"""
report = {
"generated_at": datetime.now().isoformat(),
"threshold": self.threshold,
"statistics": self.get_statistics(),
"score_distribution": self.get_score_distribution(),
"alerts": self.alerts,
"evaluations": [e.to_dict() for e in self.history[-100:]] # 최근 100개
}
with open(filepath, "w", encoding="utf-8") as f:
json.dump(report, f, ensure_ascii=False, indent=2)
return filepath
모니터링 사용 예시
monitor = RAGQualityMonitor(threshold=0.75)
평가 결과 추가
eval_result = RAGEvaluation(
timestamp=datetime.now().isoformat(),
question="What is machine learning?",
answer="Machine learning is a subset of AI that enables systems to learn from data.",
contexts=["ML is a branch of AI", "ML uses statistical methods"],
faithfulness=0.85,
answer_relevancy=0.90,
context_precision=0.78,
context_recall=0.82,
overall_score=0.0 # 자동 계산됨
)
monitor.add_evaluation(eval_result)
통계 출력
print("품질 모니터링 통계:")
print(json.dumps(monitor.get_statistics(), indent=2))
연속적 품질 모니터링 파이프라인
import schedule
import time
from datetime import datetime
def run_daily_evaluation():
"""매일 실행되는 평가 작업"""
print(f"\n{'='*50}")
print(f"일일 RAG 품질 평가 시작: {datetime.now()}")
print('='*50)
# HolySheep AI 연결 테스트
try:
test_result = rag_chain("Test query for quality assurance")
print(f"✓ RAG 시스템 정상 동작")
except Exception as e:
print(f"✗ RAG 시스템 오류: {e}")
send_alert("RAG System Down", str(e))
return
# RAGAS 평가 실행
try:
result = evaluate(dataset=dataset, metrics=metrics, llm=eval_llm, embeddings=eval_embeddings)
# 모니터에 결과 추가
for i, score_dict in enumerate(result.scores):
eval_result = RAGEvaluation(
timestamp=datetime.now().isoformat(),
question=eval_data["user_input"][i],
answer=eval_data["response"][i],
contexts=eval_data["retrieved_contexts"][i],
faithfulness=score_dict["faithfulness"],
answer_relevancy=score_dict["answer_relevancy"],
context_precision=score_dict["context_precision"],
context_recall=score_dict["context_recall"],
overall_score=0.0
)
monitor.add_evaluation(eval_result)
# 리포트 생성
report_path = monitor.export_report(f"rag_quality_report_{datetime.now().date()}.json")
print(f"✓ 리포트 생성 완료: {report_path}")
# 통계 출력
stats = monitor.get_statistics()
print(f" - 평균 점수: {stats['average_score']:.3f}")
print(f" - 통과율: {stats['pass_rate']*100:.1f}%")
print(f" - 알림 수: {stats['alert_count']}")
# 품질 저하 감지 시 알림
if stats['average_score'] < 0.7:
send_alert("Quality Degradation", f"평균 점수가 {stats['average_score']:.3f}로 하락했습니다")
except Exception as e:
print(f"✗ 평가 파이프라인 오류: {e}")
send_alert("Evaluation Pipeline Error", str(e))
def send_alert(title: str, message: str):
"""알림 전송 (실제 환경에서는 Slack, Email 등으로 대체)"""
print(f"\n🚨 알림: {title}")
print(f" 메시지: {message}")
# Slack webhook, email, PagerDuty 등으로 전송 가능
스케줄링 설정 (매일 오전 9시)
schedule.every().day.at("09:00").do(run_daily_evaluation)