BM25 하이브리드 검색: 벡터 결합부터 재현율 검증까지

BM25와 벡터 검색을 결합하는 하이브리드 검색의 원리와 구현 방법을 살펴봅니다. RRF와 가중 점수의 차이, 정확한 식별자 처리, Elasticsearch·Weaviate·Pinecone 설정, 라벨링한 질의를 활용한 recall@k 비교까지 안내합니다.

게시일

작성자
BM25 하이브리드 검색: 벡터 결합부터 재현율 검증까지

검색이 질문의 뜻은 이해하면서 제품 코드는 놓친다면, 임베딩 모델을 교체하기 전에 어휘 기반 검색 경로부터 추가해 보십시오. 하이브리드 검색은 BM25 같은 키워드 검색의 후보 목록과 벡터 검색의 후보 목록, 이렇게 2개를 하나의 순위로 합칩니다. 먼저 상호 순위 융합(RRF)을 적용하고, 정확한 식별자를 처리하는 규칙은 명시적으로 유지합니다. 관련 문서를 라벨링한 질의 세트에서 기존에 잘 처리하던 질의를 해치지 않으면서 재현율이 높아질 때만 변경을 유지합니다.

문서 확인일: 2026년 10월 24일. 아래 구현 설명은 Elasticsearch, Weaviate, Pinecone의 현행 문서를 기준으로 합니다. 평가 설정은 실험을 시작하기 위한 제안이며, 실제로 측정한 개선 결과나 공급사의 벤치마크가 아닙니다.

BM25와 벡터를 결합한 하이브리드 검색이란?

하이브리드 검색은 표현 자체와 그 의미를 모두 검색에 반영합니다. 이 글에서 다루는 구조에서는 키워드 검색과 벡터 검색이 각각 순위가 매겨진 목록을 만들고, 융합 방식이 두 목록을 합칩니다. Weaviate의 개념 설명에서 두 검색 경로와 결합 방식을 확인할 수 있습니다.

같은 고객 지원 질문을 두 조사 담당자에게 맡긴다고 생각해 보십시오. 한 명은 색인에서 정확한 이름과 오류 문자열을 확인합니다. 다른 한 명은 표현이 달라도 관련된 내용과 답변을 찾습니다. 어느 한쪽에 두 역할을 모두 맡기는 대신, 각자 추린 후보를 합치는 방식입니다.

BM25는 문서에 포함된 질의어를 바탕으로 점수를 매기는 어휘 기반 랭킹 함수입니다. 단어의 출현 빈도와 문서 길이가 점수에 영향을 주므로, 일치하는 단어 수만 세는 방식보다 유용합니다. Elasticsearch는 텍스트 유사도의 기본값으로 BM25를 사용합니다. Elastic의 유사도 레퍼런스에서 점수에 영향을 주는 요소를 설명합니다.

벡터 검색은 콘텐츠를 숫자로 표현한 임베딩을 비교해 의미가 비슷한 내용을 찾습니다. 예를 들어 “자꾸 로그아웃됩니다”라는 질문을, 표현이 다른 세션 만료 문서와 연결할 수 있습니다. 시맨틱 검색 가이드에서 이 검색 방식을 설명합니다.

예시 질의가 ERR_SESSION_EXPIRED after login이라면, 어휘 기반 검색은 오류 문자열이 그대로 들어 있는 문서를 결과에 포함할 수 있게 합니다. 의미 기반 검색은 인증 세션과 갱신에 관한 안내를 보탤 수 있습니다. 최종 순서는 융합 과정에서 결정합니다. 이는 의도한 동작을 설명하는 예시이지, 별도 설정이 없는 저장소에서 특정 순위가 나온다는 주장은 아닙니다.

질의가 BM25와 벡터 검색 경로로 나뉜 뒤 융합 단계에서 합쳐져 최종 결과로 이어지는 구조
두 경로에서 각각 후보를 검색한 뒤 하나의 순위로 합칩니다.

BM25를 결합해도 정확한 식별자 처리 규칙은 필요합니다

키워드 검색 경로가 있다고 해서 식별자의 정확한 일치가 보장되지는 않습니다. 문자열을 검색 가능한 토큰으로 바꾸는 텍스트 분석 과정에서 색인에 저장되는 형태가 달라질 수 있기 때문입니다. 예를 들어 Elastic의 표준 분석기는 문서에 나온 예시에서 하이픈으로 연결된 구문을 분리합니다. 현재 설정한 분석기가 실제 SKU와 오류 코드를 어떻게 처리하는지 확인하십시오. 표준 분석기 문서에서 관련 내용을 볼 수 있습니다.

