RAG 시스템에서 검색(Retrieval)은 최종 답변의 품질을 결정하는 가장 중요한 단계다. 아무리 강력한 LLM이라도, 엉뚱한 문서를 컨텍스트로 받으면 좋은 답변을 생성할 수 없다.
문제는 단일 검색 방식만으로는 한계가 뚜렷하다는 점이다. 키워드 검색은 정확한 용어가 포함된 문서를 잘 찾지만, 의미가 같은 다른 표현은 놓친다. 반대로 벡터 기반 의미 검색은 유사한 의미를 잘 잡아내지만, 고유명사나 기술 용어처럼 정확한 매칭이 중요한 경우에 약하다.
이 문제를 해결하기 위해 등장한 것이 하이브리드 검색(Hybrid Retrieval) 전략이다. 서로 다른 검색 방식을 결합하여 각각의 약점을 보완하고, 여기에 Reranking까지 더하면 검색 정확도를 크게 끌어올릴 수 있다.
Fusion Retrieval: 두 검색의 점수를 하나로
Fusion Retrieval(융합 검색) 은 의미론적 검색(Semantic Search) 과 키워드 검색(Keyword Search) 이라는 두 가지 검색 방식의 장점을 결합한 하이브리드 기법이다.
동일한 문서 데이터에 대해 벡터 인덱스와 키워드 인덱스를 모두 구축하고, 두 검색 결과의 점수를 정규화한 뒤 가중합으로 융합하는 방식이다.
작동 방식
1단계 -- 이중 인덱싱(Dual Indexing)
RAG 시스템을 구축할 때, 같은 문서 데이터에 대해 두 종류의 인덱스를 생성한다.
- 벡터 인덱스: FAISS 같은 벡터 DB를 사용하여 문서의 의미를 임베딩 벡터로 저장
- 키워드 인덱스: BM25 같은 알고리즘을 사용하여 문서의 키워드 기반 인덱스를 생성
2단계 -- 병렬 검색(Parallel Retrieval)
사용자 질문이 들어오면, 두 인덱스에서 동시에 검색을 수행한다. 벡터 검색은 의미적 유사도 점수를, 키워드 검색은 BM25 점수를 각각 반환한다.
3단계 -- 점수 정규화 및 융합(Score Normalization & Fusion)
여기서 핵심적인 문제가 하나 있다. 벡터 유사도 점수와 BM25 점수는 스케일이 완전히 다르다. 코사인 유사도는 0~1 범위이고, BM25 점수는 0부터 수십까지 올라갈 수 있다. 이 두 점수를 그대로 합산하면 한쪽이 다른 쪽을 압도하게 된다.
그래서 먼저 각 점수를 0~1 사이로 정규화(normalize) 한 뒤, alpha 가중치를 적용하여 하나의 융합 점수(fused score)로 합산한다.
fused_score = alpha * semantic_score + (1 - alpha) * keyword_score
4단계 -- 순위 재조정(Re-ranking)
최종 융합 점수를 기준으로 전체 결과의 순위를 다시 매기고, 상위 문서를 최종 검색 결과로 반환한다.
장점
- 검색 품질 향상: 키워드 매칭과 의미 이해를 동시에 활용하므로, 단일 검색 방식보다 더 정확한 결과를 얻는다.
- 강건성(Robustness): 한쪽 검색이 놓칠 수 있는 문서를 다른 쪽이 보완한다. "머신러닝"을 검색했을 때 "기계학습"이라고 적힌 문서도 벡터 검색이 잡아준다.
- 유연한 조절: alpha 값을 변경하면 의미 검색과 키워드 검색의 비중을 용도에 맞게 조절할 수 있다. 기술 문서 검색이라면 키워드 비중을 높이고, 일반 질의응답이라면 의미 검색 비중을 높이는 식이다.
Ensemble Retrieval: 집단 지성 기반 검색
Ensemble Retrieval(앙상블 검색) 은 Fusion Retrieval의 확장판이라 할 수 있다. 단 두 개의 검색기를 결합하는 데 그치지 않고, 여러 독립적인 검색기(Retriever)를 동시에 활용한 뒤, 각 검색기가 반환한 결과를 지능적으로 종합하여 최종 순위를 결정한다.
하나의 검색 방식에 의존하지 않고, 서로 다른 강점을 가진 여러 검색기의 결과를 융합하는 '집단 지성' 기반의 검색 기법이다.
작동 방식
1단계 -- 다중 검색기 준비(Multiple Retrievers Setup)
의미론적 검색, 키워드 검색, 계층적 검색 등 각기 다른 강점을 가진 여러 종류의 검색기를 독립적으로 준비한다. Fusion Retrieval이 벡터 + 키워드라는 두 축에 한정된다면, Ensemble Retrieval은 검색기의 수와 종류에 제한이 없다.
2단계 -- 병렬 검색 실행(Parallel Retrieval)
사용자 질문이 들어오면, 준비된 모든 검색기에 동시에 전달하여 각각의 검색을 병렬로 수행한다.
3단계 -- 결과 수집(Result Collection)
각 검색기는 자체 기준에 따라 평가한 문서 순위 목록을 반환한다. 이 시점에서 N개의 검색기로부터 N개의 서로 다른 순위 리스트가 모인다.
4단계 -- 순위 융합 및 재정렬(Rank Fusion & Re-ranking)
수집된 여러 순위 목록을 RRF(Reciprocal Rank Fusion) 와 같은 알고리즘으로 하나의 통합 순위로 재조정한다.
RRF는 어떻게 동작하는가?
RRF의 핵심 아이디어는 간단하다. 여러 검색기에서 일관되게 상위에 나타나는 문서에 더 높은 점수를 부여하는 것이다.
# RRF 점수 계산
# k는 순위 간 격차를 완화하는 상수 (보통 60)
rrf_score = sum(1 / (k + rank_i) for rank_i in ranks_from_each_retriever)
예를 들어 문서 A가 검색기 1에서 1위, 검색기 2에서 3위, 검색기 3에서 2위라면, 세 순위 모두 상위권이므로 높은 RRF 점수를 받는다. 반면 문서 B가 검색기 1에서 1위이지만 나머지에서는 50위 밖이라면, RRF 점수는 낮아진다.
이 방식은 Fusion Retrieval의 점수 정규화 방식과 비교했을 때 중요한 차이가 있다. Fusion Retrieval은 점수 자체를 정규화하여 합산하지만, RRF는 순위만 활용한다. 점수의 스케일이나 분포가 검색기마다 달라도 순위만 보면 되므로, 이질적인 검색기들을 결합하기에 더 유리하다.
5단계 -- 최종 결과 전달
종합적으로 평가된 최상위 문서를 LLM에게 컨텍스트로 전달한다.
장점
- 높은 강건성과 포괄성: 특정 검색기가 놓치는 정보를 다른 검색기가 보완하므로, 어떤 유형의 질문에도 안정적인 성능을 유지한다.
- 재현율(Recall) 극대화: 여러 접근 방식을 동시에 사용하므로, 질문과 관련된 잠재적 문서를 놓치지 않고 찾아낼 확률이 크게 향상된다.
- 모듈성 및 확장성: 시스템 구조를 수정하지 않고도 새로운 검색기를 추가하거나 기존 검색기를 제거하는 등 유연한 구성이 가능하다.
구현
LangChain에서는 EnsembleRetriever 클래스를 통해 이 로직을 비교적 간단하게 구현할 수 있다.
from langchain.retrievers import EnsembleRetriever
from langchain_community.retrievers import BM25Retriever
from langchain_community.vectorstores import FAISS
# 개별 검색기 생성
bm25_retriever = BM25Retriever.from_documents(documents)
faiss_retriever = faiss_vectorstore.as_retriever(search_kwargs={"k": 10})
# 앙상블 검색기 생성 (가중치 설정 가능)
ensemble_retriever = EnsembleRetriever(
retrievers=[bm25_retriever, faiss_retriever],
weights=[0.4, 0.6] # 각 검색기의 비중
)
Fusion vs. Ensemble: 어떤 차이가 있는가?
두 기법 모두 여러 검색 결과를 결합한다는 점은 같지만, 접근 방식에 차이가 있다.
| 구분 | Fusion Retrieval | Ensemble Retrieval |
|---|---|---|
| 검색기 수 | 벡터 + 키워드 (2개) | 제한 없음 (N개) |
| 융합 방식 | 점수 정규화 + 가중합 | 순위 기반 융합 (RRF) |
| 조절 파라미터 | alpha (점수 가중치) | 검색기별 가중치 |
| 유연성 | 두 검색 축의 비중 조절 | 검색기 자유롭게 추가/제거 |
간단히 말하면, Fusion Retrieval은 "점수를 섞는" 방식이고, Ensemble Retrieval은 "순위를 모아 투표하는" 방식이다.
Reranking: 검색 결과를 한 번 더 정제하기
하이브리드 검색으로 후보 문서를 확보했다면, 여기서 한 단계 더 나아갈 수 있다. Reranking(리랭킹) 은 1단계의 빠른 검색 결과를 대상으로, 더 정교한 2단계 모델을 투입하여 순위를 다시 매기는 후처리 기법이다.
1단계 검색기가 넉넉하게 확보한 후보군에서, 리랭커 모델이 질문과의 관련성을 정밀하게 재평가하여 진짜 관련 있는 문서만 골라낸다.
왜 이런 2단계 구조가 필요할까? 1단계 검색기는 수십만 개의 문서를 빠르게 훑어야 하므로, 속도를 위해 정확도를 일부 희생한다. 벡터 검색의 임베딩은 문서와 질문을 독립적으로 인코딩하기 때문에, 둘 사이의 미묘한 상호작용을 놓칠 수 있다. Reranking은 후보가 줄어든 상태에서 질문과 문서를 함께 분석하므로 더 정확한 판단이 가능하다.
작동 방식
1단계 -- 후보군 확보(Initial Retrieval)
벡터 검색이나 하이브리드 검색 같은 빠른 검색기로, 질문과 관련된 후보 문서를 넉넉하게 확보한다. 보통 상위 20개 정도를 가져온다.
2단계 -- 리랭커 모델 투입(Reranking)
확보된 후보 문서를 하나씩 원본 질문과 짝지어, 크로스-인코더(Cross-Encoder) 나 LLM 같은 고성능 리랭커 모델에 전달한다. 리랭커는 질문과 문서를 동시에 입력받아 관련성을 깊이 있게 평가한다.
이것이 1단계 검색과의 결정적 차이다. 1단계의 바이-인코더(Bi-Encoder) 는 질문과 문서를 각각 독립적으로 임베딩한 뒤 유사도를 비교하지만, 크로스-인코더는 질문과 문서를 하나의 입력으로 결합하여 처리하므로 두 텍스트 간의 세밀한 상호작용을 포착할 수 있다.
3단계 -- 정확한 순위 부여(Re-ordering)
리랭커가 매긴 새로운 관련성 점수를 기준으로 후보 문서의 순위를 다시 매긴다.
4단계 -- 최종 컨텍스트 구성(Final Selection)
재정렬된 순위에서 최상위 문서(보통 3~5개)만 선택하여 LLM에게 전달할 최종 컨텍스트를 구성한다.
구현 방식
리랭커 모델은 크게 두 가지 방식으로 구현할 수 있다.
1. LLM 기반 리랭킹
LLM에게 직접 프롬프트를 보내 문서와 질문의 관련성을 점수로 평가하도록 요청하는 방식이다. 구현이 간단하지만, API 호출 비용과 지연 시간이 문제가 될 수 있다.
2. 크로스-인코더(Cross-Encoder) 기반 리랭킹
sentence-transformers 라이브러리의 CrossEncoder 모델처럼, 리랭킹에 특화된 모델을 사용하는 방식이다. LLM 기반보다 빠르고 비용 효율적이며, 리랭킹 성능도 우수하다. 실제 프로덕션 환경에서는 이 방식이 더 많이 사용된다.
from sentence_transformers import CrossEncoder
# 크로스-인코더 모델 로드
reranker = CrossEncoder("cross-encoder/ms-marco-MiniLM-L-6-v2")
# 질문-문서 쌍의 관련성 점수 계산
query = "RAG에서 검색 정확도를 높이는 방법은?"
pairs = [(query, doc.page_content) for doc in candidate_docs]
scores = reranker.predict(pairs)
# 점수 기준으로 재정렬
reranked_docs = [doc for _, doc in sorted(
zip(scores, candidate_docs), reverse=True
)]
두 방식 모두 사용자 정의 Retriever 클래스로 래핑하면 LangChain의 QA 파이프라인에 쉽게 통합할 수 있다.
장점
- 정확도 향상: 1단계 검색기가 놓치는 미묘한 문맥적 관련성을 포착하여, LLM에게 전달되는 컨텍스트의 질을 크게 높인다.
- 효율적인 균형: 빠른 1단계 검색과 정확하지만 느린 2단계 리랭킹을 결합하여, 전체 시스템의 속도 저하를 최소화하면서도 높은 정확도를 달성한다.
- 노이즈 감소: 키워드는 일치하지만 실제로는 관련 없는 '가짜' 관련 문서를 효과적으로 걸러낸다.
전체 파이프라인: 검색부터 리랭킹까지
세 기법은 서로 경쟁 관계가 아니라, 단계적으로 결합하여 사용할 수 있다.
사용자 질문
|
v
[1단계] Fusion / Ensemble Retrieval
├── 벡터 검색 → 상위 결과
├── 키워드 검색 → 상위 결과
└── (기타 검색기) → 상위 결과
|
v
점수/순위 융합 → 후보 문서 20개
|
v
[2단계] Reranking
|
크로스-인코더로 정밀 평가 → 최종 문서 3~5개
|
v
LLM에게 컨텍스트로 전달
1단계에서 Fusion이나 Ensemble로 다양한 검색기의 결과를 넉넉하게 확보하고, 2단계에서 Reranking으로 정밀하게 걸러내는 구조다. 이 조합은 검색의 재현율(Recall)과 정밀도(Precision)를 동시에 높여주며, 실제 프로덕션 RAG 시스템에서 널리 채택되는 패턴이다.
정리
단일 검색 방식의 한계를 극복하기 위한 세 가지 전략을 살펴보았다.
- Fusion Retrieval: 벡터 검색과 키워드 검색의 점수를 정규화하여 가중합으로 융합한다. alpha 파라미터로 두 검색의 비중을 유연하게 조절할 수 있다.
- Ensemble Retrieval: 여러 독립적인 검색기의 결과를 RRF 같은 순위 기반 알고리즘으로 통합한다. 점수 스케일이 다른 이질적인 검색기들도 쉽게 결합할 수 있다.
- Reranking: 1단계 검색 결과를 대상으로 크로스-인코더 등 정교한 모델을 투입하여 순위를 재조정한다. 속도와 정확도의 균형을 잡는 핵심 후처리 단계다.
검색은 RAG의 성패를 가르는 단계다. 하이브리드 검색과 리랭킹을 적절히 조합하면, LLM이 더 정확하고 신뢰할 수 있는 답변을 생성하는 데 필요한 고품질 컨텍스트를 확보할 수 있다.