Skip to content

[Feat] 하이브리드 검색 도입: 벡터 유사도에 희귀 단어 가중 키워드 점수 결합 #456

Closed

Description

@kangcheolung

📌 Description

배경

멘토링에서 "벡터 검색에 키워드 검색을 함께 쓰는 하이브리드 방식"을 고려해 보라는 개선안이 나왔습니다.
현재 검색은 벡터(코사인 유사도) 하나로만 순위를 매기고 거릅니다.

벡터 거리순 후보 (topK × 4) → 유사도 0.30 미만 제거 → 문서당 2개 상한 → 상위 topK

(VectorSearchQueryService.selectCandidates, VectorSearchRepository.findTopK)

RAG는 이 결과 중 상위 3개 청크만 프롬프트에 넣기 때문에, 정답 청크가 4위 이하로 밀리면 LLM이 정답을 보지 못합니다.

현재 상태 측정 (구현 전 기준선)

로컬 DB에 같은 5개 문서(청크 83개)를 올리고 질문 42개(정답 있음 34, 문서에 없음 8)로 벡터 단독을 측정했습니다.
화면(공유 DB)에서 7개 질문을 실행한 결과와 순위·점수가 같아 로컬 측정을 대리 테스트로 썼습니다.

최소 유사도 기준 정답이 1위 정답이 3위 안 MRR@10 식별자 질문 1위 문서에 없는 질문 → 결과 없음
0.30 (현재) 0.765 0.941 0.847 0.714 0 / 8
0.45 0.765 0.941 0.843 0.714 3 / 8
0.50 0.706 0.882 0.784 0.571 6 / 8
0.55 0.588 0.735 0.652 0.357 8 / 8

문제

  1. 짧은 용어·약어 질문에 약함: OM은 뭐야?의 정답 청크가 7위로 밀려 LLM이 "관련 문서를 찾지 못했습니다"라고 답함. 글자가 그대로 들어 있다는 정보를 순위에 쓰지 않음
  2. 기준값만으로는 해결 불가: 기준값을 올리면 문서에 없는 질문은 걸러지지만 식별자 질문의 1위 비율이 0.714에서 0.357로 떨어짐. 점수 하나로는 두 문제를 동시에 풀 수 없음
  3. 문서에 없는 질문에도 항상 5건이 반환됨 (AI가 이미 거절하므로 사용자에게 보이는 답변 영향은 작음)

반대로 바꿔 말한 질문은 현재도 잘 동작하므로 이 성능은 유지해야 합니다.

목표

기존 벡터 갈래는 그대로 두고, 질문 단어 일치를 작은 가산점으로 더해 벡터가 놓친 청크를 끌어올립니다.

설계 결정 (최종)

초기 계획은 pg_trgm 키워드 갈래 + RRF 결합이었으나, 처음 보는 질문(세트 2, 38개)에서 1위 정답이 28개에서 24개로 줄어 폐기했습니다.
RRF는 순위만 봐서 벡터 1위 정답을 밀어내고, 문장 전체 비교는 질문 말투까지 점수화했기 때문입니다.

  • 점수 결합: 순위 점수 = 벡터 유사도 + 0.1 × 단어 커버리지. 벡터가 순위를 주도하고 단어 일치는 최대 0.1점 보조
  • 단어 커버리지: 질문에서 핵심 단어만 뽑고(조사·질문 말투 제거, 중복 제거), 단어마다 IDF 배점 ln((N+1)/(n+0.5))(N=읽을 수 있는 색인 완료 문서의 청크 수, n=그 단어가 든 청크 수)을 준 뒤 청크가 가진 단어의 배점 합 ÷ 질문 단어 전체의 배점 합으로 계산
  • 관련도 판정: 벡터 유사도 ≥ 0.45 또는 커버리지 ≥ 0.50일 때만 결과로 인정
  • 단어 일치 방식: 정규식(~*) + pg_trgm GIN 인덱스. tsvector('simple')는 "고구려는", "@MessageMapping으로"처럼 조사가 붙은 토큰을 한 단어로 봐서 제외. 영문·숫자 단어는 단어 경계로 일치
  • 문서당 상한은 점수 결합 후에 적용
  • 롤백 스위치: search.hybrid.enabled(기본 켜짐). 끄면 기존 벡터 단독 검색
  • 검증 방식: 질문 세트 3개를 겹치지 않게 나누고, 마지막 세트는 설계와 판정 기준 4개를 결과를 보기 전에 파일로 고정한 뒤 처음 사용