식별자의 표기 자체가 요청한 항목을 결정한다면, 식별자를 정확히 일치하는 값을 찾기 위한 전용 필드에 보관합니다. Elastic은 이런 구조화된 값에 사용하는 keyword 필드를 설명합니다. 가상의 SKU인 AX-BLUE를 검색했는데 설명이 비슷하다는 이유로 AX-GREEN 추천으로 슬쩍 바뀌어서는 안 됩니다. Keyword 필드 레퍼런스를 참고하십시오.

판단 기준: 제품이 정확한 항목 조회를 보장해야 한다면, 인식된 고유 식별자는 정확 일치 조회로 보냅니다. 해당 항목의 문제 해결 안내처럼 식별자에 더 넓은 요구가 결합된 질문에는 하이브리드 랭킹을 사용합니다.

RRF와 가중 점수, 검색 결과를 어떻게 합칠까요?

서로 다른 점수 척도를 보정하지 않고 비교 기준을 마련하려면 RRF부터 시작합니다. 가중 점수는 균형을 조정할 라벨이 있고, 점수 간 격차를 보존할 이유가 있을 때 검토합니다.

RRF는 점수 대신 순위를 사용합니다

**상호 순위 융합(reciprocal rank fusion, RRF)**은 문서가 등장한 각 목록에서 다음 기여도를 더합니다.

RRF(문서) = 각 검색 결과 목록의 1 / (c + 순위)를 모두 더한 값.

순위는 첫 번째 위치부터 시작합니다. 어떤 목록에 문서가 없으면 그 목록에서는 점수를 더하지 않습니다. Gordon V. Cormack, Charles L. A. Clarke, Stefan Büttcher는 SIGIR 2009 논문에서 RRF를 소개했습니다. 이들은 실험에서 상수를 60으로 고정했습니다. 논문은 이 상수를 k라고 표기하지만, 이 글에서는 recall@k의 결과 수와 혼동하지 않도록 c라고 부릅니다.

RRF는 높은 BM25 점수와 높은 벡터 점수가 같은 의미를 가질 필요가 없어 편리합니다. 각 목록에서의 위치만 중요합니다. Pinecone의 RRF 구현은 첫 순위부터 세는 방식과 목록에 없는 문서의 처리를 명확히 설명합니다.

다만 이 방식이 어떤 결과를 만드는지 확인해야 합니다. 가상의 예에서 문서 A는 키워드 목록의 맨 위에 있고 벡터 목록에는 없습니다. 문서 B는 두 목록에서 모두 두 번째입니다. c = 60이면 A의 점수는 1/61, B의 점수는 2/62입니다. B가 앞섭니다. 이는 공식으로 직접 계산한 예시이며 검색 벤치마크가 아닙니다.

가중 점수는 점수 간 차이를 보존합니다

가중 점수 융합은 관련성 점수의 척도를 비교 가능한 상태로 만든 뒤 합칩니다. 흔히 사용하는 형태는 다음과 같습니다.

점수 = alpha × 정규화한 벡터 점수 + (1 − alpha) × 정규화한 키워드 점수.

가중치 alpha가 두 점수의 비중을 조절합니다. 시작값을 0.5로 정하면 계수가 같아질 뿐, 두 검색 방식의 유용성이 같거나 최적의 설정이라는 뜻은 아닙니다. 정규화와 가중치 중 어느 하나만 바뀌어도 순위가 달라질 수 있으므로 둘을 함께 기록합니다.

Elasticsearch의 linear retriever는 minmax 같은 정규화 방식을 적용한 가중 합을 지원합니다. Weaviate의 상대 점수 융합은 각 결과 집합 안의 상대적인 점수 격차를 보존합니다. 순위 기반 융합은 그 격차를 버립니다.

첫 비교는 RRF로 시작하고, 평가용으로 따로 남겨 둔 질의에서 효과가 확인될 때만 가중 융합을 유지합니다. 원시 BM25 점수와 벡터 유사도 점수를 그대로 더한 뒤 의미 있는 합이라고 가정해서는 안 됩니다. 후보 깊이, 즉 후보를 몇 개까지 가져오는지도 기록합니다. 정규화할 목록에 어떤 결과가 들어가느냐에 따라 점수 범위가 달라질 수 있기 때문입니다.

하이브리드 검색은 언제 벡터 검색보다 유리한가요?

하이브리드 검색은 벡터 검색이 놓친 관련 자료를 키워드 검색이 찾고, 융합 후에도 그 자료가 유용할 만큼 높은 순위를 유지할 때 도움이 됩니다. 이는 직접 확인할 수 있는 조건이지, 언제나 정확도가 높아진다는 주장이 아닙니다.

