RAG 회고 Python Qdrant Agentic RAG

[RAG Playground] 에필로그: Naive RAG에서 Agentic RAG까지

드디어 시리즈가 끝났습니다.

프롤로그 때 “뭘 해볼지 모르겠지만 일단 해보겠다”라고 썼는데, 퀘스트 형태로 대략 뽑아두고 최소 주에 하나씩, 복습도 겸하면서 확실히 내 것으로 만들자는 생각으로 나름 부지런히 해봤는데요~!

그러다 보니 어느새 두 달 — 2월 중순에 시작해서 4월 초까지, Naive RAG부터 Agentic RAG까지 한 단계씩 쌓아서 드디어 마무리를 하게되었습니다 😮‍💨

아무튼 이제 RAG Playground 시리즈를 마무리하면서, 뭘 만들었는지, 뭘 배웠는지, 그리고 솔직히 아쉬운 건 뭔지 정리해보겠습니다.


성능 변화: Naive에서 Agentic까지

먼저, 거두절미하고 가장 중요한 성능 변화부터 짚고 넘어가겠습니다.

평가 방법

Quest 7까지 마치고 나서, 지금까지 “눈으로만” 봤던 검색 품질을 숫자로 측정해봤습니다.

평가 지표는 세 가지를 사용했습니다.

  • NDCG@5 — 상위 5개 결과의 순위와 관련도를 함께 고려하는 지표. 관련 문서가 앞에 올수록 점수가 높아집니다.
  • MRR — 첫 번째 관련 문서가 몇 번째에 나오는지의 역수. 1위에 나오면 1.0, 2위면 0.5.
  • Precision@5 — 상위 5건 중 관련 문서가 몇 개인지의 비율.
이번 프로젝트 진행하면서 처음으로 알게된 평가 지표 방법입니다. 열심히 레퍼런스 참고와 AI의 도움으로 정리해봤습니다. (꾸벅)

평가셋

평가셋은 직접 수동으로 만들었습니다. 10개 질의를 선정하고, 각 질의에 대해 관련 문서에 grade를 부여했습니다 💪

grade 3 — 완벽히 관련 (지역 + 업종 모두 매칭)
grade 2 — 부분 관련 (지역 or 업종 하나만 매칭)
grade 1 — 약한 관련 (넓은 의미에서 관련)

질의 구성은 아래와 같습니다.

질의소스유형
동래구 목욕탕family_card명확한 지역+업종
부산진구 한식 맛집family_card명확한 지역+업종
해운대구에서 양식 할인family_card명확한 지역+업종
미용실 저렴한 곳family_card업종 명확, 조건 모호
아이스크림 가게family_card데이터 없음 케이스
이번 주말에 아이들이랑 갈 만한 데family_card탐색형
사하구 도서관library명확한 지역+업종
해운대구 영어 도서관library데이터 없음 케이스
금정구 카페family_card명확한 지역+업종
부산 어린이 도서관library광역 탐색형

“아이스크림 가게”나 “해운대구 영어 도서관”처럼 데이터 자체에 없는 케이스를 일부러 포함시킨 게 포인트입니다. 없는 걸 없다고 판단하는 것도 검색 시스템의 중요한 능력이니까요 👍

결과

10개 질의 × 7개 모드로 NDCG@5 / MRR / Precision@5를 정량 측정했습니다.

모드별 NDCG@5 비교 (10개 질의 평균)

모드NDCG@5MRRP@5평균 응답
Naive0.520.450.511,454ms
BM250.300.300.16872ms
Hybrid0.520.530.341,414ms
Rerank0.770.750.662,259ms
HyDE+Rerank0.820.780.463,714ms
Multi+Rerank0.800.750.603,518ms
Agentic0.730.700.628,085ms

의외로 HyDE+RerankNDCG(0.82)·MRR(0.78) 모두 1위였습니다. 가상 문서로 Dense 검색을 강화한 뒤 재정렬하는 방식이 이 데이터셋에서 가장 효과적이었습니다.

RerankPrecision@5(0.66) 에서 1위 — NDCG는 HyDE에 밀리지만 상위 5건 안에 관련 문서를 넣는 정밀도는 가장 높았습니다. 비용 대비 효율로 보면 사실상 가장 실용적인 모드입니다.

AgenticNDCG 0.73으로 Rerank보다 낮은 수치가 나왔습니다. 멀티 소스가 유효한 질의(도서관 관련)에서는 빛났지만, 단일 소스 질의에서는 오히려 불필요한 fallback이 노이즈를 만들었습니다. 응답 시간도 8초로 다른 모드의 2~4배였고요. “복잡한 질의에서 진가를 발휘한다”는 전제가 맞긴 하지만, 단순 평균으로는 그게 잘 안 보인다는 점도 확인할 수 있었습니다.


진짜로 배운 것들

1. RAG는 검색 문제다

처음에는 “LLM이 좋으면 RAG도 좋겠지”라고 막연히 생각했는데, 실험을 거듭하면서 결국 RAG의 품질은 retrieval에서 거의 결정된다는 걸 알게 됐습니다.

LLM이 아무리 뛰어나도 컨텍스트에 엉뚱한 문서가 들어오면 답변이 흔들립니다. 반대로 retrieval이 좋으면 LLM이 그냥 정리만 해줘도 충분합니다.

그리고 retrieval 품질만큼이나 중요한 게 문서 자체의 품질이었습니다. 이 프로젝트에서는 Quest 2에서 원본 API 필드를 그대로 저장하지 않고, LLM이 읽기 좋은 텍스트 포맷으로 변환해서 임베딩했는데 — 이 작업이 이후 검색 품질에 생각보다 큰 영향을 줬습니다.

