어디살지 POC chat-v2 — 전수 누락 점검 (29 항목) + OpenRouter 한계
- Gemini Embedding
task_type무시 — query/doc 같은 텍스트 cosine = 1.0 (비대칭 임베딩 적용 안 됨) - Cohere Rerank
documents=dict[] + rank_fields미지원 (ZodError 400 — string[] 만 허용) - 두 P0 옵션 적용은 직접 provider 호출로 전환 시에만 가능 (Vertex AI key + Cohere key)
- 이미 적용한 P0 —
area_keywordsMatchText + PREFIX tokenizer index → area@10 11% → 43% (4배)
1. 전수 매트릭스 — 29 항목 (5축)
| # | 축 | 옵션·파라미터 | 현재 | 공식 권장 | 등급 | 비고 |
|---|---|---|---|---|---|---|
| A1 | Qdrant | area_keywords MatchText | ✅ 적용 | FieldCondition+MatchText | DONE | area@10 ↑4배 |
| A2 | Qdrant | address PREFIX index | ✅ 적용 | 한국어 부분 매칭 | DONE | "강남" → "강남구" 매칭 |
| A3 | Qdrant | deposit·rent·room_count·property_type 인덱스 | ❌ 0개 | numeric/keyword 인덱스 | P1 | N 증가 시 필수 |
| A4 | Qdrant | Sparse vector (BGE-M3 native) | ❌ 별도 BM25 | dense+sparse native fusion | P2 | 현재 RRF 도 우수 |
| A5 | Qdrant | Quantization (INT8 SQ) | ❌ | 큰 컬렉션 | P3 | N=100 불요 |
| A6 | Qdrant | search_params.ef (runtime) | ❌ | recall 튜닝 | P2 | ef_construct=100 기본 |
| A7 | Qdrant | with_vectors (MMR 시만) | ✅ | OK | OK | |
| A8 | Qdrant | score_threshold | 0.0 (env) | dynamic | OK | 0.30 → 0.0 적용 |
| B1 | Gemini Emb | task_type RETRIEVAL_QUERY/DOC | ❌ OpenRouter 무시 | 비대칭 임베딩 ↑5-10% | P0 | provider 직접 호출 필요 |
| B2 | Gemini Emb | output_dimensionality (Matryoshka) | 3072 고정 | 1024 truncate | P2 | 비용 1/3 |
| B3 | Gemini Emb | title (document) | ❌ | 문서 핵심 키워드 boost | P3 | |
| B4 | Gemini Emb | batch_size | 32 | ≤100 (quota) | OK | |
| C1 | Cohere Rerank | rank_fields=[address,description] | ❌ OpenRouter 미지원 | 필드별 가중 | P0 | Cohere 직접 호출 필요 |
| C2 | Cohere Rerank | return_documents=True | False | 디버깅 시 활성 | P3 | |
| C3 | Cohere Rerank | max_chunks_per_doc | ❌ | 긴 문서 분할 | P3 | |
| D1 | Hybrid | RRF k | 60 | 60 표준 | OK | |
| D2 | Hybrid | candidate_pool SearchService | top_k×3 | top_k×5-10 | P1 | recall ↑ |
| D3 | Hybrid | candidate_pool qdrant_adapter | 60 고정 | top_k 비율 | P2 | |
| D4 | Hybrid | MMR λ | 0.85 (정확도 우선) | 0.7~0.85 | OK | |
| D5 | Hybrid | reranker top_n | top_k×3 | OK | OK | |
| E1 | BM25 | tokenizer Kiwi | ✅ Kiwi 형태소 | 한국어 적합 | OK | |
| E2 | BM25 | k1, b | 기본 (1.5, 0.75) | 도메인 튜닝 가능 | P3 | |
| F1 | Groq | tool_choice="required" | "auto" | 단발 sanity 모드 | P1 | tool 호출률 ↑ |
| F2 | Groq | parallel_tool_calls | ❌ | 검색+회상 동시 | P2 | |
| F3 | Groq | temperature 호출별 | 0.1~0.7 적정 | 호출별 | OK | |
| G1 | Agent | system prompt (라우팅 11 분기) | ✅ 정교 | OK | OK | |
| G2 | Agent | tool description 강화 | ✅ 적용 | OK | DONE | "매물 보여줘" 명시 |
| G3 | Agent | tool 최대 3 호출 | ✅ | OK | OK | |
| H1 | Session | Redis TTL (24h/90d) | ✅ | OK | OK | session/profile 분리 |
| H2 | Session | shown_ids 자동 제외 | ✅ | OK | OK | |
| I1 | 관측성 | Sentry / OTel / Prometheus | ❌ 0 라이브러리 | sentry-sdk[fastapi] | P1 | 프로덕션 전 필수 |
2. OpenRouter wrapper 한계 — 직접 검증
| 옵션 | 공식 (provider 직접) | OpenRouter wrapper | 증거 |
|---|---|---|---|
Gemini task_type |
RETRIEVAL_QUERY vs RETRIEVAL_DOCUMENT 다른 임베딩 | 무시 | 같은 텍스트 두 호출 cosine = 1.0000 |
Cohere documents=dict[] + rank_fields |
필드별 가중 평가 | 미지원 (string[] 만) | HTTP 400 ZodError "expected string, received object" |
Gemini output_dimensionality (Matryoshka) |
128~3072 dim truncate | 미검증 (옵션 보내도 응답 dim 3072 추정) | — |
해결: P0 옵션을 실제 적용하려면 OpenRouter 우회 — Google AI Studio Gemini API key + Cohere API key 직접 사용.
어댑터 코드에 task_type 옵션 호출은 남겨둠 (OpenRouter 가 미래 지원 시 자동 활성).
3. 우선순위 매트릭스
| 우선 | 항목 | 이유 | 예상 효과 |
|---|---|---|---|
| P0 | 시드 N=100 → 300+ | area×type 매물 부족이 ceiling | area@10 43% → 80%+ |
| P0 | Gemini task_type — provider 직접 호출 | OpenRouter 무시 확인 | 대칭 → 비대칭 임베딩 ↑5-10% |
| P0 | Cohere rank_fields — 직접 호출 | OpenRouter 미지원 | 지역·의미 가중 분리 |
| P1 | Qdrant payload 인덱스 (deposit/rent/room_count/property_type) | filter 자주 쓰는 필드 | N 늘면 속도 ↑ |
| P1 | SearchService candidate_pool top_k×3 → ×8 | RRF/Rerank 입력 풀 확대 | recall ↑ |
| P1 | Sentry + OTel 도입 | 관측성 0 | 운영 가시성 |
| P1 | Groq tool_choice="required" sanity env | 단발 tool 호출률 33% | 측정 정확도 ↑ |
| P2 | Embedding dim 3072 → 1024 (Matryoshka) | OpenRouter 검증 필요 | 비용·메모리 1/3 |
| P2 | Sparse vector (Qdrant native) | 현재 BM25 별도 | fusion 단순화 |
| P3 | Cohere return_documents / max_chunks | 디버깅·긴 문서 | 운영 개선 |
| P3 | BM25 k1·b 튜닝 | 도메인 최적화 | marginal |
4. variant (dense/hybrid/full) — full 이 항상 best 인가?
| variant | 구성 | 지연 | 비용 | 유즈케이스 |
|---|---|---|---|---|
| dense | Qdrant cosine top-K + MMR | ~150ms | 0 | 대량 데이터 / 비용 민감 |
| hybrid | + BM25 RRF | ~250ms | 0 | 키워드 정확 매칭 필요 |
| reranker | + Cohere rerank | ~400ms | Cohere API | 의미 정확도 우선 |
| full | dense + BM25 + Rerank + MMR | ~500ms | Cohere API | 품질 최우선 (현 기본값) |
측정 (8 쿼리): dense vs hybrid vs full area@10 모두 43.4% 동일 — 시드 N=100 작아 변별 미미. ceiling 도달 후엔
full이 비용·지연만 추가. 시드 N≥300 후 재측정 필요. 현 기본값 variant="full" 합리 — POC 단계 품질 우선.
5. 적용된 변경 (코드)
| 파일 | 변경 | 상태 |
|---|---|---|
qdrant_adapter.py | area_keywords MatchText filter + _ensure_text_index(PREFIX) | ✅ 적용 |
.env | POC_SCORE_THRESHOLD=0.0 | ✅ 적용 |
openrouter_gemini_adapter.py | task_type 옵션 호출 (provider 미지원 — no-op) | ⚠ 미래 호환 |
openrouter_cohere_adapter.py | rank_fields 시도 후 revert (ZodError 400) | ↺ revert |
관련 리포트
- 9축 종합: poc-eval-suite-result
- Raw 데이터 분석: poc-raw-analysis
- 설계서: poc-search-design-2026