다음과 같은 경우에 우선 시험해 볼 만합니다.

  • 고객 지원 검색: 정확한 오류 문자열, 함수명, 계정 관련 용어가 증상 설명과 함께 등장하는 경우입니다.
  • 카탈로그 검색: 정확한 제품명이나 SKU에 호환성, 교체 부품 같은 요구가 섞인 경우입니다.
  • 사내 지식 검색: 프로젝트 이름과 약어가 자연어 질문에 섞인 경우입니다.

Pinecone의 하이브리드 검색 개요는 의미를 찾는 밀집 벡터 검색과 함께 사용할 키워드 신호로 제품 코드, 오류 문자열, 이름을 구체적으로 꼽습니다.

식별자만 조회한다면 신뢰할 수 있는 정확 일치 조회부터 마련합니다. 현재 문서 집합과 질의를 벡터 검색이 이미 잘 처리한다면, 검색 문제가 있다는 근거를 확인한 뒤 다른 경로를 추가합니다.

실질적인 한계는 후보에 관련 문서가 포함되는 범위입니다. 융합은 각 검색 경로가 찾아온 문서의 합집합 안에서만 순서를 정할 수 있습니다. 어느 경로도 관련 구절을 찾지 못했다면 융합 방식을 바꿔도 되살릴 수 없습니다. 융합 전에는 있었던 구절이 최종 결과에서 빠졌다면 랭킹과 결과를 잘라내는 지점을 살펴봅니다.

현재 사용하는 저장소에 하이브리드 검색을 추가하는 방법

현재 저장소가 필요한 기본 검색 기능을 제공한다면 그대로 사용합니다. 데이터베이스와 검색 전략을 동시에 바꾸면 무엇이 개선에 기여했는지 가려내기 어렵습니다.

아래 표는 공급사의 우열이 아니라, 두 검색 결과를 어디에서 결합하는지 구분합니다.

저장소문서에 명시된 결합 방식설정할 항목
Elasticsearch내장 RRF 또는 정규화한 선형 융합하위 검색기, 후보 윈도, 필터, 융합 방식
Weaviate내장 BM25F와 벡터 융합검색 속성, 융합 방식, 명시적인 alpha 값
Pinecone내장 밀집·희소 벡터 결합, 클라이언트에서 BM25와 밀집 검색 결과를 RRF로 융합API 방식 선택, ID 유지, 해당 계층의 설정 조정

Elasticsearch에서 하이브리드 검색 구성하기

Elasticsearch는 텍스트 검색과 벡터 검색을 지원하는 검색 엔진입니다. rrf 검색기 아래에 standard 텍스트 검색기와 knn 벡터 검색기를 배치할 수 있습니다. 공식 RRF 검색기 레퍼런스에서 이 구조와 후보 윈도를 설명합니다.

Elastic의 하이브리드 검색 제품 페이지
Elasticsearch 하이브리드 검색

기존 색인에서는 검색할 텍스트와 해당 임베딩이 같은 문서 ID를 사용하도록 유지합니다. 두 검색 경로가 동일한 검색 대상 콘텐츠를 조회하게 한 뒤 융합합니다. 테넌트, 권한, 제품 버전에 따른 제한도 두 경로에 일관되게 적용합니다.

다음 요청은 ES_URL과 ES_API_KEY를 설정한 뒤 실행할 수 있습니다. 기존 docs 색인에 text 텍스트 필드와 embedding 밀집 벡터 필드가 있고, query-vector.json에 질의의 실제 임베딩 배열이 들어 있다고 가정합니다. 이 배열은 색인된 문서에 사용한 모델로 생성합니다. 명령 실행에는 jq와 curl이 필요합니다.

Bash
jq -n --slurpfile v query-vector.json \
  --arg q 'ERR_SESSION_EXPIRED after login' \
  '{
    size: 10,
    retriever: {
      rrf: {
        retrievers: [
          {standard: {query: {match: {text: $q}}}},
          {knn: {
            field: "embedding",
            query_vector: $v[0],
            k: 50,
            num_candidates: 100
          }}
        ],
        rank_window_size: 50,
        rank_constant: 60
      }
    }
  }' > hybrid-request.json

curl --fail-with-body "$ES_URL/docs/_search" \
  -H "Authorization: ApiKey $ES_API_KEY" \
  -H 'Content-Type: application/json' \
  --data-binary @hybrid-request.json

이는 평가를 위한 예시 설정입니다. size는 최종 결과 수이며, rank_window_size는 각 하위 검색기의 결과 중 몇 개까지 융합에 참여할 수 있는지 제어합니다. 이 예시에는 필터가 없으므로, 접근 권한을 제어하는 애플리케이션에 적용하려면 해당 검색 대상 제한을 추가해야 합니다. Elastic 문서는 각 하위 검색기의 명세에 따라 적용되는 상위 filter를 설명합니다.

