고급 RAG 기술은 더 나은 것이 아니라, 때때로 더 나을 뿐입니다
요약
다양한 고급 RAG 기술(HyDE, Hybrid, Reranking 등)을 직접 실험하며 각 기술이 가진 트레이드오프를 분석합니다. 기술 추가가 항상 성능 향상을 보장하지 않으며, 복잡성에 따른 비용과 성능 사이의 정밀한 측정이 중요함을 강조합니다.
핵심 포인트
- HyDE와 같은 기술은 쿼리에 따라 정밀도를 높이기도 하지만 재현율을 급락시키기도 함
- 고급 RAG 기술은 무료 업그레이드가 아닌 트레이드오프가 존재하는 도구임
- RAG 시스템 구축 시 기술의 최첨단 여부보다 복잡성에 대한 정당성을 검증해야 함
- 검색(Retrieval)과 생성(Generation)의 실패 지점을 명확히 구분하여 측정해야 함
저는 RAG 파이프라인에 다섯 가지 검색 기술을 추가하고 각각을 측정했습니다. 가장 유용했던 결과는 오히려 역효과를 낸 기술이었습니다.
검색 성능을 악화시킨 기술
저는 HyDE가 검색을 개선할 것이라고 기대했습니다.
한 쿼리에서는 컨텍스트 정밀도(Context Precision)를 0에서 0.80으로 끌어올렸습니다. 제 베이스라인(Baseline)이 완전히 놓쳤던 청크(Chunk)를 찾아내어 첫 번째로 순위를 매겼습니다. 깔끔한 승리였습니다.
하지만 다른 쿼리에서는 정반대의 결과가 나타났습니다. 재현율(Recall)이 0.80에서 0.17로 급락했습니다. 동일한 기술이 단순히 도움을 주지 못한 것에 그치지 않고, 결과에서 올바른 청크들을 적극적으로 끌어내 버린 것입니다.
동일한 파이프라인. 동일한 기술. 정반대의 결과.
이것이 바로 "RAG 성능을 높이기 위해 HyDE를 추가하세요"라는 제목의 블로그 포스트들이 말해주지 않는 사실입니다. HyDE는 더 나은 것이 아닙니다. 때때로 더 나을 뿐이며, 어느 때에 그러한지를 아는 것이 실제 기술입니다. 제가 이번 주에 추가한 모든 "고급 RAG 기술"은 정확히 이와 같았습니다. 즉, 무료 업그레이드가 아니라 트레이드오프(Tradeoff)가 존재하는 도구였습니다. 작업의 핵심은 기술을 추가하는 것이 아니었습니다. 어떤 쿼리에서, 왜, 어떤 기술이 그 복잡성을 감수할 가치가 있는지 측정하는 것이었습니다.
이 포스트는 바로 그 측정에 관한 것입니다.
내가 구축한 것 (그리고 유일하게 중요했던 질문)
저는 Anthropic의 자체 문서(15개 문서 페이지, 667개 청크, HNSW 인덱스를 사용한 Postgres + pgvector)를 기반으로 RAG 파이프라인을 구축했습니다. 그런 다음 서로 전환하며 직접 비교할 수 있는 다섯 가지 검색 모드를 결합했습니다.
Dense (밀집) — 일반적인 벡터 유사도 (bge-small 임베딩).
Hybrid (하이브리드) — 밀집(Dense) + BM25 키워드 검색을 Reciprocal Rank Fusion으로 결합.
Reranking (재순위화) — 광범위한 후보군을 추출한 뒤, 크로스 인코더(Cross-encoder)로 재점수화.
HyDE — 질문 대신 가상의 답변(Hypothetical Answer)을 임베딩.
Contextual retrieval (문맥적 검색) — 임베딩 전, 각 청크 앞에 LLM이 작성한 문서 인지형(Document-aware) 요약을 추가.
저는 ML 연구자가 아닌 백엔드 엔지니어의 관점에서 이 문제에 접근했습니다. 백엔드 엔지니어가 이러한 기술들에 대해 던지는 질문은 "이것이 최첨단(State-of-the-art)인가?"가 아닙니다. 그것은 캐시 레이어(Cache layer)나 메시지 큐(Message queue)에 대해 던지는 질문과 같습니다. 즉, "이 중 어떤 것이 실제로 그 복잡성을 정당화하는가?"입니다. RAG 시스템은 모델이 부착된 분산 시스템(Distributed system)입니다. 추가하는 모든 구성 요소는 운영하고, 디버깅하고, 비용을 지불해야 하는 대상입니다. 그렇다면 — 어떤 것들이 그 비용을 돌려줄까요?
먼저 지루한 베이스라인(Baseline)을 측정하세요
무언가 화려한 것을 측정하기 전에, 저는 28개의 질문으로 구성된 평가 세트(Eval set)에 대해 일반적인 밀집 검색 (Dense retrieval)을 측정했습니다.
충실도 (Faithfulness): 0.96
문맥 정밀도 (Context precision): 0.60
이 두 숫자는 완전히 다른 두 가지 문제를 가리키고 있으며, 이 둘을 혼동하는 것이 제가 보는 가장 흔한 RAG 실수입니다. RAG는 독립적인 두 지점에서 실패합니다:
검색 (Retrieval) — 올바른 청크(Chunks)를 가져왔는가? (문맥 정밀도 / 재현율 (Recall))
생성 (Generation) — 모델이 가져온 내용으로부터 충실하게 답변했는가? (충실도 (Faithfulness))
저의 충실도는 이미 0.96이었습니다. 생성기(Generator)가 문제가 아니었습니다. 좋은 문맥이 주어졌을 때, 모델은 답변을 잘 근거(Grounded)를 두어 작성했습니다. 약점은 0.60인 정밀도였습니다. 즉, 제가 모델에 입력한 내용의 약 40%가 노이즈(Noise)였습니다. 올바른 청크는 대개 top-k 안에 있었지만, 단지 엉뚱한 것들에 파묻혀 있었을 뿐입니다.
이것은 프로젝트 전체를 재구성합니다. 제가 추가하려 했던 모든 고급 기술은 검색 (Retrieval)을 목표로 하고 있었고, 검색이야말로 정확히 실패하고 있는 절반이었습니다. 만약 충실도가 낮은 숫자였다면, 이 중 어떤 것도 도움이 되지 않았을 것입니다. 대신 프롬프트 (Prompt)를 튜닝하고 있었겠지요. 지표를 분리해서 확인하기 전까지는 이를 알 수 없습니다. 먼저 데이터를 보고, 그다음 도구를 선택하세요.
각 기술이 제 역할을 다한 곳 — 혹은 그렇지 못한 곳
하이브리드 검색 (Hybrid search, BM25 + dense). 밀집 검색 (Dense retrieval)에는 사각지대가 있습니다: 바로 정확한 용어 (exact terms)입니다. "cache_control: {"type": "ephemeral"}가 무엇을 하나요?"라고 질문하면, 순수 시맨틱 유사도 (semantic similarity) 방식은 막연하게 관련된 캐싱 관련 산문 쪽으로 흘러가 버립니다. BM25는 밀집 검색이 완전히 놓친 정확한 청크 (chunk)를 정확히 잡아냈습니다. 하지만 단순한 상호 순위 융합 (Reciprocal Rank Fusion, RRF)은 이를 다시 순위를 낮춰버렸습니다. 밀집 랭커 (dense ranker)가 정답을 맞힌 단 하나의 희소 랭커 (sparse ranker)보다 더 많은 표를 가져갔기 때문입니다. 교훈: 정확한 용어 매칭은 실재하며 별개인 실패 모드 (failure mode)이지만, 융합 (fusion)이 공짜는 아닙니다. RRF에는 가중치 (weighting)가 필요합니다. 그렇지 않으면 당신이 추가한 바로 그 신호 (signal)를 평균화하여 사라지게 만듭니다.
HyDE — 구원책이자 역효과. 일상적인 어투로 작성되어 코퍼스 (corpus)와 일치하지 않는 쿼리(query)(
Contextual retrieval (문맥적 검색). Anthropic의 기술로, LLM이 각 청크 (chunk)를 해당 문서 내에서의 위치와 함께 요약하여 작성하고, 이를 임베딩 (embedding) 전에 앞에 붙이는 방식입니다. 문제는 비용입니다. 청크당 한 번의 LLM 호출이 필요합니다. 프롬프트 캐싱 (Prompt caching)이 이를 실행 가능하게 만듭니다 (문서를 캐싱하여, 100만 청크당 약 $1). 이 방식은 쉬운 쿼리에는 거의 도움이 되지 않았지만, 밀집 검색 (dense retrieval)이 가장 크게 실패하는 지점, 즉 문맥이 부족하고 결핍된 청크들에는 가장 큰 도움이 되었습니다. 다른 모든 기술과 마찬가지로 상황에 따라 다릅니다.
기술별 복잡성이 정당화되는 경우…
Hybrid + BM25: 쿼리가 정확한 용어/파라미터에 의존할 때, 밀집 검색 (dense retrieval)은 이를 뭉뚱그려 처리합니다.
HyDE: 쿼리가 문서와 전혀 다른 방식으로 표현될 때 — 그리고 이미 일치하는 경우에는 오히려 해가 됩니다.
Reranking (재순위화): 올바른 청크가 검색되었으나 순위가 너무 낮아 사용할 수 없을 때
Contextual retrieval (문맥적 검색): 청크가 짧거나 문맥이 부족할 때; 인제스트 (ingest) 비용을 감당할 여력이 있을 때
튜토리얼 작성을 멈추고 인프라 작업을 시작하게 된 전환점
표준 RAG 평가 라이브러리인 Ragas를 사용해 보았습니다. Haiku 모델을 판사 (judge)로 설정했을 때, 전체 평가 매트릭스 (eval matrix)를 완료하는 데 약 11시간이 걸릴 것으로 예상되었습니다. 원인은 Ragas가 판사의 JSON이 유효하지 않을 때마다 재시도 (retry)를 반복하는 구조화된 출력 (structured-output) 레이어로 판사 호출을 감싸고 있었기 때문입니다. 이로 인해 각 질문은 약 8분 동안 실패한 호출이 몰아치는 폭풍으로 변했습니다.
그래서 저는 네 가지 지표 (metrics)가 실제로 무엇을 계산하는지 읽고, 저만의 비동기 하네스 (async harness)를 구축했습니다. 각 판사 호출은 사소한 스키마 아래 단일 불리언 (boolean) 값을 반환하도록 하여, 재시도 없이 첫 시도에 성공하게 만들었습니다. 그리고 이 모든 호출은 세마포어 (semaphore) 하에서 병렬로 실행됩니다. 지표는 동일합니다. 11시간 대신 221초가 걸렸습니다. 약 50배 더 빠릅니다.
이것이 도구를 사용하는 것과 도구를 이해하는 것 사이의 경계입니다. 지표를 직접 구현할 수 있을 정도로 잘 알고 있다면, 보이지 않는 이유로 느려지는 블랙박스 (black box)의 인질이 되는 것을 멈출 수 있습니다. 상자를 열고, 직접 측정하십시오. 그 본능이 업무의 전부입니다.
솔직한 마무리
이 중 어느 것도 프로덕션 (production) 단계에서 바로 사용할 수 있는 수준은 아니며, 그 간극에 대해 정확히 말씀드리고 싶습니다.
프로덕션 (production) 환경을 구축한다면, 저는 HyDE의 역효과 (backfire)를 방지하기 위한 쿼리 라우터 (query router)를 구축할 것입니다. 즉, 각 쿼리를 분류한 다음, 맹목적으로 하나의 방식을 적용하는 대신 적절한 검색 모드 (retrieval mode)를 선택하는 방식입니다. 또한, 제가 가진 일부 샘플이 아니라 실제 규모 (real scale)에서 평가 매트릭스 (eval matrix)를 실행할 것입니다 (모든 모드 × 모든 카테고리). 제가 추가한 멀티 테넌트 격리 (multi-tenant isolation, 테넌트 + 민감도 필터링) 기능은 단순히 성공적인 경로 (happy path)를 통과하는 것뿐만 아니라, 적대적 테스트 (adversarial testing)가 필요합니다. 그리고 아직 측정되지 않은 것들이 많습니다: p99 지연 시간 (latency), 쿼리당 비용, 그리고 제가 직접 선별하지 않은 코퍼스 (corpus)에서의 동작 방식 등입니다.
하지만 여기서 얻을 수 있는 교훈은 모든 백엔드 엔지니어들이 이미 실천하고 있는 것입니다. 이러한 기술들은 트레이드오프 (tradeoffs)가 존재하는 도구이며, 여러분의 데이터, 쿼리, 그리고 장애 모드 (failure mode)에 어떤 것이 도움이 되는지를 측정하는 것이 바로 핵심적인 작업이라는 점입니다. 모델은 새로워졌지만, 그 원칙은 변하지 않았습니다.
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기