RAG 성능 평가: 데이터셋 구축부터 재현성 로그까지

현재 Woorinara 챗봇의 RAG 흐름은 단순하다. 사용자의 query가 들어오면 routing을 통해 location, visa, llm fallback으로 나뉘고, location/visa의 경우 해당 데이터를 prompt에 포함시켜 답변을 생성한다. 이번 글에서는 이 중 visa 데이터 RAG 성능 개선을 위한 평가 체계 구축 과정을 공유하고자 한다.

평가 데이터셋 구축

Qdrant Records to LangChain Documents

챗봇이 참조하는 Qdrant Vector Store에는 사증민원 자격별 안내 매뉴얼체류민원 자격별 안내 매뉴얼이 저장되어 있다. 다만 전처리 과정이나 원문 구조는 알 수 없었기에, Qdrant scroll 방식으로 모든 Record를 수집해 Document 구조로 재구성했다. 이는 RAGAS 평가를 위해 반드시 필요한 단계이다.

Persona 기반 시나리오

RAGAS는 문서에서 엔티티와 내용을 추출해 Knowledge Graph를 만든 뒤, Persona에 맞는 시나리오를 생성한다. 이에 서비스 도메인에 맞춰 다음 6개의 Persona를 정의했다.

  • 한국에 처음 오는 외국인
  • 장기체류 예정 유학생
  • 외국인 근로자
  • 가족 동반 배우자
  • 재외동포 (교포)
  • 민원 처리 공무원 (관리자 관점)

각 Persona별 40개씩 총 240개의 테스트셋을 생성했고, query 타입은 단일/복수 문서 잠초 여부(Single/Multi Hop)와 구체/추상적 질문(Specific/Abstract)으로 다양하게 분포되었다.

생성된 평가 데이터셋 결과

아래는 최종적으로 생성된 테스트 데이터셋이다. Persona 별로 2개씩 가져와봤다.

생성된 query와 답변들이 한국어 또는 영어인 한계점이 있긴 하지만, 어느정도 Persona와 원문을 반영하여 잘 생성된 것으로 보인다.

평가 코드 설계

비동기 배치 평가

처음에는 행 단위 평가를 진행했으나, 속도 문제로 비동기 + 배치 방식으로 개선했다. 다만, 호출량이 많을 경우 429 에러가 발생했기에 AsyncRateLimiteer(60RPM)Semaphore(max_concurrency)를 적용해 안정성을 확보했다.

평가 지표

RAGAS가 제공하는 지표를 기본으로 채택했다.

  • answer_correctness
  • answer_relevancy
  • context_precision
  • context_recall
  • faithfulness

추가로 생성 시간토큰 사용량까지 기록했다.

샘플링과 캐시

240개 전체 데이터셋을 매번 평가하는 것은 비효율적이므로, 샘플링 비율 옵션을 CLI 인자로 두어 개발 단계에서는 소규모로 반복 실험이 가능하도록 했다. 또한 memory/sqlite 캐시 기능을 추가해 동일 입력(프롬프트, 모델, 파라미터)에서는 결과를 재사용할 수 있도록 했다.

frac에 0.5를 주어 전체 데이터셋의 반만을 이용해 테스트를 진행했으나, 역시 많은 시간과 비용이 발생하는 것을 확인했다. 따라서 샘플링 비율을 낮추되 같은 질문 세트로 평가(쌍대 비교)하기로 했다. 이에 매니페스트 기반으로 “같은 질문 세트”를 보장하도록 확장하였다.

결과 리포팅과 범용성

결과 요약

결과 리포팅은 Persona별/전체 평균, 표준편차, 95% CI, 커버리지까지 요약해 한눈에 성능을 비교할 수 있도록 했다.

범용 평가기로 확장

초기에는 CurrentRAG에 종속적이었지만, GraphLike 프로토콜(invoke/ainvoke)만 충족하면 어떤 RAG도 평가 가능하도록 리팩토링했다. 실행 시 --rag pkg.mod:ClsOrObj 형태로 주입하면, 별도 코드 수정 없이 다양한 RAG를 테스트할 수 있다.

재현성 보장: 실행 메타데이터 관리

실험 재현성을 위해 모든 실행을 하나의 run 디렉토리로 패키징했다.

  • 입력 데이터: CSV 사본, 해시
  • 설정: EvalConfig, CLI 인자, seed
  • 환경: Python/OS, uv.lock, pip freeze, Git 커밋 정보
  • 산출물: generations.csv, scores.csv, 요약 결과, 로그
  • 무결성: 주요 파일 sha256

이 구조 덕분에 “왜 점수가 달랐는가?”에 대한 추적이 즉시 가능해졌다.

runs/
└─ 2025-09-08T12-30-15Z_CurrentRAG_len512_seed42/     # run_id
   ├─ inputs/
   │  ├─ dataset.csv                 # 실제 사용 CSV(사본)
   │  └─ dataset.sha256
   ├─ results/
   │  ├─ evaluation_result_{RAG}_{num of dataset}_summary_{tidy/wide}_by_group.csv       
   │  ├─ evaluation_result_{RAG}_{num of dataset}_summary_{tidy/wide}_overall.csv              
   │  ├─ generations.csv             # 생성 단계 출력(args.out)
   │  ├─ generations.csv             # 생성 단계 출력(args.out)
   │  ├─ generations.csv             # 생성 단계 출력(args.out)
   │  ├─ scores.csv                  # RAGAS 점수
   │  ├─ usage.csv                   # 각 데이터 토큰 사용량 등
   │  ├─ usage_summary.csv           # 전체 데이터 토큰 사용량 등
   │  └─ logs.txt                    # 표준로그(파일 핸들러)
   ├─ config/
   │  ├─ eval_config.json            # EvalConfig와 CLI 인자
   │  ├─ rag_spec.json               # --rag, 결과 키, 그래프 메타
   │  └─ seeds.json           # seed, PYTHONHASHSEED 등
   ├─ env/
   │  ├─ lockfiles/
   │  │  ├─ lockfiles.json
   │  │  ├─ pyproject.toml
   │  │  └─ uv.lock
   │  ├─ python.txt                  # python --version
   │  ├─ platform.json               # OS/CPU/메모리/GPU 요약
   │  ├─ pip_freeze.txt              # pip freeze
   │  ├─ git.json                    # 브랜치/커밋/dirty 상태
   │  └─ sdk_versions.json           # openai/langchain/ragas 버전
   ├─ meta.json                      # 상단 모든 요약(런 레코드)
   └─ hashes.json                    # 주요 산출물 sha256 모음

비용·지연 추적

LangChain 콜백을 활용해 LLM의 토큰 사용량·비용·지연 시간을 실행별로 수집했다. 모델별 단가를 로드해 실행 종료 후 총 비용까지 산출 가능하며, 덕분에 실험별 성능/비용 트렌드를 손쉽게 비교할 수 있었다.

결론

이번 작업으로 Woorinara 챗봇의 RAG 성능을 Persona 기반 평가 데이터셋 + 범용 평가 코드 + 재현성 로그 체계로 검증할 수 있게 되었다. 단순 성능 수치뿐 아니라 비용과 효율성까지 한 번에 파악할 수 있는 기반이 마련된 것이다.

앞으로는 이 평가 체계를 활용해 RAG 성능 개선 실험(예: 프롬프트 최적화, 검색 파이프라인 개선)을 빠르고 안정적으로 반복할 수 있을 것이다.

참고