주의할 점: 하위 검색기의 후보 윈도가 좁으면, 하이브리드 검색으로 되찾으려던 결과 자체가 탈락할 수 있습니다. 융합 방식을 조정하기 전에 각 경로의 후보와 최종 결과를 비교합니다. linear retriever로 전환한다면 가중치와 함께 정규화도 설정합니다.

Weaviate에서 하이브리드 검색 구성하기

Weaviate는 텍스트와 벡터 검색을 제공하는 데이터베이스이며, collection.query.hybrid로 하이브리드 검색을 실행합니다. 어휘 기반 검색에는 여러 필드를 지원하는 변형인 BM25F를 사용합니다. 하이브리드 질의 문서는 검색할 속성, 융합 방식 선택, 점수 설명을 다룹니다.

Weaviate의 하이브리드 검색 기능 페이지
Weaviate 하이브리드 검색

제목, 오류 텍스트, 제품명처럼 유용한 어휘 단서를 담은 필드를 query_properties로 지정합니다. 애플리케이션에서 임베딩을 생성한다면 질의 벡터를 전달하고, 그렇지 않다면 컬렉션에 설정된 벡터화 경로를 사용합니다. 융합 방식을 비교할 때는 fusion_type을 명시적으로 지정하고, 결과의 순위가 바뀐 이유를 진단할 때는 explain_score를 요청합니다.

alpha도 명시적으로 설정합니다. 0은 키워드 검색을, 1은 벡터 검색을 선택합니다. 현행 문서는 이 값을 생략했을 때 실제로 적용되는 가중치가 클라이언트에 따라 달라진다고 안내합니다. 기본 융합 방식은 relativeScoreFusion이며, rankedFusion도 사용할 수 있습니다. Weaviate의 융합 레퍼런스를 참고하십시오.

주의할 점: 가중치를 바꿔도 검색 대상에서 빠진 필드나 훼손된 식별자 토큰은 복구되지 않습니다. 임베딩 모델을 교체해야 한다고 결론 내리기 전에 어휘 기반 검색이 무엇을 기여하는지 확인합니다. 가중치 범위의 양 끝인 벡터 전용 검색과 키워드 전용 검색을 진단 기준으로 삼은 뒤, 결합한 설정을 평가합니다.

Pinecone에서 하이브리드 검색 구성하기

검색 데이터베이스 Pinecone은 서로 다른 하이브리드 구성을 문서화하고 있습니다. 서버의 밀집·희소 벡터 결합과 클라이언트의 RRF는 구현이 다르므로, 예시를 복사하기 전에 사용할 방식을 정합니다. Pinecone의 하이브리드 검색 개요를 참고하십시오.

필터링, 클라이언트 측 융합, 서버 측 결합을 구분하는 Pinecone 문서
Pinecone 하이브리드 검색 문서

BM25와 밀집 벡터 검색을 결합하려면 Documents API 방식으로 전문 검색과 밀집 벡터 검색을 각각 실행한 뒤, 순서가 매겨진 문서 ID를 애플리케이션에서 RRF로 합칩니다. Pinecone의 RRF 가이드에 이 클라이언트 측 구현이 나옵니다.

내장 기능으로 밀집 벡터와 희소 벡터를 결합하려면 Vectors API를 사용해 각 레코드에 두 표현을 함께 저장하고 하나의 질의에서 결합할 수 있습니다. 단일 색인 가이드는 vector_type="dense"와 metric="dotproduct"를 요구합니다. 두 표현의 상대적인 영향력은 클라이언트에서 질의 벡터의 크기를 조정해 정합니다.

희소 벡터는 표현 방식일 뿐, BM25로 생성했다는 증거는 아닙니다. 설계 문서에 어휘 모델이나 전문 검색의 점수 계산 방식을 명시합니다.

주의할 점: 키워드로 필터링한 뒤 밀집 벡터로 순위를 매기는 방식은 두 신호를 모두 랭킹에 반영하는 것과 다릅니다. 필터는 후보를 제거하므로, 이미 제외한 동의어 일치 결과를 되살릴 수 없습니다. 반드시 충족해야 하는 조건에는 필터를 사용하고, 어느 검색 경로에서든 결과를 보탤 수 있어야 한다면 융합을 사용합니다.

RAG에서 하이브리드 검색은 어떤 역할을 하나요?