범위

  • document_chunks.chunk_text에 pg_trgm GIN 인덱스 추가 (Flyway V46)
  • 단어 조회 쿼리 추가: 벡터 쿼리와 동일한 권한·상태 조건(permittedIds, INDEXED, current_version_id, 삭제 제외, 활성 임베딩, 같은 모델)
  • 질문 단어 추출, IDF 배점, 점수 결합, 관련도 판정, 결합 후 문서당 상한
  • 판정 기준값·가산점 가중치를 설정값으로 분리 (search.hybrid.*)
  • 전후 비교용 질문 세트와 측정 도구

범위 밖

  • 청킹 방식 변경 (문장·문단 경계, 작은 구간 병합, PDF 제목 인식)
  • 리랭커, 형태소 분석기 도입
  • 기존 문서 재임베딩 (임베딩은 바뀌지 않으므로 필요 없음)
  • 메타데이터 필터(JSONB) 기반 검색
  • 프런트엔드 표시 변경
  • 큰 코퍼스(수만 청크)에서의 정확도·응답 시간 측정, 갈래별 점수 로그 (후속)

✅ To-do

  • 운영 대상 OpenSQL에서 pg_trgm 사용 가능 여부 확인 (1.6, trusted, 미설치)
  • 로컬 DB에 같은 5개 문서를 올려 공유 DB 화면 결과와 동일한지 확인
  • 라벨된 질문 세트 3개 작성 (식별자, 바꿔 말함, 한영 혼합, 문서에 없음)
  • 구현 전 기준선 측정 (벡터 단독, 기준값 0.30~0.55)
  • Flyway: pg_trgm 확장과 chunk_text GIN 인덱스 (V46)
  • 단어 조회 Repository 쿼리 (벡터와 동일한 권한·상태 조건)
  • 질문 단어 추출, IDF 배점, 점수 결합과 결합 후 문서당 상한 Service 구현
  • 관련도 판정과 설정값 분리, 롤백 스위치
  • 권한 누수 방지 테스트 (접근 불가·삭제·비INDEXED·이전 버전·비활성 임베딩·다른 모델)
  • 단위 테스트, Repository 통합 테스트
  • 동일 질문 세트로 전후 재측정, 실제 AI 답변 12개 비교
  • 설계 문서 docs/design/kangcheolung-#456-hybrid-search.md 작성
  • 큰 코퍼스(청크 수만 개)에서 응답 시간 전후 비교 (DB 조회가 요청마다 2번 늘어남)
  • 공유 DB에 pg_trgm 확장 선설치 (머지 후, 새 코드로 공유 DB에 백엔드를 띄우기 전)
  • 화면에서 동일 질문으로 최종 확인

✅ 완료 기준

  • 질문 111개에서 정답이 1위인 질문이 벡터 단독보다 늘어난다 (82개에서 92개)
  • 사전에 고정한 기준으로 처음 보는 질문(세트 3, 39개)에서도 개선된다 (1위 28개에서 32개, 좋아진 8 / 나빠진 0)
  • 바꿔 말한 질문의 정확도가 벡터 단독 대비 떨어지지 않는다 (세트 3 기준: 1위 8 ≥ 7, 3위 안 9 ≥ 8)
  • 권한 없는 문서가 단어 조회 경로로 나오지 않는다

구현 PR: #482

Activity

  1. self-assigned this
    on Oct 9, 2026
  2. kangcheolung commented on Oct 9, 2026

    @kangcheolung
    MemberAuthor

    Branch feature/456 created!

  3. added 17 commits that reference this issue on Oct 10, 2026
  4. changed the title [-][Feat] 하이브리드 검색 도입: pg_trgm 키워드 갈래 + RRF 결합과 관련도 판정 개선[/-] [+][Feat] 하이브리드 검색 도입: 벡터 유사도에 희귀 단어 가중 키워드 점수 결합[/+] on Oct 11, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions