728x90
RAG 시스템은 단순한 문서 검색이나 GPT 응답 생성 기능만으로는 충분하지 않습니다.
실제 사용자에게 응답을 제공하기 전에, 그 응답이 과연 정확한지, 신뢰할 수 있는지를
정량적으로 평가할 수 있는 테스트 체계가 반드시 필요합니다.
그래서 이 글에서는
👉 LangChain 기반 Retrieval QA 체인을 활용한 자동화 테스트 스크립트를 구현합니다.
- 📄 질문-정답 쌍을 담은 JSON 파일을 읽고
- 🤖 Retrieval QA 체인을 통해 GPT 응답을 생성하고
- 🎯 기대 정답과 비교하여 **정확도(Exact Match)**와 F1 점수를 계산합니다
이러한 테스트 루틴은
🔍 RAG 시스템이 업데이트되었을 때 품질이 유지되는지,
🧪 모델 변경 또는 컬렉션 추가 시 성능 회귀 테스트에 활용 가능한 기반 도구가 됩니다.
🎯 목표
- RAG QA 체인을 통해 자동으로 질문에 답변
- 기대 정답과 생성된 응답을 비교하여 품질 측정
- 정확도(Exact Match), F1 점수 기반으로 정량적 평가 수행
🧾 Step 1. 테스트 샘플 (JSON 형식)
[
{
"question": "상품 테이블의 파티션 기준은 뭐야?",
"expected_answer": "category 컬럼 기준으로 파티셔닝 되어 있습니다."
},
{
"question": "고객 테이블의 컬럼 목록은?",
"expected_answer": "고객 테이블은 customer_id, name, region 컬럼으로 구성되어 있습니다."
}
]
- question: 실제 유저가 던질 수 있는 질문
- expected_answer: 모델이 생성해야 할 이상적인 답변 (정답)
⚙️ Step 2. 성능 평가 스크립트
# src/eval_rag.py
import json # 테스트 샘플(JSON) 파일을 읽기 위한 표준 라이브러리
import re # 텍스트 정규화 및 토큰 분리를 위한 정규표현식 처리
from langchain.vectorstores import Chroma # 벡터 검색용 ChromaDB
from langchain.embeddings import OpenAIEmbeddings # 문서 임베딩 처리용 OpenAI 모델
from langchain.chat_models import ChatOpenAI # GPT-3.5 기반 LLM 모델
from langchain.chains import RetrievalQA # 문서 검색 + 응답 생성을 위한 QA 체인
def normalize(text: str) -> list[str]:
"""
답변과 정답을 비교하기 위해,
텍스트를 소문자로 바꾸고 특수문자를 제거하여 단어 리스트로 변환합니다.
이 처리는 F1 점수를 계산하기 위한 사전 작업입니다.
"""
text = text.lower() # 모두 소문자로 변환
text = re.sub(r"[^a-z0-9가-힣 ]+", "", text) # 특수문자 제거 (한글, 영문, 숫자, 공백만 남김)
return text.strip().split() # 단어 단위로 분리하여 리스트 반환
def run_eval(questions_path: str, chroma_dir: str, collection: str):
"""
주어진 질문-정답 쌍을 기반으로 RAG 시스템의 응답 품질을 자동으로 평가합니다.
출력 지표: 정확도(정답 일치율), 평균 F1 점수
"""
# ✅ 1. JSON 파일로부터 테스트 샘플 로딩
with open(questions_path, "r", encoding="utf-8") as f:
samples = json.load(f)
# ✅ 2. QA 체인 구성
vectordb = Chroma(
persist_directory=chroma_dir, # 벡터 DB 저장 경로
embedding_function=OpenAIEmbeddings(), # 동일한 임베딩 모델 사용
collection_name=collection # 평가할 컬렉션 이름
)
retriever = vectordb.as_retriever(search_kwargs={"k": 2}) # 상위 2개 문서 검색 설정
llm = ChatOpenAI(
model_name="gpt-3.5-turbo", # 평가용 LLM 모델
temperature=0 # 변동성 최소화 → 일관된 결과 확보
)
qa = RetrievalQA.from_chain_type(
llm=llm, # 응답 생성용 LLM 연결
retriever=retriever # 검색기 연결
)
# ✅ 3. 평가 지표 초기화
total = len(samples) # 전체 질문 개수
exact_match = 0 # 정답과 정확히 일치한 응답 수
f1_total = 0 # F1 점수 합계
# ✅ 4. 각 질문-정답 쌍에 대해 평가 수행
for i, sample in enumerate(samples):
question = sample["question"]
expected = sample["expected_answer"]
result = qa.run(question) # QA 체인을 통해 GPT 응답 생성
pred_tokens = normalize(result) # 생성된 응답 전처리
true_tokens = normalize(expected) # 정답 전처리
# 정확도 체크: 텍스트가 완전히 일치할 경우
if result.strip() == expected.strip():
exact_match += 1
# F1 점수 계산: 정답 단어와 예측 단어의 교집합 기준
common = set(pred_tokens) & set(true_tokens)
if common:
precision = len(common) / len(pred_tokens)
recall = len(common) / len(true_tokens)
if precision + recall > 0:
f1 = 2 * (precision * recall) / (precision + recall)
else:
f1 = 0
else:
f1 = 0
f1_total += f1
# ✅ 개별 결과 출력
print(f"[{i+1}] Q: {question}")
print(f" 🔹 예상: {expected}")
print(f" 🔸 실제: {result}")
print(f" 🎯 F1 점수: {f1:.2f}\n")
# ✅ 5. 전체 지표 요약 출력
print("====== 최종 평가 결과 ======")
print(f"✅ 정답 일치율: {(exact_match / total) * 100:.2f}%")
print(f"✅ 평균 F1 점수: {(f1_total / total) * 100:.2f}%")
▶️ 실행 예시
python src/eval_rag.py
[1] Q: 상품 테이블의 파티션 기준은 뭐야?
🔹 예상: category 컬럼 기준으로 파티셔닝 되어 있습니다.
🔸 실제: category 컬럼 기준으로 파티셔닝 되어 있습니다.
🎯 F1 점수: 1.00
[2] Q: 고객 테이블의 컬럼 목록은?
🔹 예상: 고객 테이블은 customer_id, name, region 컬럼으로 구성되어 있습니다.
🔸 실제: 고객 테이블은 customer_id, name, region 컬럼으로 구성되어 있습니다.
🎯 F1 점수: 1.00
====== 최종 평가 결과 ======
✅ 정답 일치율: 100.00%
✅ 평균 F1 점수: 100.00%
📎 요약 및 핵심 정리
- RAG 시스템의 응답 품질을 정량적으로 평가할 수 있는 자동 테스트 루틴을 구성했습니다.
- 정확도(Exact Match)는 완전 일치를 기준으로, F1 점수는 단어 유사도 기준으로 측정됩니다.
- 이 구조는 모델 변경 시 회귀 테스트, 성능 비교, 컬렉션 추가 전 검증 등에 적극 활용될 수 있습니다.
728x90