데모 날에는 잘 됐어요. 담당자가 던진 질문 다섯 개에 사내 문서 RAG가 다 그럴듯하게 답했거든요. 그런데 배포 2주 뒤 제보가 들어왔어요. "재택 신청 마감을 물었더니 출장 규정을 읽어줘요." 담당자가 다시 몇 개 쳐 봤는데 이번엔 멀쩡했고요.
그래서 누구 말이 맞는 걸까요. 이 장면에서 빠져 있는 건 모델이 아니라 판단 기준이에요.
'RAG 정확도'라는 숫자는 하나가 아니었어요
RAG는 두 단계로 돌아가요. 문서 조각을 찾아오는 검색, 그 조각을 읽고 답을 쓰는 생성. 그래서 "답이 이상하다"는 제보의 원인도 두 갈래예요. 검색이 엉뚱한 문서를 물어왔거나, 문서는 맞는데 모델이 딴소리를 했거나.
둘을 섞어 "정확도 80%"로 적으면 뭘 고쳐야 하는지 알 수 없어요. 그래서 단계마다 따로 재요. 오픈소스 평가 프레임워크 Ragas의 네 지표가 이 구분을 따라요.
| 단계 | 지표 | 무엇을 재나 |
|---|---|---|
| 검색 | Context Recall | 정답에 필요한 조각을 빠짐없이 가져왔는가 |
| 검색 | Context Precision | 관련 있는 조각을 관련 없는 것보다 위에 올렸는가 |
| 생성 | Faithfulness | 답의 주장 하나하나가 가져온 조각으로 뒷받침되는가 |
| 생성 | Response Relevancy | 답이 질문 의도에서 벗어나거나 군더더기를 붙이지 않았는가 |
그 전에 골든셋부터예요 — 20문항이면 시작할 수 있어요
네 지표 모두 정답을 알아야 계산돼요. 그 정답 묶음이 골든셋이에요. 벤치마크가 아니라 실제 질문에 정답 위치를 적어둔 표예요.
| 열 | 예시 | 어디서 구하나 |
|---|---|---|
| 질문 | 재택 신청 마감이 언제예요 | 채팅 로그 · 티켓 원문 그대로 |
| 정답 문서 | 재택근무 운영 지침 | 담당 부서에 물어봐요 |
| 정답 구절 | 전날 오후 6시까지 | 문서에서 복사해요. 요약하지 않아요 |
| 기대 답변 | 전날 18시까지 근태 시스템 등록 | 선택. 생성 지표에만 필요해요 |
질문은 지어내지 않고 로그에서 가져와요. 사용자는 목차를 모른 채 묻기 때문에 "며칠 걸려요"처럼 문서 어휘와 다른 말투가 섞여야 진짜 검색 품질이 드러나요. 문서에 답이 없는 질문도 두세 개 넣어요. 그 정답은 "모른다"예요.
스무 문항은 작지만 같은 문항을 매번 돌리니 어제와 오늘을 비교할 수 있어요.
검색은 LLM 없이도 잴 수 있어요 — 그래서 매일 돌릴 수 있어요
검색 지표부터 붙였어요. 정답 구절이 상위 k개 조각에 있는지는 문자열 포함으로 판정할 수 있어 모델 호출도 비용도 없거든요. Ragas도 이 방식을 LLM 없는 Context Recall 변형으로 두고 있어요.
def recall_at_k(retriever, golden, k=3):
hit = 0
for question, _, span in golden:
chunks = retriever.search(question)[:k]
if any(span in c for c in chunks):
hit += 1
return hit / len(golden)
이걸 붙이고 달라진 건 "청크 크기를 바꿔도 되나요"에 5초 안에 답하게 됐다는 거예요. 사내 규정을 흉내 낸 예시 코퍼스 8건에 골든셋 20문항을 만들고, 문자 2-gram BM25로 청크 크기만 바꿔 돌려봤어요. 고객사 데이터가 아니라 이 글을 쓰며 만든 재현용 코퍼스이고, 수치는 그 안에서 실측한 값이에요.
| 청크 크기(자) | 겹침 | 조각 수 | Recall@1 | Recall@3 |
|---|---|---|---|---|
| 150 | 0 | 23 | 0.80 | 0.95 |
| 300 | 0 | 15 | 0.85 | 1.00 |
| 300 | 80 | 16 | 0.90 | 1.00 |
| 600 | 0 | 8 | 1.00 | 1.00 |
숫자보다 150자에서 빠진 한 문항이 흥미로웠어요. "장비 신청하면 며칠 걸리나요"인데, 정답이 든 조각의 시작이 이랬어요.
스크 포털에서 하며 처리에는 영업일 기준 5일이 걸린다. 4. 분실 및 파손: …
"장비 신청은 IT 헬프데"는 앞 조각에, "스크 포털에서 … 5일"은 뒤 조각에 갈라진 거예요. 정답이 든 조각에 질문의 단어가 없으니 검색기가 고를 이유가 없었고요. 청크 경계 문제는 알던 것이지만, 어느 문항이 그래서 깨지는지를 스크립트가 짚어줬다는 게 달랐어요. 다만 이 코퍼스는 문서가 짧아 600자가 곧 문서 통째라, 큰 청크가 낫다는 결론으로 가져가면 안 돼요.
검색기를 고치는 이야기는 한국어 토크나이저와 하이브리드 검색 글에 있어요. 이 글의 범위는 "고쳤는지 어떻게 아나"까지예요.
생성 품질은 심판 모델에 맡기되, 심판도 검사해요
Faithfulness는 문자열 비교로 안 돼요. Ragas는 답변에서 주장을 하나씩 뽑고, 각 주장이 가져온 조각에서 추론 가능한지 확인해 뒷받침된 주장 수를 전체로 나눠요. 이 일을 다른 LLM에 시키는 게 LLM-as-a-judge예요.
그런데 심판도 모델이에요. Zheng 등의 2023년 논문은 GPT-4급 심판이 사람 선호와 80% 넘게 일치한다고 보고하면서도 위치 · 장황함 · 자기 강화 편향을 지적했어요. 그래서 심판 점수가 낮은 문항 일부는 사람이 다시 읽어 얼마나 갈리는지 기록하고, 심판 프롬프트는 골든셋과 같은 저장소에서 버전 관리해요. 프롬프트를 바꿔 오른 점수를 모델이 좋아진 걸로 착각하는 사고가 제일 흔하거든요.
평가는 배포 파이프라인 안에 있어야 살아남아요
사람이 기억해서 돌리는 검사는 한 달을 못 가요. 그래서 검색 지표는 임베딩 모델 · 청크 설정 · 색인 스크립트가 바뀌는 커밋마다 CI에서 돌리고, 기준 아래면 배포를 막게 뒀어요.
| AS-IS | TO-BE | |
|---|---|---|
| 판단 기준 | 담당자가 몇 개 쳐 본 인상 | 골든셋 20문항 이상, 제보마다 추가 |
| 돌리는 시점 | 데모 전날 | 검색 관련 커밋마다 · 야간 배치 |
| 실패의 모습 | 배포 2주 뒤 제보 | 배포 전 CI 실패 로그 |
| 원인 좁히기 | 프롬프트부터 만짐 | 검색 지표 → 생성 지표 순서 |
rag-eval:
script:
- python eval/retrieval.py --golden eval/golden.jsonl --k 3
rules:
- changes: [indexing/**, eval/**, config/chunking.yaml]
# recall@3 < 0.90 이면 스크립트가 exit 1 로 파이프라인을 막아요
이렇게 두고 달라진 건 "이 변경이 검색을 깨는지"가 리뷰어의 감이 아니라 로그에 남는다는 거예요. 생성 지표는 비용이 드니 야간에 한 번 돌리고 추세를 봐요. 파일럿에서 운영으로 넘어갈 때 무너지는 지점에서 비결정성 검증을 첫째로 꼽았는데, RAG에서 그 실체가 이 골든셋이에요.
다시 처음 질문으로 돌아가면
이 RAG가 잘 되는지는 누가 판단하나요. 제보한 사람도 담당자도 아니고, 골든셋이 판단해요. 다만 완성해서 서랍에 넣는 문서가 아니라 제보가 올 때마다 한 줄씩 자라는 표예요. 저희가 RAG 구축에서 하는 일의 상당 부분도 이 표를 만들고 파이프라인에 묶는 일이에요.
자주 묻는 질문
Q. 골든셋 20문항으로 충분한가요? A. 통계적으로 충분하진 않아요. 다만 변경 전후 비교에는 바로 쓸 수 있고, 운영 중 제보를 문항으로 바꿔 넣으면 늘어나요.
Q. 벡터 검색에도 같은 방식이 되나요? A. 돼요. 검색기 종류와 무관하게 상위 k개 조각에 정답 구절이 있는지 보면 돼요. 정답 구절은 문서에서 그대로 복사해야 해요.
Q. 생성 지표까지 매 커밋에 돌려야 하나요? A. 저희는 야간 배치로 돌리고 검색 지표만 커밋마다 게이트로 걸어요. 검색이 깨지면 생성 지표는 어차피 같이 떨어지거든요.
Sources
- Ragas — Faithfulness: https://docs.ragas.io/en/stable/concepts/metrics/available_metrics/faithfulness/
- Ragas — Context Recall (LLM 기반 · 비LLM 변형): https://docs.ragas.io/en/stable/concepts/metrics/available_metrics/context_recall/
- Ragas — Context Precision: https://docs.ragas.io/en/stable/concepts/metrics/available_metrics/context_precision/
- Ragas — Response Relevancy: https://docs.ragas.io/en/stable/concepts/metrics/available_metrics/answer_relevance/
- Zheng et al., "Judging LLM-as-a-Judge with MT-Bench and Chatbot Arena", arXiv:2306.05685 (2023-06-09): https://arxiv.org/abs/2306.05685