아무리 좋은 검색 기법도 문서가 의미적으로 잘 구조화되어 있지 않으면 효과가 반감됩니다. retrieval 파이프라인을 고도화하기 전에, 검색 대상 문서가 얼마나 잘 만들어졌는지를 먼저 점검해야 한다는 걸 느꼈습니다.

2. relevance score는 생각보다 유용하다

Re-ranker가 뱉는 relevance score를 처음에는 “참고용”으로만 봤는데, 실험하다 보니 꽤 신뢰할 수 있는 지표였습니다.

  • 0.9+: 관련 문서 확실히 있음
  • 0.5~0.9: 있긴 한데 좀 애매함
  • 0.5 미만: 데이터 자체가 없을 가능성이 높음

이 경계선을 기반으로 fallback 정책을 짤 수 있었고, 이게 Quest 7 Agentic RAG의 핵심으로 작용이 되었습니다.

3. Agentic RAG는 검색 기법보다 오케스트레이션 문제다

Quest 3~6는 “어떤 검색 기법을 추가할까”의 문제였는데, Quest 7은 “언제 어떤 기법을 쓸까”의 문제로 바뀌었습니다.

같은 HyDE라도 명확한 질의에 쓰면 오히려 노이즈가 됩니다. Multi-Query도 마찬가지입니다. 기법 자체보다 언제 쓸지 판단하는 계층이 더 중요하다는 걸 깨달았습니다.

4. 멀티 소스 라우팅이 진짜 어렵다

데이터 소스를 추가하는 것 자체는 어렵지 않습니다. 진짜 어려운 건 소스가 늘어날수록 “이 질의에 어떤 소스를 열어야 하는가”를 판단하는 문제입니다.

지금은 키워드 휴리스틱으로 라우팅하는데, 소스가 2개일 때는 그럭저럭 통하지만 5개, 10개가 되면 규칙 기반만으로는 한계가 옵니다. 결국 소스 선택 자체도 RAG로 풀어야 하는 시점이 온다는 게 Quest 7의 결론이었습니다.

5. 없는 데이터는 어떤 기법도 못 살린다

“아이스크림 가게”는 Naive부터 Agentic까지 전 모드에서 관련 결과가 없었습니다. relevance score가 0.003~0.02를 찍으면서 “데이터에 없음”을 명확하게 신호를 줬습니다.

RAG 품질의 천장은 결국 데이터입니다. 검색 기법을 아무리 잘 다듬어도 없는 걸 만들어낼 수는 없습니다.


아쉬운 것들

평가셋 10개만 가지고 테스트를 진행했기 떄문에 정확한 결론을 내리기에는 어렵지 않았나 생각합니다. 통계적으로 의미 있는 결론을 내리려면 최소 50~100개 이상은 있어야 하는데, 수동 라벨링으로 진행하다보니 한계가 있습니다 😮‍💨

AI로 평가셋을 자동 생성하는 방법도 있는데, “AI가 만든 평가셋을 AI가 평가하는” 구조도 가능하겠고 요즘 그런식으로 에이전틱하게 평가하는 게 유행인 것 같은데 우선은 학습을 목적으로 진행했기 떄문에 직접 진행하였고, 결국 100% AI가 전부 하기보단 신뢰할 수 있는 평가셋은 사람이 직접 만들어야 한다는 걸 다시 느꼈습니다.

그리고 Agentic RAG의 NDCG가 Rerank보다 낮게 나온 것도 아쉬운 부분입니다. 10개 질의 중 멀티 소스가 실제로 유효한 질의가 적다 보니 평균이 눌렸는데, 이걸 공정하게 평가하려면 멀티 소스 특화 질의를 더 많이 넣었어야 했습니다. 평가셋 설계 자체가 중요함을 다시 느꼈습니다.


마무리 소감

처음에 “프롤로그”를 쓸 때는 Naive RAG 하나 만들고 끝날 수도 있겠다고 생각했습니다. 그런데 하다 보니까 계속 다음 단계가 보였습니다. Naive만 해보면 Hybrid가 궁금해지고, Hybrid를 해보면 Re-rank가 궁금해지고, Re-rank를 해보면 Query Rewriting이 궁금해지고…

코드 퀄리티는 아직 많이 부족하고, 실제 서비스로 가져가기엔 한참 멀었지만 — RAG가 어떤 문제를 어떻게 푸는지는 이제 손으로 느끼는 정도는 된 것 같습니다.

이번 시리즈를 마쳤다고 해서 RAG 기법을 완전히 제 것으로 만들었다고 생각하지 않기 떄문에, 다음 시리즈로는 Text-to-SQL 에이전트와 RAG 기법을 함께 엮어서, 정형 데이터와 비정형 데이터를 동시에 다루는 프로젝트를 진행해 보겠습니다.

이 시리즈를 읽어주신 분들께 감사드립니다. 저도 쓰면서 많이 배웠습니다 🫡


공공데이터로 RAG 뚝딱거려보기 시리즈

  1. 프롤로그
  2. Quest 1: 프로젝트 환경 세팅
  3. Quest 2: 공공데이터 API 연동
  4. Quest 3: Naive RAG 파이프라인 구축
  5. Quest 4: Hybrid Search
  6. Quest 5: Re-ranking
  7. Quest 6: Query Rewriting
  8. Quest 7: Agentic RAG
  9. 에필로그 ← 현재