Featured Post
작성자:
Iros
- 공유 링크 만들기
- X
- 이메일
- 기타 앱

사실 이 글은, 제가 한동안 RAG를 붙잡고 혼자 끙끙대다가 “아, 이건 기록으로 남겨야겠다” 싶어서 쓰게 됐어요. 주말에 제주 바다 보고 와서 기분 좋게 출근했는데, 월요일 아침부터 RAG 답변 품질 이슈가 슬랙에 줄줄이 올라와 있더라고요. 뭐랄까, 여행 가방도 아직 다 못 풀었는데 현실이 먼저 문을 두드린 느낌이랄까요.
요즘 제가 회사에서 꽤 공을 들이고 있는 일이 RAG, 그러니까 Retrieval-Augmented Generation 시스템을 실무 서비스에 맞게 다듬는 작업입니다. 처음에는 정말 신났어요. 문서 넣고, 벡터 DB 붙이고, LLM 연결하니까 답변이 그럴싸하게 나오거든요. 그런데 며칠 지나면 슬슬 찜찜해집니다. “이거 진짜 잘하는 거 맞나?”, “어제보다 좋아진 건가?”, “프롬프트 바꾼 게 도움이 된 건가?” 이런 질문이 계속 머릿속에서 맴돌아요.
예전에 제가 RAG로 내 코드베이스를 검색해보니 알겠더라, AI 코딩이 답답했던 진짜 이유라는 글에서도 비슷한 얘기를 한 적이 있는데요. RAG는 만들어보면 꽤 빨리 그럴듯한 데모가 나옵니다. 문제는 그다음이에요. 데모가 아니라 실제 서비스로 가져가려면 “느낌상 괜찮다”는 말로는 부족하더라고요. 개발자끼리야 대충 눈빛으로 통한다고 해도, 고객사나 팀장님 앞에서는 숫자가 필요합니다.
처음엔 저도 테스트 질문 몇 개 던져보고 “오, 답변 괜찮네” 하고 넘어갔습니다. 솔직히 말하면 그게 제일 편하거든요. 그런데 데이터가 조금 늘고, chunk size를 바꾸고, 프롬프트 문장 하나 고쳤더니 답변 품질이 오르락내리락하기 시작했어요. 그때 발견한 도구가 바로 Ragas였습니다. 오늘은 제가 Ragas로 RAG 평가 환경을 만들면서 겪은 시행착오를 편하게 풀어보려고 해요. 공식 문서 요약보다는, 제가 실제로 부딪힌 부분 위주로 이야기해볼게요.
RAG 평가는 왜 이렇게 애매하게 느껴질까
일반적인 개발 테스트는 그래도 어느 정도 선이 명확하잖아요. API가 200을 주는지, DB에 값이 들어갔는지, 함수가 기대한 값을 반환하는지. 그런데 RAG는 묘하게 다릅니다. 답변이 이상할 때 원인이 한 군데가 아니거든요.
예를 들어 사용자가 “우리 회사 휴가 규정에서 반차는 어떻게 쓰나요?”라고 물었다고 해볼게요. 답변이 엉뚱하게 나왔다면 이유가 꽤 여러 가지일 수 있습니다.
- 문서 검색 단계에서 휴가 규정 문서를 못 찾았을 수도 있고요.
- 문서는 찾았는데 엉뚱한 조항을 상위에 올렸을 수도 있습니다.
- 좋은 문서를 가져왔는데 LLM이 자기 마음대로 말을 보탰을 수도 있어요.
- 프롬프트가 애매해서 “문서에 없으면 모른다고 답하라”는 지시가 제대로 안 먹혔을 수도 있고요.
이게 참 사람 피곤하게 만듭니다. 회의에서는 “검색이 문제인가요, 생성이 문제인가요?”라는 질문이 나오는데, 막상 로그를 보면 둘 다 조금씩 문제인 경우가 많아요. 딱 잘라 말하기가 어렵습니다.
Ragas가 마음에 들었던 건, 이 복잡한 문제를 나눠서 볼 수 있게 해준다는 점이었어요. 답변이 문서에 충실한지, 질문과 관련이 있는지, 검색된 context가 적절했는지 같은 걸 각각 점수로 볼 수 있습니다. 완벽한 정답지는 아니지만, 적어도 “느낌적인 느낌”에서 한 발은 벗어나게 해줘요.
| 평가 지표 | 주로 보는 영역 | 제가 실무에서 이해한 느낌 |
|---|---|---|
| Faithfulness | Generator | LLM이 검색된 문서에 없는 말을 지어내지 않았는지 봅니다. 할루시네이션 잡을 때 제일 자주 봤어요. |
| Answer Relevance | Generator | 답변이 질문 의도에 잘 맞는지 확인합니다. 문서에는 맞는데 사용자가 궁금한 걸 비껴가면 여기서 티가 납니다. |
| Context Precision | Retriever | 가져온 chunk 중 쓸모 있는 정보가 위쪽에 잘 올라왔는지 봅니다. 검색 품질 튜닝할 때 꽤 유용했어요. |
| Context Recall | Retriever | 정답을 만들기 위해 필요한 정보를 빠뜨리지 않고 가져왔는지 봅니다. 이 점수가 낮으면 답변이 좋아지기 어렵습니다. |
제가 구성한 Ragas 평가용 프로젝트 구조
처음에는 기존 RAG 코드 안에 평가 스크립트를 대충 끼워 넣었습니다. 늘 그렇죠. “이번 한 번만 빨리 보자” 하고 만든 코드가 일주일 뒤에는 아무도 못 건드리는 덩어리가 됩니다. 저도 몇 번 당하고 나니까, 평가 코드는 아예 별도 흐름으로 빼두는 게 낫겠더라고요.
제가 실무에서 쓰기 편했던 구조는 대략 이런 식이었습니다. RAG pipeline은 그대로 두고, 평가 데이터셋과 평가 실행 코드를 분리했습니다. 특히 evaluation_dataset.json은 Git으로 관리하면서 변경 이력을 남겨두는 게 좋았습니다. 나중에 “왜 지난주보다 점수가 떨어졌지?” 하고 볼 때 꽤 도움이 돼요.
my-rag-evaluator/
├── data/
│ ├── sample_documents.txt
│ └── evaluation_dataset.json
├── reports/
│ ├── ragas_result_2026_05_01.csv
│ └── ragas_result_2026_05_08.csv
├── src/
│ ├── __init__.py
│ ├── rag_pipeline.py
│ ├── evaluator.py
│ └── dataset_loader.py
├── requirements.txt
└── main.py
저는 RAG 쪽 데이터 연결은 LlamaIndex를 자주 씁니다. PDF나 PostgreSQL 같은 데이터 소스를 붙일 일이 많아서요. 혹시 데이터 연결 구조부터 고민 중이라면, 예전에 적어둔 LlamaIndex로 PDF와 PostgreSQL을 연결해보니, RAG 데이터 연결이 훨씬 편해졌습니다 글도 같이 보면 감이 좀 올 거예요. Ragas는 평가 도구고, LlamaIndex는 RAG 파이프라인을 구성하는 쪽에 가깝다고 보면 됩니다.
Ragas로 실제 점수 뽑아보기
Ragas 평가를 돌리려면 기본적으로 몇 가지 데이터가 필요합니다. 질문, LLM이 만든 답변, 검색으로 가져온 context, 그리고 기준이 되는 정답입니다. 여기서 많은 분들이 ground_truth를 귀찮아하는데요. 저도 그랬습니다. 그런데 이걸 대충 만들면 평가 점수도 같이 대충 나옵니다. 평가 데이터는 진짜 반찬이 아니라 밥이에요. 부실하면 전체가 흔들립니다.
아래 코드는 제가 처음 팀원들에게 공유했던 간단 버전입니다. 실제 운영에서는 dataset loader를 따로 만들고, 결과를 CSV나 dashboard로 뽑았지만, 흐름을 보기에는 이 정도가 제일 편합니다.
# src/evaluator.py
import os
from datasets import Dataset
from ragas import evaluate
from ragas.metrics import (
faithfulness,
answer_relevance,
context_recall,
context_precision
)
evaluation_data = {
"question": [
"사내 휴가 규정에서 반차는 어떻게 신청하나요?",
"Ragas를 사용할 때 가장 먼저 봐야 할 지표는 무엇인가요?"
],
"answer": [
"반차는 근태 시스템에서 오전 또는 오후 반차를 선택한 뒤 팀장 승인 절차를 거치면 신청할 수 있습니다.",
"Ragas에서는 답변이 문서에 충실한지 확인하는 Faithfulness와 검색이 필요한 정보를 잘 가져왔는지 보는 Context Recall을 먼저 확인하는 것이 좋습니다."
],
"contexts": [
[
"반차는 사내 근태 시스템에서 신청할 수 있으며 오전 반차와 오후 반차로 구분됩니다.",
"휴가 신청은 조직장의 승인을 받은 뒤 최종 반영됩니다."
],
[
"Ragas는 RAG 시스템의 검색 및 생성 품질을 평가하기 위한 프레임워크입니다.",
"Faithfulness는 생성된 답변이 제공된 context에 기반하는지 평가합니다.",
"Context Recall은 질문에 답하기 위해 필요한 정보가 검색 context에 포함되었는지 평가합니다."
]
],
"ground_truth": [
"반차는 근태 시스템에서 오전 또는 오후 반차를 선택해 신청하며 조직장 승인 후 반영됩니다.",
"Ragas에서는 Faithfulness와 Context Recall을 우선 확인하면 검색과 생성 문제를 나눠 파악하는 데 도움이 됩니다."
]
}
def run_ragas_evaluation():
dataset = Dataset.from_dict(evaluation_data)
print("Ragas 평가를 시작합니다.")
result = evaluate(
dataset=dataset,
metrics=[
faithfulness,
answer_relevance,
context_recall,
context_precision
]
)
df_result = result.to_pandas()
print("\n=== 평가 결과 ===")
print(
df_result[
[
"question",
"faithfulness",
"answer_relevance",
"context_recall",
"context_precision"
]
]
)
return result
if __name__ == "__main__":
if not os.environ.get("OPENAI_API_KEY"):
print("OPENAI_API_KEY 환경변수가 설정되지 않았습니다.")
else:
run_ragas_evaluation()
처음 실행했을 때 기분이 좀 묘했습니다. 그동안 회의에서 “좋아진 것 같은데요”라고 말하던 게, 숫자로 딱 나오니까요. 물론 숫자가 전부는 아닙니다. 그래도 대화를 바꿔줍니다. “이 답변 마음에 안 들어요”에서 “Faithfulness는 올랐는데 Answer Relevance가 떨어졌네요”로 바뀌거든요. 이 차이가 생각보다 큽니다.
제가 실제로 만난 에러와 삽질들
도구 소개 글만 보면 다들 쉽게 되는 것 같잖아요. 그런데 실무는 늘 한 번씩 발목을 잡습니다. 저도 Ragas 붙이면서 자잘하게 많이 넘어졌어요. 특히 한글 데이터, API 비용, 평가 데이터 품질 이 세 가지가 제일 크게 다가왔습니다.
한글 평가에서 가끔 점수가 이상하게 튀었습니다
처음에는 Ragas 기본 설정으로 평가를 돌렸습니다. 그런데 어떤 답변은 사람이 보기에는 꽤 괜찮은데 점수가 이상하게 낮게 나오더라고요. 반대로 애매한 답변인데 점수가 생각보다 높게 나오는 경우도 있었습니다. 한글 문장 특유의 생략이나 표현 차이를 평가 LLM이 엄격하게 보거나, prompt parsing이 흔들리는 느낌이 있었어요.
그때 봤던 로그 중 하나가 이런 식이었습니다. 실제 서비스 로그는 아니고 형태만 비슷하게 정리한 예시입니다.
WARNING:ragas.executor:Exception raised in Job[12]: The output parser could not parse the completion.
Expected format: JSON
Received: 평가 결과는 다음과 같습니다. 충실성은 높지만 일부 표현이...
이런 로그를 보면 처음엔 살짝 허탈합니다. “아니, 평가를 하랬더니 평가 설명문을 써버리면 어떡하니…” 싶은 거죠. 그래서 저는 가능하면 평가용 prompt를 한국어 기준에 맞게 더 단단하게 잡아주는 편입니다. “반드시 JSON만 반환해라”, “설명 문장을 덧붙이지 마라”, “문서에 없는 내용이면 낮게 평가해라”처럼요. 조금 귀찮아도 이 작업을 해두면 평가 결과가 훨씬 안정적이었습니다.
API 비용은 생각보다 빨리 올라갑니다
이건 정말 현실적인 이야기입니다. Ragas는 내부적으로 LLM을 judge처럼 사용합니다. 그러니까 질문 하나를 평가한다고 LLM 호출이 한 번만 나가는 게 아니에요. metric마다 호출이 나가고, 데이터셋이 커질수록 비용이 생각보다 빨리 올라갑니다.
저도 처음에 멋모르고 평가 질문을 200개 가까이 넣고 돌렸다가, 대시보드에서 사용량 올라가는 걸 보고 조용히 중지 버튼을 눌렀습니다. 커피 한 잔 값이면 되겠지 했는데, 실험을 자주 돌리면 금방 점심값이 되고, 더 가면 팀 비용으로 설명해야 하는 수준이 됩니다. 회사 비용이라도 마음이 편하진 않더라고요.
| 상황 | 제가 택한 방식 | 느낀 점 |
|---|---|---|
| 로컬 개발 중 | 샘플 10~20개만 빠르게 평가 | 프롬프트 방향성 확인용으로 충분했습니다. |
| PR 확인 단계 | Golden Dataset 30~50개 평가 | 회귀 테스트처럼 쓰기 좋았습니다. |
| 배포 전 점검 | 전체 평가셋 실행 | 비용은 들지만, 이때는 쓸 만한 가치가 있었습니다. |
가능하다면 실험 단계에서는 Ollama 같은 로컬 LLM을 평가 judge로 붙여보는 것도 방법입니다. 물론 로컬 LLM 평가가 항상 정답은 아니에요. 그래도 프롬프트 수정 전후의 상대 비교 정도는 꽤 쓸 만했습니다. 비용을 아예 0원에 가깝게 만들 수 있다는 점도 무시 못 하고요.
바이브 코딩으로 RAG 만질 때 꼭 지키는 제 기준
요즘은 저도 AI 도움을 많이 받습니다. 프롬프트 초안 만들고, 테스트 케이스 뽑고, 평가 데이터셋 구조 잡을 때 LLM을 꽤 적극적으로 써요. 그런데 여기서 조심해야 할 게 있습니다. AI가 만들어준 답변이 그럴싸하다고 해서 바로 평가 데이터로 넣으면 안 됩니다. 평가 데이터는 시스템의 기준점이 되거든요. 기준점이 흔들리면 나중에 튜닝도 같이 흔들립니다.
제가 자주 쓰는 방식은 이렇습니다. AI에게 무작정 “평가 데이터 만들어줘”라고 하지 않고, 역할과 제약을 아주 구체적으로 줍니다. 특히 “비슷한 질문만 만들지 말고, 실패할 만한 질문도 섞어라”는 지시를 꼭 넣습니다. RAG는 쉬운 질문만 잘 맞히는 것보다, 애매한 질문에서 모른다고 말할 줄 아는 게 더 중요할 때가 많거든요.
너는 RAG 평가 데이터셋을 만드는 QA 엔지니어야.
아래 문서를 기준으로 평가용 질문과 정답을 만들어줘.
조건:
- 질문은 쉬운 질문, 애매한 질문, 문서에 답이 없는 질문을 섞어줘.
- 정답은 반드시 문서에 있는 내용만 근거로 작성해줘.
- 문서에 답이 없으면 "문서에서 확인할 수 없습니다"라고 작성해줘.
- 검색 context로 쓰일 만한 핵심 문장도 함께 뽑아줘.
- 비슷한 표현의 질문을 반복하지 말아줘.
- 결과는 JSON 배열만 반환해줘.
필드:
- question
- ground_truth
- expected_context_keywords
- difficulty
- answerable
이렇게 만든 초안은 사람이 한 번 봐야 합니다. 이 부분은 아직까지 제 고집입니다. 아무리 AI가 편해도, 평가셋만큼은 개발자나 도메인 담당자가 눈으로 확인해야 해요. 특히 사내 규정, 금융, 의료, 법무 쪽 문서라면 더더욱 그렇습니다. 잘못 만든 ground_truth 하나가 전체 점수를 이상하게 끌고 갈 수 있습니다.
- Golden Dataset은 작게 시작하세요. 처음부터 500개 만들면 관리가 안 됩니다. 저는 30개 정도로 시작하는 걸 좋아합니다.
- 실패 케이스를 일부러 넣으세요. 문서에 없는 질문, 오래된 용어, 축약어 질문이 있어야 시스템 약점이 보입니다.
- 질문만 바꾸지 말고 context도 같이 저장하세요. 나중에 retriever가 뭘 가져왔는지 비교해야 원인을 찾을 수 있습니다.
- 점수 하나에 목숨 걸지 마세요. Faithfulness가 높아도 답변이 불친절할 수 있고, relevance가 높아도 근거가 약할 수 있습니다.
Ragas 점수를 팀에서 어떻게 써먹었나
Ragas를 붙이고 제일 좋았던 건, 논의 방식이 바뀐 점이었습니다. 예전에는 프롬프트 바꾼 뒤에 답변 몇 개 보고 “조금 나아진 것 같은데요”라고 했습니다. 듣는 사람도 애매하고, 말하는 사람도 찜찜하죠. 그런데 평가 리포트를 뽑기 시작하니까 훨씬 편해졌습니다.
예를 들면 이런 식입니다.
| 변경 내용 | Faithfulness | Context Recall | 판단 |
|---|---|---|---|
| 기존 프롬프트 | 0.71 | 0.68 | 답변은 그럴듯하지만 근거 누락이 자주 보였습니다. |
| 근거 문장 인용 추가 | 0.84 | 0.69 | 생성 품질은 좋아졌지만 검색 쪽 개선은 크지 않았습니다. |
| chunk size와 overlap 조정 | 0.86 | 0.78 | 검색 누락이 줄어서 배포 후보로 올릴 수 있었습니다. |
이 숫자들이 절대적인 진실은 아닙니다. 하지만 팀 안에서 대화할 기준점은 만들어줍니다. 특히 PM이나 비개발 직군과 이야기할 때 좋았어요. “이번 변경은 답변 말투 개선이 아니라, 근거 기반성을 높이기 위한 작업입니다”라고 설명하기가 쉬워졌거든요.
Ragas 도입 전에 한 번은 생각해볼 것들
제가 Ragas를 좋아하긴 하지만, 모든 프로젝트에 무조건 넣어야 한다고 말하고 싶진 않습니다. 간단한 데모나 사내 PoC 초반 단계라면 오히려 부담스러울 수 있어요. 평가셋 만들고, 비용 관리하고, 결과 해석하는 일도 결국 일이니까요.
다만 아래 상황이라면 저는 꽤 적극적으로 추천합니다.
- RAG 답변 품질을 고객사나 내부 의사결정자에게 숫자로 설명해야 할 때
- chunk size, top_k, embedding model을 바꿨는데 뭐가 좋아졌는지 감이 안 올 때
- 프롬프트를 수정할 때마다 기존 답변이 망가지는지 걱정될 때
- RAG 시스템을 운영 환경에 올리고 지속적으로 품질을 추적해야 할 때
- 개발자마다 “좋은 답변”의 기준이 달라서 논의가 자꾸 감정적으로 흐를 때
제가 느낀 Ragas의 가장 큰 가치는 점수 그 자체보다 RAG 품질을 나눠서 바라보게 해주는 습관에 있었습니다. 답변이 이상하면 예전에는 그냥 LLM 탓을 했어요. 그런데 이제는 “검색이 부족했나?”, “context는 충분했는데 생성을 잘못했나?”, “질문 의도를 못 잡았나?” 이렇게 나눠서 보게 됩니다. 이게 실무에서는 꽤 큰 변화입니다.
이런 분들께 특히 잘 맞습니다
Ragas는 RAG를 막 시작한 분보다는, 이제 슬슬 운영 품질을 고민하는 분들께 더 잘 맞는 도구라고 생각합니다. 데모는 이미 만들었고, 내부 사용자도 붙기 시작했고, 그런데 답변 품질을 어떻게 관리해야 할지 막막한 분들 있잖아요. 딱 그 시점에 빛을 봅니다.
개발자 입장에서 특히 좋았던 건, 감으로 하던 일을 조금씩 테스트 문화 안으로 끌고 들어올 수 있다는 점이었습니다. “이 변경 괜찮은 것 같아요”가 아니라 “이 변경 후 Faithfulness는 올랐고, Context Recall은 유지됐습니다”라고 말할 수 있으니까요. 별거 아닌 것 같지만, 운영 서비스에서는 이런 말 한마디가 꽤 든든합니다.
물론 Ragas도 만능은 아닙니다. 평가 LLM이 틀릴 수도 있고, 한글 데이터에서는 prompt를 다듬어야 하고, 비용도 신경 써야 합니다. 그래도 RAG를 진지하게 운영하려는 팀이라면 한 번쯤은 꼭 써볼 만합니다. 저도 이제는 RAG 프롬프트를 바꾸거나 retriever 설정을 건드릴 때, 그냥 배포하지 않습니다. 작게라도 평가를 돌려보고 갑니다. 나이 들수록 조심성이 늘어서 그런가 싶기도 한데요. 뭐, 장애 한 번 막으면 그걸로 충분히 값어치가 있더라고요.
RAG를 만들었는데 “이게 진짜 좋아진 건지 모르겠다” 싶은 개발자, LLM 답변 품질을 숫자로 설명해야 하는 팀 리더, 그리고 바이브 코딩으로 만든 AI 기능을 이제 실무 서비스 수준으로 끌어올리고 싶은 분이라면 Ragas를 한 번 붙여보세요. 감으로 보는 시대에서, 적어도 반쯤은 벗어날 수 있습니다. 그 반 걸음이 생각보다 마음을 편하게 해줍니다.
👨💻
작성자: 20년 경력 IT 전문 아키텍트
실무 개발과 아키텍처 설계를 거쳐 현재는 AI 바이브 코딩과 개발 자동화를 연구하고 있습니다. 직접 삽질하며 깨달은 실전 꿀팁과 에러 극복 사례만 투명하게 공유합니다.
댓글
댓글 쓰기