본문 바로가기

AI·LLM 엔지니어링

RAG Hybrid Search 실전 가이드 — Dense·BM25·RRF로 검색 정확도 높이기

반응형

RAG 검색에서 벡터 유사도만 사용하면 의미가 비슷한 문서는 잘 찾지만 제품 코드, 오류 번호, 사람 이름, 정확한 버전처럼 문자열 일치가 중요한 질문을 놓칠 수 있습니다. 반대로 BM25만 사용하면 같은 뜻을 다른 단어로 표현한 질문에 약합니다.

Hybrid Search는 Dense Vector의 의미 검색과 Sparse/BM25의 키워드 검색을 함께 사용합니다. 중요한 점은 두 점수를 단순히 더하는 것이 아니라, 서로 다른 점수 척도를 안전하게 결합하고 실제 평가 데이터로 효과를 확인하는 것입니다.

RAG 전체 구조와 Chunking이 먼저 필요하다면 RAG 구현 가이드를, 운영 지표는 RAG 운영 가이드를 참고하세요.

핵심 요약

  • Dense는 의미·다국어·표현 변형에 강하고, BM25는 정확한 키워드·코드·고유명사에 강합니다.
  • Cosine과 BM25 점수는 범위가 달라 원시 점수를 바로 가중합하면 불안정할 수 있습니다.
  • RRF는 점수 대신 각 검색기의 순위를 사용해 결과를 결합합니다.
  • Hybrid Search가 항상 단일 검색보다 좋은 것은 아니며, 각 검색기와 결합 결과를 분리 평가해야 합니다.
  • 후보 Recall을 높인 뒤 Cross-Encoder나 ColBERT로 최종 정밀도를 높일 수 있습니다.

1. Dense와 Sparse가 놓치는 질문

질문 유형DenseBM25/Sparse

“로그인이 안 돼요” ↔ “인증 실패 해결” 강함 표현이 다르면 약함
CUDA error 802 숫자 의미를 흐릴 수 있음 정확 문자열에 강함
제품명·약어·API 함수명 학습 여부에 따라 편차 원문 일치에 강함
자연어로 풀어쓴 모호한 질문 강함 핵심 단어가 없으면 약함

실제 서비스의 질문은 네 유형이 섞입니다. 그래서 한 검색기를 무조건 선택하기보다 서로 다른 실패 패턴을 보완하도록 설계합니다.

2. RRF로 순위를 결합하는 이유

Dense 검색의 Cosine Similarity는 보통 제한된 범위에 있고 BM25 점수는 코퍼스와 쿼리에 따라 크게 변합니다. 다음처럼 원시 점수를 바로 더하면 가중치의 의미가 쿼리마다 달라질 수 있습니다.

주의가 필요한 방식
final = 0.7 × cosine_score + 0.3 × bm25_score

Reciprocal Rank Fusion은 원시 점수 대신 순위를 사용합니다.

RRF_score(d) = Σ 1 / (k + rank_i(d))

여러 검색 결과에서 상위에 반복 등장한 문서가 높은 점수를 받습니다. k는 상위 순위 차이의 민감도를 조절하는 상수입니다. 구현체마다 순위 시작값과 기본 k가 다를 수 있으므로 라이브러리 문서를 기준으로 봐야 합니다.

3. Qdrant식 구현 흐름

from qdrant_client import models

result = client.query_points(
    collection_name="knowledge",
    prefetch=[
        models.Prefetch(query=dense_vector, using="dense", limit=30),
        models.Prefetch(query=sparse_vector, using="sparse", limit=30),
    ],
    query=models.FusionQuery(fusion=models.Fusion.RRF),
    limit=10,
    with_payload=True,
)

예제의 숫자는 출발점일 뿐입니다. 최종 10개가 필요하다고 후보도 10개만 가져오면 두 검색기가 서로 보완할 공간이 부족할 수 있습니다. Dense와 Sparse 후보 폭을 20·50·100처럼 비교하고 Recall과 지연시간을 함께 봅니다.

4. 한국어 BM25에서 자주 생기는 문제

한국어는 조사와 어미가 붙고 띄어쓰기 변형이 많아 단순 공백 토큰화만으로 검색 품질이 불안정할 수 있습니다.

  • “권한이 없습니다”와 “권한 없음”이 같은 핵심어를 공유하도록 토큰화를 점검합니다.
  • 제품 코드·버전·에러 번호는 형태소 분석에서 삭제되지 않도록 보존합니다.
  • 불용어 목록이 도메인 핵심어를 제거하지 않는지 확인합니다.
  • 색인과 질의에 같은 정규화 규칙을 적용합니다.
  • 영문 대소문자, 하이픈, 점이 포함된 API 이름의 처리 규칙을 고정합니다.

5. 평가 데이터와 지표

최소한 질문, 관련 문서 ID, 관련도 등급을 가진 작은 평가 세트를 만듭니다. 검색 방식별로 같은 세트를 실행해 다음을 기록합니다.

지표확인하는 것

Recall@k 관련 문서가 후보 k개 안에 들어왔는가
MRR@k 첫 관련 문서가 얼마나 앞에 있는가
nDCG@k 여러 관련도 등급을 순서에 반영했는가
P95 latency 결합 검색이 사용자 지연시간을 넘지 않는가
Answer groundedness 최종 답변이 검색 근거를 실제로 사용했는가

평가 세트는 고유명사, 정확한 코드, 자연어 의역, 짧은 질문, 긴 질문을 나눠 구성합니다. 전체 평균만 보면 특정 질문 유형이 악화된 사실을 놓칠 수 있습니다.

6. 디버깅 순서

  1. Dense-only와 Sparse-only 결과를 각각 저장합니다.
  2. 한쪽 검색기가 단독으로도 나쁘다면 결합 전에 임베딩·토큰화를 수정합니다.
  3. 두 검색기가 괜찮은데 결합만 나쁘다면 후보 폭과 RRF 가중치를 확인합니다.
  4. 문서가 후보에는 있지만 순위가 낮다면 Reranker를 검토합니다.
  5. 문서가 아예 없다면 Chunking·메타데이터 필터·색인 누락을 확인합니다.

7. 운영 체크리스트

  • 색인 버전과 Dense·Sparse 모델 버전을 함께 기록합니다.
  • 검색기별 후보와 RRF 최종 순위를 추적 가능한 로그로 남깁니다.
  • 문서 갱신 시 두 색인이 같은 시점에 반영되는지 확인합니다.
  • 임베딩 모델·Chunking·코퍼스가 바뀌면 결합 가중치를 다시 평가합니다.
  • 정확도 이득과 P95 지연시간·비용을 함께 승인 기준으로 둡니다.

공식 자료

정리: Hybrid Search의 목적은 점수 두 개를 섞는 것이 아니라 서로 다른 검색 실패를 보완하는 것입니다. Dense와 Sparse를 각각 고친 뒤 RRF로 결합하고, 질문 유형별 Recall@k와 지연시간으로 검증해야 안정적인 개선이 됩니다.

반응형