하이브리드 검색은 **검색 증강 생성(retrieval-augmented generation, RAG)**의 검색 단계를 바꿉니다. RAG에서는 애플리케이션이 언어 모델의 답변 생성에 앞서 검색한 근거 자료를 제공합니다. 하이브리드 검색이 생성 단계를 대체하거나 답변의 정확성을 보장하지는 않습니다.

검색 단위는 일관되게 유지합니다. 키워드 검색은 문서 전체를, 벡터 검색은 구절을 반환한다면 융합 전에 ID의 대응 관계를 정해야 합니다. 그렇지 않으면 같은 출처의 여러 구절이 후보 목록을 차지하는 동안 다른 관련 출처가 밀려날 수 있습니다. 저장과 검색의 기본 구조는 벡터 데이터베이스 해설에서 다룹니다.

에이전틱 RAG는 어떤 검색 작업을 할지, 다시 검색할지를 판단하는 과정을 추가합니다. 하이브리드 검색기도 그 작업 중 하나가 될 수 있습니다. 고객 지원 봇이 정확한 오류 코드를 놓친다면, 오케스트레이션을 더하기 전에 검색기부터 수정하고 평가합니다.

검색 방식을 비교하는 동안 생성 모델과 프롬프트는 고정합니다. 정확한 용어를 포함한 질의 그룹의 결과는 나아졌지만 같은 뜻을 다르게 표현한 질의의 검색 성능이 여전히 낮다면, 동일한 라벨링 질의로 임베딩 모델을 비교합니다. 임베딩, 청킹, 융합을 동시에 바꾸면 무엇이 결과를 바꿨는지 알 수 없습니다.

도입 전후 recall@k로 재현율을 비교하는 방법

관련 문서를 라벨링한 소규모 질의 세트를 만들고, 키워드 전용·벡터 전용·하이브리드 검색을 같은 정답 기준으로 비교합니다. 유창하게 생성된 답변은 검색 성능 지표가 아닙니다.

Recall@k = 상위 k개 결과에서 찾은 관련 ID 수 ÷ 해당 질의에 대해 라벨링한 전체 관련 ID 수. 질의별 값을 평균하면 매크로 recall@k가 됩니다. 라벨이 불완전하다면 이 결과는 이미 관련성을 판정한 항목을 기준으로 한 재현율이라고 밝혀야 합니다. 문서 집합의 모든 관련 항목에 대한 재현율을 입증하는 것은 아닙니다.

  1. 작은 질의 세트부터 구성합니다

    답변 가능한 질의 60개로 시작합니다. 정확한 용어를 포함한 질의 20개, 같은 뜻을 다르게 표현한 질의 20개, 두 특성이 섞인 질의 20개로 나눕니다. 이는 권장하는 작업 부하 구분이며, 충분한 표본 크기를 보장하지는 않습니다. 가능하면 실제 검색 기록에서 가져오고, 현재 검색에 실패하는 이름, SKU, 오류 문자열을 포함합니다.

    각 그룹의 절반은 개발용으로, 나머지 절반은 별도의 평가용으로 남겨 둡니다. 의도적으로 답변할 수 없게 만든 질의 6개도 별도로 추가합니다. 이 질의들의 동작은 검토하되, 관련 항목 집합이 비어 있어 분모가 없으므로 재현율 계산에서는 제외합니다.

  2. 관련된 검색 단위에 라벨을 붙입니다

    각 질의의 ID와 그룹, 관련성이 확인된 모든 문서 또는 구절의 ID를 기록합니다. 세 검색 방식이 찾은 후보를 모으고, 어느 방식도 찾지 못한 알려진 정답도 추가합니다. 어떤 방식이 후보를 가져왔는지 보지 않은 상태에서 관련성을 판정합니다. 변하지 않는 ID를 사용하고, 처음부터 끝까지 문서 또는 구절 중 같은 단위를 유지합니다.

  3. 비교 조건을 고정합니다

    문서 집합의 스냅샷, 권한, 임베딩 모델, 청킹을 동일하게 유지합니다. 최종 결과의 k는 10, 하이브리드 검색의 각 경로에서 가져올 후보는 50개로 시작합니다. 이는 검토를 시작할 설정이지 최적값이 아닙니다. 첫 비교에서는 리랭킹을 끕니다.

    각 방식의 최종 ID 목록을 순서대로 내보냅니다. 융합 전의 키워드 후보 목록과 벡터 후보 목록도 저장하면, 후보 누락 문제와 랭킹 문제를 구분할 수 있습니다.

  4. 설정을 조정한 뒤 별도 평가 세트로 검증합니다

    개발용 절반에서 융합 방식과 후보 깊이를 조정합니다. 사용할 설정을 정한 뒤, 남겨 둔 평가용 절반을 한 번 평가합니다. 전체 평균과 함께 모든 질의 그룹의 결과를 보고하고, 성능이 나빠진 개별 질의를 살펴봅니다. 평균이 조금 높아졌다는 것은 추가로 살펴볼 근거일 뿐, 통계적 증명은 아닙니다.

같은 질의, 관련성 라벨, 재현율 지표로 벡터 검색과 하이브리드 검색 실행 결과를 비교하는 구조
두 실행에 동일한 질의와 관련성 라벨을 사용합니다. 이 그림은 측정 결과를 제시하지 않습니다.

로컬에서 재현율 평가 스크립트 실행하기

다음 코드를 evaluate_recall.py로 저장합니다. Python 3 표준 라이브러리만 사용합니다. qrels.jsonl에는 질의마다 qid, slice, relevant 필드를 가진 JSON 객체를 하나씩 기록합니다. 각 결과 파일은 질의 ID를 반환된 ID의 순서 있는 목록에 연결합니다.

예를 들어 라벨 레코드는 {"qid":"q-exact","slice":"exact","relevant":["guide-login"]}, 결과 파일의 내용은 {"q-exact":["guide-reset","guide-login"]} 형태일 수 있습니다. 이는 형식 예시이며, 테스트한 시스템의 검색 결과가 아닙니다. 라벨링한 모든 질의를 모든 결과 파일에 포함하고, 검색 결과가 없으면 빈 목록을 사용합니다.

Python
import json
import sys
from collections import defaultdict
from statistics import mean

qrels_path, bm25_path, vector_path, hybrid_path, cutoff = sys.argv[1:]
k = int(cutoff)
if k < 1:
    raise ValueError("k must be positive")

with open(qrels_path, encoding="utf-8") as f:
    labels = [json.loads(line) for line in f if line.strip()]
if not labels or len({r["qid"] for r in labels}) != len(labels):
    raise ValueError("Labels must be nonempty with unique query IDs")

runs = {}
for name, path in (("bm25", bm25_path), ("vector", vector_path),
                   ("hybrid", hybrid_path)):
    with open(path, encoding="utf-8") as f:
        runs[name] = json.load(f)
    missing = {r["qid"] for r in labels} - runs[name].keys()
    if missing:
        raise ValueError(f"{name} is missing query IDs: {sorted(missing)}")

groups = defaultdict(list)
skipped = 0
for row in labels:
    relevant = set(row["relevant"])
    if not relevant:
        skipped += 1
        continue
    scores = {}
    for name, run in runs.items():
        unique_ids = list(dict.fromkeys(run[row["qid"]]))
        scores[name] = len(relevant & set(unique_ids[:k])) / len(relevant)
    groups["overall"].append(scores)
    groups["slice:" + row["slice"]].append(scores)

if not groups:
    raise ValueError("No answerable queries to score")
for group, rows in sorted(groups.items()):
    values = {name: mean(r[name] for r in rows) for name in runs}
    delta = values["hybrid"] - values["vector"]
    report = " ".join(f"{name}={v:.3f}" for name, v in values.items())
    print(f"{group} n={len(rows)} recall@{k} {report} delta={delta:+.3f}")
print(f"Excluded no-answer queries: {skipped}")

별도로 남겨 둔 평가용 라벨과 내보낸 결과에 실행합니다.

Bash
python3 evaluate_recall.py qrels.jsonl bm25.json vector.json hybrid.json 10

스크립트는 중복 ID를 제거한 뒤 상위 결과를 잘라냅니다. 애플리케이션에서도 동일한 중복 제거 정책을 적용합니다. 벡터 검색 대비 하이브리드 검색의 변화와 키워드 검색 기준값을 함께 보여 주므로, 성능이 좋은 어휘 기반 검색이 비교에서 빠지지 않습니다.

관련 질의 그룹의 성능이 개선되고, 나빠진 결과도 제품 요구사항상 허용할 수 있을 때 배포합니다. 후보는 좋아졌는데 최종 재현율이 그대로라면 융합 방식이나 최종 결과 수를 조정합니다. 최종 재현율이 개선됐다면 결과 순서, 생성된 답변의 정확성, 종단 간 지연 시간을 별도로 검토합니다. 재현율은 이 항목들을 측정해 주지 않습니다.

과장된 기대를 걷어내고 다음 작업을 정하기

“하이브리드는 두 방식의 장점만 취한다”는 말은 실제 엔지니어링 판단을 가립니다. 유용한 자료를 더 폭넓게 찾을 수 있지만, 최종 목록에 영향을 주는 검색 경로도 하나 늘어납니다. RRF는 정확히 일치하는 결과를 뒤로 밀 수 있습니다. 가중 융합에서는 점수 척도나 정규화 동작의 차이로 한쪽이 유리해질 수 있습니다. 어느 방식도 빠진 원문 자료를 복구하지는 못합니다.

개발자는 지금 적용을 검토해야 합니다. 관련 있는 정확 용어 문서가 벡터 후보에 없다는 사실을 확인했다면, 어휘 기반 검색 경로를 추가합니다. 문서 식별 체계와 권한을 일관되게 유지하고, 라벨링한 질의로 비교합니다.

운영자는 배포를 미뤄야 합니다. 현재 문제가 오래된 콘텐츠, 권한 누락, 관련성 라벨 부족이라면 이런 선결 조건부터 해결한 뒤 검색 전략을 비교합니다.

신뢰할 수 있는 식별자 조회를 갖춘 팀은 당장 바꿀 필요가 없습니다. 더 폭넓은 탐색이 필요해질 때까지 안정적인 조회 경로를 유지하고, 자연어 질문에 대해서만 하이브리드 검색을 별도로 평가합니다.

다음 주 첫 작업으로 검색에 실패한 질의를 모으고, 나와야 할 출처에 라벨을 붙인 뒤, 기존 벡터 검색기와 키워드 검색 기준선을 나란히 실행해 보십시오. 각 경로가 무엇을 보태는지 확인한 다음 융합을 추가합니다. 따로 남겨 둔 평가 질의에서 효과를 입증한 설정을 유지합니다.

자주 묻는 질문

BM25 자체가 하이브리드 검색인가요?

아닙니다. BM25는 어휘 기반 랭킹 함수입니다. 그 결과를 벡터 검색 경로의 결과와 결합하는 것이 하이브리드 검색의 한 형태입니다.

ChatGPT는 시맨틱 검색을 사용하나요?

생성된 답변만 보고 검색 알고리즘을 확인할 수는 없습니다. 언어 모델을 활용해 직접 구축한 시스템이라면 BM25, 벡터, RRF를 쓴다고 추측하기보다 실제로 설정된 검색 경로를 확인합니다.

시맨틱 검색이란 무엇인가요? 예시로 설명해 주세요

“자꾸 로그아웃됩니다” 같은 질의가 표현이 다른 세션 만료 안내와 연결될 수 있습니다. 이것이 의미 기반 검색입니다. 정확한 오류 문자열을 찾으려면 여전히 어휘 기반 매칭이 필요할 수 있습니다.

하이브리드 검색의 예시를 볼 수 있나요?

ERR_SESSION_EXPIRED after login에 대해 키워드 검색 경로는 오류 문자열이 그대로 일치하는 자료를 찾고, 벡터 검색 경로는 관련된 세션 안내를 찾습니다. 후보를 합친 뒤 기대한 출처가 나오는지 관련성 라벨과 대조합니다.

하이브리드 검색과 벡터 검색의 핵심 차이는 무엇인가요?

벡터 검색만 사용하면 임베딩 유사도로 자료를 찾습니다. 이 글에서 설명한 하이브리드 방식은 키워드 경로로도 검색한 뒤, 두 경로의 근거를 하나의 결과 목록에 합칩니다.

하이브리드 검색과 시맨틱 검색은 같은 뜻인가요?

아닙니다. 시맨틱 검색은 하이브리드 검색을 구성하는 한 요소가 될 수 있습니다. 하이브리드는 검색 신호의 결합을 뜻하며, 특정한 융합 알고리즘 하나를 가리키지는 않습니다.

Google Search도 시맨틱 검색인가요?

Google Search는 랭킹 시스템에 의미 이해를 활용합니다. Google의 랭킹 시스템 가이드는 BERT와 신경망 매칭을 설명합니다. 그렇다고 전체 구조가 이 글에서 다룬 BM25와 벡터의 결합 방식이라는 뜻은 아닙니다.

RAG와 시맨틱 검색은 어떻게 다른가요?

RAG는 근거 자료를 검색하고 답변을 생성할 때 활용합니다. 시맨틱 검색은 그 자료를 제공할 수 있는 검색 방식입니다. RAG 애플리케이션은 키워드 검색이나 하이브리드 검색도 사용할 수 있습니다.

하이브리드 검색은 어떤 원리로 작동하나요?

이 가이드의 구조에서는 검색 가능한 콘텐츠에 키워드 검색과 벡터 검색을 실행하고, 변하지 않는 ID를 기준으로 후보를 합친 뒤, RRF나 의도적으로 설계한 가중 점수 방식으로 순위를 정합니다. 배포 전에 라벨링한 질의로 최종 결과를 평가합니다.

뉴스레터로 실무 개발 가이드 받아보기.

