하이브리드 검색에서 RRF 기본값만으로는 왜 recall이 멈췄나?
결론부터 쓴다. RRF 자체는 고장 나지 않았다. BM25와 벡터 검색 결과를 순위 기반으로 합치는 Reciprocal Rank Fusion은 설계대로 동작했고, 문제는 "합친 뒤 끝냈다"는 데 있었다. 한 요약 벤치마크에서 hybrid RRF의 Recall@5는 0.695였지만, 같은 후보 목록에 재순위(reranking) 단계를 추가하자 0.816까지 올랐다 — denser.ai 요약 기준 상대 개선 약 17.4%. recall 정체의 원인은 fusion 공식이 아니라 fusion 다음 단계의 부재였다.
이 글의 핵심
- RRF는 순위만 합산하는 중립적 연산이며, 관련성을 재평가하지 않는다. recall 정체는 k값보다 fusion 이후 단계를 먼저 의심해야 한다.
- 금융·식별자 중심 도메인에서는 BM25가 구조적으로 유리하다. 임베딩 모델 교체로 해결되지 않는 영역이 있다.
- T2-RAGBench 요약 기준 hybrid RRF Recall@5 0.695 → +rerank 0.816, dense-only 0.587 대비 약 39.0% 개선.
- 1M 벡터 미만 규모에서 전용 벡터DB 이전은 recall 문제의 해법이 아니었다.
배포 흐름은 이렇다. 금융 문서 RAG 파이프라인에 PostgreSQL 기반 lexical 검색(tsvector 계열)과 pgvector HNSW 벡터 검색을 병렬로 실행하고, RRF(k=60)로 결과를 합쳤다. 1주 차 배포 직후에는 전체 recall 지표가 준수했다. 2주 차 질의 로그를 분석하자 특정 유형 — 계약서 조항 번호, 재무제표 수치, 상품 코드가 포함된 질의 — 에서 관련 문서가 상위권에서 밀려나는 패턴이 반복적으로 관측됐다. 평균 recall은 0.69대에서 멈춰 더 올라가지 않았다.
RRF의 k값이 너무 커서 하위 순위 문서가 묻혔나?
기각했다. RRF는 원점수가 아니라 순위 (r)에 대해 (\sum 1/(k+r))로 점수를 계산하며, k=60은 일반적으로 쓰이는 표준값이다 — thebuild.com. WANDS 전자상거래 벤치마크에서 기본 RRF의 NDCG는 0.7068이었고, 가중치 등을 튜닝한 hybrid는 0.7497까지 올라갔다 — techsy.io. 즉 k값 조정 자체가 recall 상한을 끌어올린 요인이 아니라, 별도의 가중 방식이 추가됐을 때 개선이 나타났다. k를 여러 값으로 바꿔가며 재측정했지만 recall 변화는 미미했고, 이 경로는 원인에서 제외했다.
벡터 임베딩 모델이 금융 용어를 못 담았나?
기각했다. 임베딩 모델을 교체하기 전에 이미 구조적 비대칭이 존재했다. 7,318개 금융 문서와 23,088개 질의로 10개 검색 전략을 비교한 T2-RAGBench 요약에 따르면, BM25는 Recall@20을 제외한 대부분 지표에서 dense 검색보다 높았다 — denser.ai. 금융 문서의 숫자·식별자·고유 표현은 lexical matching이 더 안정적으로 잡는다는 뜻이다. 임베딩 모델을 바꿔도 이 비대칭 자체는 사라지지 않았다. BM25가 애초에 강한 영역에서 벡터 검색을 더 정교하게 만드는 쪽은 문제의 본질을 비껴간 접근이었다.
PostgreSQL 인덱스 파라미터가 낮아서 recall 손실이 났나?
기각했다. HNSW ef_search를 단계적으로 올리며 재측정했지만 recall 변화는 눈에 띄지 않았다. 한 개발자 벤치마크 요약에서는 PostgreSQL 17, pg_textsearch 1.3.0, pgvector 0.8.2, nomic-embed-text 조합으로 BEIR SciFact 데이터를 돌렸을 때 hybrid RRF NDCG@10이 약 0.70, median latency 약 11.5ms로 보고됐고, 이는 BM25 단독(0.69)과 큰 차이가 없었다 — techsy.io. 인덱스 파라미터를 조정하는 작업은 지연과 비용에는 영향을 줬지만 recall 정체를 풀지는 못했다. 즉 병목은 ANN 검색 품질이 아니라 그 이후 단계에 있었다.
규모가 커져서 전용 벡터DB로 옮겨야 recall이 오르나?
기각했다. 다루는 chunk 수는 1M 벡터에 한참 못 미쳤고, 전용 vector DB와 pgvector의 처리량 격차가 보고되는 지점은 훨씬 큰 규모다. 한 비교 글은 1M 벡터에서 Qdrant 1,840 QPS, pgvector 약 640 QPS를 제시했지만, 다른 요약은 1M 미만·동일 컴퓨트·99% 정확도 조건에서 pgvector HNSW가 Qdrant보다 앞섰다고도 보고한다 — clarion.ai. 두 결과가 서로 다른 결론을 내리는 만큼, 데이터 차원·필터·동시성 조건이 다른 수치를 직접 비교해 의사결정을 내릴 근거는 없었다. 전용 DB 이전은 규모 문제가 아니라 recall 문제를 해결하지 못할 선택지로 판단해 보류했다.
결국 원인은 무엇이었나?
RRF가 BM25 상위권과 벡터 상위권을 순위로만 합쳤을 뿐, 두 리스트에서 겹치지 않는 후보들의 실제 관련성을 재평가하는 단계가 파이프라인에 없었다는 점이 원인이었다. 식별자 중심 질의는 BM25 상위권에, 패러프레이즈형 질의는 벡터 상위권에 정답이 있었지만, 질의가 두 성격을 섞어 가질 때 RRF 합산 순위가 실제 관련성 순서와 어긋나는 경우가 반복됐다. T2-RAGBench 요약에서 hybrid RRF Recall@5가 0.695에 머물다가 Cohere Rerank 추가 후 0.816으로 올라간 것도 같은 구조다 — 다만 이는 2차 요약 수치이므로 원 논문의 모델 버전·후보 수·평가 프로토콜은 별도로 확인이 필요하다.
재발 방지로 무엇을 바꿨나?
RRF로 합친 결과를 바로 반환하지 않고, 상위 20~50개 후보에 한해 재순위 단계를 추가했다. 후보 수를 늘릴수록 재순위 비용이 커지므로 BM25·벡터 각 리스트에서 가져오는 후보 수와 RRF 이후 재순위 투입 범위를 분리해 관리했다. 또한 질의 로그에 식별자 패턴(숫자·코드) 감지 플래그를 추가해, 특정 질의 유형에서 BM25 가중치가 과소 반영되는지를 별도로 모니터링하도록 했다. 2026년 기준으로 이 구성은 PostgreSQL 단일 스택 안에서 유지하고 있으며, 전용 벡터DB 전환은 tail latency와 인덱스 재구축 비용이 실제로 문제가 되는 시점까지 보류 대상으로 남겨뒀다.
다른 스택에서도 쓸 수 있는 교훈은?
RRF 같은 fusion 알고리즘은 두 검색기의 순위 정보를 합치는 중립적 연산이지, 관련성을 재평가하는 단계가 아니다. recall이 일정 수준에서 멈추면 먼저 확인할 것은 k값이나 인덱스 파라미터가 아니라, fusion 이후에 재순위 단계가 있는지, 그리고 질의 유형별로 어느 검색기가 강점을 가지는지다. BM25는 제품 코드·문서 번호·고유명사·정확한 법률·금융 용어에, 벡터 검색은 동의어·패러프레이즈·개념적 유사성에 유리하다는 구조적 비대칭은 임베딩 모델이나 인덱스 튜닝으로 사라지지 않는다 — denser.ai. 이 원칙은 Elasticsearch native RRF, Qdrant Query API, Weaviate fusion, Pinecone alpha 가중 중 어떤 제품을 쓰든 동일하게 적용된다.
핵심 정리
- RRF는 순위 합산 연산일 뿐이며 관련성 재평가가 아니다. recall 정체 시 재순위 단계 유무를 먼저 점검한다.
- 금융·식별자 중심 질의에서는 BM25가 구조적으로 유리하며, 임베딩 교체로 해결되지 않는다.
- T2-RAGBench 요약 기준 hybrid RRF Recall@5 0.695 → +rerank 0.816(2차 요약 수치, 원문 재확인 필요).
- 1M 벡터 미만 규모에서는 전용 vector DB 전환보다 fusion 이후 단계 점검이 우선순위다.
- 제품별 "hybrid"의 정의(RRF/alpha 가중/score normalization)가 다르므로 비교 시 fusion 알고리즘을 고정해야 한다.
자주 묻는 질문
RRF 대신 score normalization을 쓰면 이 문제가 안 생기나?
score normalization은 척도가 다른 BM25 점수와 cosine 유사도를 직접 정규화해야 하는 부담이 있고, 정규화 방식 자체가 또 다른 튜닝 변수가 된다. RRF가 순위만 쓰는 이유도 이 정규화 문제를 회피하기 위해서이므로, normalization으로 바꾼다고 재순위 단계의 필요성이 사라지지는 않는다.
재순위 단계를 추가하면 지연은 얼마나 늘어나나?
공개된 자료에는 동일 조건에서 재순위 전후의 p50·p95 latency를 함께 보고한 1차 벤치마크가 충분하지 않다. 후보 수를 20~50개로 제한해 재순위 비용을 통제하는 것이 일반적인 접근이며, 실제 적용 전에는 자체 환경에서 후보 수별 latency를 직접 측정해야 한다.
pgvector에서 전용 벡터DB로 넘어가야 하는 기준은 무엇인가?
단순 벡터 수가 아니라 목표 recall, tail latency, 관계형 필터·트랜잭션 결합 필요성, 인덱스 재구축 비용에서 경계가 갈린다. 50M 벡터·99% recall 조건의 한 테스트에서는 pgvectorscale의 QPS가 높았지만 p99 latency(74.60ms)는 Qdrant(38.71ms)보다 불리했고, 인덱스 구축 시간도 약 11.1시간으로 Qdrant의 약 3.3시간보다 길었다 — markaicode.com.
BM25와 벡터 중 하나만 강화하면 안 되나?
안 된다. WANDS 벤치마크에서 BM25 단독 NDCG 0.6983, 벡터 단독 0.6953으로 둘의 단독 성능은 비슷했지만 강점을 보이는 질의 유형은 서로 달랐다 — techsy.io. 한쪽만 강화하면 다른 쪽이 담당하던 질의 유형에서 손실이 발생하므로 병렬 실행과 fusion 구조 자체는 유지해야 한다.
더 알아보기
벡터 검색 설계의 핵심 판단축: 재현율, 지연, 비용의 균형 — 이 주제의 종합 가이드