docs: 하이브리드 검색 keyword recall=0 및 grounding 실패 원인 확정 리서치

- keyword recall 0: simple 토크나이저가 한국어 조사 분리 불가 (실측 recall 27%)
- grounding 실패 3계층: 검색(8건), 라우팅 마커(3건), LLM 판단(정상)
- prefix 매칭(:*) 적용 시 recall 27%→90% 즉시 개선 가능

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
happybell80 2026-03-21 15:44:44 +09:00
parent 0a967ce7c1
commit 3e78c7cf6e

View File

@ -0,0 +1,113 @@
tags: [research, rag, companyx, hybrid, keyword, tsvector, grounding]
# 260321 하이브리드 검색 keyword recall=0 및 grounding 실패 원인 확정 리서치
## 목적
- 3중 검색(벡터+키워드+그래프) 구현 후 1차 검증에서 드러난 두 가지 실패의 직접 원인을 확정한다.
- 실패 1: 샘플 질문 16개 keyword recall이 0
- 실패 2: 재오픈 20개 중 인덱싱 문서가 있는데도 grounding 실패하는 케이스
## 사실 (Facts)
### 1. keyword recall = 0의 직접 원인: `simple` 토크나이저의 한국어 교착어 미처리
tsvector 트리거가 `to_tsvector('simple', chunk_text)`로 생성한다. `simple` 설정은 공백 기준 분리 + 소문자 변환만 수행하고 형태소 분석을 하지 않는다.
한국어는 교착어로 어간에 조사/어미가 항상 붙기 때문에 다음이 발생한다:
| 원문 | tsv 토큰 | tsquery 검색어 | 결과 |
|------|----------|---------------|------|
| `투자를 하다` | `'투자를'` | `'투자'` | 불일치 |
| `개인투자조합을 운용` | `'개인투자조합을'` | `'개인투자조합'` | 불일치 |
| `투자 유치` | `'투자'` | `'투자'` | 일치 |
DB 실측 (키워드 '투자', Company X 청크 대상):
| 방식 | 건수 | recall |
|------|------|--------|
| `chunk_text LIKE '%투자%'` | 1,099 | 100% |
| `tsv @@ to_tsquery('simple', '투자')` (현재) | 299 | 27% |
| `tsv @@ to_tsquery('simple', '투자:*')` (prefix) | 986 | 90% |
- **주요 손실(~63%)**: 조사 미분리 — `'투자를'`, `'투자에'`, `'투자와'``'투자'`와 exact match 실패
- **추가 손실(~10%)**: 복합어 미분리 — `'개인투자조합'`, `'총투자금액'`이 하나의 토큰으로 저장되어 prefix로도 불가
`_build_keyword_tsquery()`는 Python에서 정규식 `[0-9A-Za-z가-힣]+`로 깨끗한 어절 토큰을 추출하지만, DB의 tsv 토큰에는 조사가 붙어 있어 구조적으로 불일치.
### 2. grounding 실패의 세 가지 계층
재오픈 20개 중 expect_evidence=True인 11건을 분석한 결과, 실패는 세 계층에서 발생한다:
#### A. 검색 계층 실패 (8건) — 근본 원인
검색이 관련 문서를 찾지 못함:
- keyword_score 전부 0.000 (위 원인)
- 벡터 검색도 의미적으로 엉뚱한 문서를 반환 (본점 소재지 변경 공문, 고유번호증 등)
- **옐로펀치 협약 문서를 원문 그대로 쿼리해도 top-5에 미등장** → 임베딩 공간에서 해당 청크가 쿼리와 먼 위치에 있거나 인덱싱 문제
해당 질문들:
- 오늘전통+옐로펀치 근거
- Company X 프로그램 운영 흐름
- 주요 프로그램 목록
- 옐로펀치 협력 범위
- 오늘전통 대상/조건
- Company X 조직도/팀 구성
- Company X 슬로건/미션
#### B. 라우팅 계층 실패 (3건) — 마커 미매칭
`should_handle_companyx_grounding()`의 intent_markers/domain_markers에 걸리지 않아 grounding 경로 자체에 미진입:
| 질문 | 이유 |
|------|------|
| IR 채널 투자사 리스트 | `IR 채널` 마커 없음 |
| 프로그램 참여 기업 선정 기준 | 일반 질문, 마커 없음 |
| 프로그램 운영 기간/일정 | 일반 질문, 마커 없음 |
이 케이스는 일반 챗봇 경로로 빠져서 "홍태주님, 확인해보니..." 톤으로 응답.
#### C. LLM 판단 계층 (정상 동작)
검색이 무관한 문서를 반환하면 LLM이 `failure_reason`을 설정하여 실패 응답 생성. 이는 엉뚱한 문서로 답변하는 것보다 나은 정상 동작.
## 원인 요약
| 실패 | 직접 원인 | 계층 |
|------|----------|------|
| keyword recall = 0 | `simple` 토크나이저가 한국어 조사 분리 불가 | DB/인덱스 |
| 인덱싱 문서 있는데 검색 실패 | 벡터 임베딩이 해당 청크를 근접 이웃으로 잡지 못함 + keyword 보완 불가 | 검색 |
| 3건 grounding 미진입 | intent/domain 마커에 일반 질문 패턴 미포함 | 라우팅 |
## 해결 방향 (구현은 별도 계획)
### keyword recall 개선 옵션
| 옵션 | 방식 | 장점 | 단점 |
|------|------|------|------|
| prefix 매칭 | tsquery에 `:*` 접미 | 즉시 적용, recall 27%→90% | 복합어 내부 매칭 불가 |
| trigram (pg_trgm) | `chunk_text % :query` | 부분 문자열 매칭 가능 | GIN 인덱스 추가 필요, 정밀도 낮음 |
| 한국어 형태소 분석 | mecab + textsearch_ko | 근본 해결 | 확장 설치 필요 (51123 서버) |
| 하이브리드 보완 | ILIKE fallback | 간단 | 인덱스 미사용, 느림 |
### 벡터 검색 품질 개선
- 200개 인덱싱 대상에 옐로펀치 협약/오늘전통 문서가 포함되어 있는지 확인 필요
- 포함되어 있다면 청킹 경계에서 핵심 내용이 잘리는지 확인
- 임베딩 모델(Gemini 768d)의 한국어 고유명사 처리 능력 검증
### 라우팅 마커 확장
- Company X team_id 사용자의 질문은 마커 없이도 grounding 시도하는 옵션 검토
- 또는 일반 질문 패턴 마커 추가 (단, 하드코딩 확장은 금지 원칙에 저촉)
## 검증에 사용한 데이터
- 테스트 결과: `/home/admin/robeing/tests/results/hybrid_vs_vector_comparison.json`
- 검증 스크립트: `/home/admin/robeing/tests/test_companyx_rag_hybrid.py`
- DB 실측: 51123 서버 `main_db.team_document_chunk` (team_id: `79441171-...`)
## 관련 문서
- [260320 로빙 다형식문서 RAG 적용1 계획](../../plans/260320_로빙_다형식문서_RAG_적용1_계획.md)
- [260315 companyx rag 답변합성 시나리오동시종결 리서치](../260315_companyx_rag_답변합성_시나리오동시종결_리서치.md)