게시일
카테고리
Build
NocoDB·Baserow로 살펴보는 2026년 Airtable 대안 7종 비교

NocoDB·Baserow로 살펴보는 2026년 Airtable 대안 7종 비교

NocoDB와 Baserow부터 SeaTable, Grist, Smartsheet, Notion, Softr까지 Airtable 대안을 비교합니다. 사용자별 비용, 행 한도의 적용 범위, API와 자동화, 자체 호스팅 조건을 살펴보고 데이터베이스와 업무 흐름에 맞는 선택을 확인합니다.2026년 11월 10일Build
프록시 서버 추천 2026: Bright Data부터 NetNut까지 7개 업체 비교

프록시 서버 추천 2026: Bright Data부터 NetNut까지 7개 업체 비교

프록시 서버 선택을 위해 Bright Data, Oxylabs, Decodo, Webshare, ThorData, IPRoyal, NetNut을 비교합니다. 주거용·데이터센터 가격과 무료 체험, KYC 제한, 세션 제어를 살펴보고 실제 수집 작업과 검증된 결과에 맞는 서비스를 고릅니다.2026년 11월 10일Build
AI 에이전트 권한 관리, NVIDIA OpenShell로 시작하기

AI 에이전트 권한 관리, NVIDIA OpenShell로 시작하기

AI 에이전트에 작업 공간과 서비스 접근 권한을 주면서 파일, 네트워크, 인증 정보의 경계를 설정하는 NVIDIA OpenShell을 살펴봅니다. 지원 환경과 설치 절차, 실행 중 바꿀 수 있는 정책, 허용·거부 테스트, 팀 도입 사례와 운영상 한계를 확인합니다.2026년 11월 9일Build
CodeRabbit pricing: 좌석별 가격부터 리뷰 한도·추가 요금까지

CodeRabbit pricing: 좌석별 가격부터 리뷰 한도·추가 요금까지

CodeRabbit 가격을 좌석 수와 실제 리뷰 사용량 기준으로 비교합니다. Essentials·Team·Advanced의 월간·연간 비용, 무료 및 오픈 소스 조건, 리뷰 한도와 파일·에이전트·보안 스캔 추가 요금을 살펴보고 팀에 필요한 요금제와 지출 한도를 정해 보십시오.2026년 11월 9일Build
백신 프로그램 추천: 2026년 중소기업을 위한 6가지 선택지

백신 프로그램 추천: 2026년 중소기업을 위한 6가지 선택지

중소기업 백신 프로그램 추천 가이드입니다. Bitdefender, Microsoft Defender, ESET, Sophos, CrowdStrike, Malwarebytes의 가격과 기기 한도, OS별 보호 범위를 비교하고 경보 담당자와 관리형 대응 서비스가 필요한 시점을 짚습니다.2026년 11월 8일Build
AI 에이전트 관리, Paperclip AI로 어디까지 가능할까요?

AI 에이전트 관리, Paperclip AI로 어디까지 가능할까요?

Paperclip AI로 여러 AI 에이전트의 작업과 예산, 검토를 관리하는 방법을 살펴봅니다. 공식 설치 절차와 지원 런타임, OpenClaw·Hermes Agent와의 차이, 권한과 비용 통제의 한계를 짚고 주말 시험 운영에서 무엇을 확인해야 할지 안내합니다.2026년 11월 7일Build
Supabase란? AI로 만든 앱의 백엔드, 연결부터 운영까지

Supabase란? AI로 만든 앱의 백엔드, 연결부터 운영까지

Supabase는 AI로 만든 앱에서 어떤 일을 할까요? Postgres 데이터베이스부터 인증, 파일 저장, RLS, AI 빌더 연동까지 살펴봅니다. 로그인과 데이터 접근 권한의 차이, 프로젝트 관리 주체, 백업과 이전 범위와 고객 데이터를 받기 전에 확인할 사항을 정리합니다.2026년 11월 7일Build
AI 코딩 에디터 추천: Cursor부터 JetBrains까지, 내 작업에 맞는 선택

AI 코딩 에디터 추천: Cursor부터 JetBrains까지, 내 작업에 맞는 선택

AI 코딩 에디터 선택에는 모델 이름보다 작업 위임과 리뷰 방식이 중요합니다. Cursor, VS Code와 Copilot, Devin Desktop, Zed, Kiro, JetBrains AI의 가격·무료 플랜·로컬 모델·BYOK·조직 통제를 비교해 개발 환경에 맞는 선택을 돕습니다.2026년 11월 6일Build
뉴스레터

매주 일요일, 한 통의 편지.뜨거운 의견이 아닌, 돌아가는 시스템.

주간 발행. 스팸 없음. 언제든 해지 가능합니다.