Files
hyungi_document_server/reports/phase2_final.md
Hyungi Ahn 120db86d74 docs(search): Phase 2 최종 측정 보고서 (phase2_final.md + csv A/B)
## 결과 요약

Phase 1.3 baseline vs Phase 2 final A/B (평가셋 v0.1, 23 쿼리):
 - Recall@10:  0.730 → 0.737 (+0.007)
 - NDCG@10:    0.663 → 0.668 (+0.005)
 - Top-3 hit:  0.900 → 0.900 (0)
 - p95 latency: 171ms → 256ms (+85)
 - news_crosslingual NDCG: 0.27 → 0.37 (+0.10 ✓)
 - exact_keyword / natural_language_ko: 완전 유지 (회귀 0)

## Phase 2 게이트: 2/6 통과
 ✓ news_crosslingual NDCG ≥ 0.30
 ✓ latency p95 < 400ms
  Recall@10 ≥ 0.78 (0.737)
  Top-3 hit ≥ 0.93 (0.900)
  crosslingual_ko_en NDCG ≥ 0.65 (0.53, bge-m3 한계)
  평가셋 v0.2 작성 (후속)

## 핵심 성과 (게이트 미달이지만 견고한 기반)
 1. QueryAnalyzer async-only 아키텍처 (retrieval 차단 0)
 2. semaphore concurrency=1 (MLX single-inference queue 폭발 방지)
 3. multilingual narrowing (news/global 한정 → 회귀 0 + news 개선)
 4. soft_filter boost 보수적 설정 (0.01, domain only)
 5. prewarm 15개 → cache hit rate 70%+

## infra_inventory.md soft lock 준수
 - config.yaml / Ollama / compose restart 변경 0

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 15:52:21 +09:00

126 lines
5.5 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Phase 2 최종 측정 보고서
**측정일**: 2026-04-08
**대상**: Document Server 검색 v2, Phase 2.1~2.3 통합
**평가셋**: `tests/search_eval/queries.yaml` v0.1 (23 쿼리, 8 카테고리)
**인프라 기준**: `memory/infra_inventory.md` (2026-04-08 실측)
## A/B 결과
| metric | Phase 1.3 baseline (A) | Phase 2 final (B) | Δ |
|---|---|---|---|
| Recall@10 | 0.730 | **0.737** | +0.007 ✓ |
| MRR@10 | 0.795 | 0.797 | +0.002 |
| NDCG@10 | 0.663 | **0.668** | +0.005 ✓ |
| Top-3 hit | 0.900 | 0.900 | 0 |
| Latency p50 | 114 ms | 109 ms | -5 |
| Latency p95 | 171 ms | **256 ms** | +85 |
## 카테고리별
| category | A NDCG | B NDCG | Δ | 비고 |
|---|---|---|---|---|
| exact_keyword | 0.96 | 0.96 | 0 | 회귀 0 ✓ |
| natural_language_ko | 0.73 | 0.73 | 0 | 회귀 0 ✓ (narrowed multilingual 덕) |
| crosslingual_ko_en | 0.53 | 0.53 | 0 | bge-m3 한계 — multilingual 효과 0 |
| **news_crosslingual** | 0.27 | **0.37** | **+0.10** | 개선 ✓ |
| news_ko | 0.36 | 0.37 | +0.01 | 미세 |
| news_en | 0.00 | 0.00 | 0 | 여전히 0 |
| news_fr | 0.46 | 0.46 | 0 | |
| other_domain | 0.88 | 0.88 | 0 | |
## Phase 2 게이트 검증
| 게이트 | 목표 | 실제 | 상태 |
|---|---|---|---|
| Recall@10 | ≥ 0.78 | 0.737 | ❌ (-0.043) |
| Top-3 hit | ≥ 0.93 | 0.900 | ❌ (-0.030) |
| crosslingual_ko_en NDCG | ≥ 0.65 | 0.53 | ❌ (-0.12) |
| news_crosslingual NDCG | ≥ 0.30 | 0.37 | ✓ |
| latency p95 | < 400 ms | 256 ms | ✓ |
| 평가셋 v0.2 완료 | - | v0.1만 | ❌ (후속) |
**2/6 통과** — 목표 미달. 단 회귀 0 + 일부 영역 개선.
## Phase 2에서 실제로 달성한 것
### 1. 아키텍처 — QueryAnalyzer async-only 구조 확립
실측 기반 철학 수정 (memory `feedback_analyzer_async_only.md`):
- `query → retrieval (즉시)` + `→ analyzer (async) → cache`
- retrieval 경로에 LLM 동기 호출 0
- background semaphore=1 (MLX single-inference 큐 폭발 방지)
- prewarm 15개 startup 시 자동 실행
- cache hit rate 첫 사용자 요청부터 70%+
### 2. 실측 데이터 — MLX 한계
gemma-4-26b-a4b-it-8bit MLX:
- full prompt (prompt_tok=2406) → **10.5초**
- 축소 prompt (prompt_tok=802) → **7~11초**
- concurrency >1 시 → **timeout 폭발** (semaphore=1 필수)
- 결론: analyzer는 **즉시 쓸 수 없는 자원**
### 3. multilingual narrowing — domain별 효과 차등
- 전 도메인 multilingual: natural_language_ko **-0.10 악화** ❌
- `domain_hint == news OR language_scope == global` 한정: 회귀 0 + news_crosslingual **+0.10** ✓
- 룰: 한국어 법령 검색에 영어 번역 쿼리 섞으면 noise
### 4. soft_filter boost — 보수적 설정 필요
- 초기 0.03+0.02 → exact_keyword **-0.03 악화**
- 낮춰서 0.01 단일 domain only → 회귀 0
- 평가셋에 filter 쿼리가 없어 효과 직접 측정 불가 (v0.2 확장 후 재평가)
## Phase 2에서 달성하지 못한 것 + 이유
### Recall@10 / Top-3 hit 회복 (0.730 → 0.78+ 미달)
- baseline 대비 +0.007 미세 개선만
- 원인: **corpus 1022 docs로 noise 증가**. chunk 수 7129. bge-m3의 embedding 공간에서 상위 후보 밀도 높아짐
- 해결책: retrieval 단계 품질 (Phase 3 evidence extraction) 또는 embedding 모델 업그레이드
### crosslingual_ko_en NDCG 0.65+ 미달 (0.53 정체)
- multilingual translation이 효과 없음
- 원인: 현재 category 3개 쿼리 중 정답 doc이 영어 교재 (Industrial Safety and Health Management 등). bge-m3는 ko 쿼리로 이 영어 doc을 약 0.5~0.6 cosine으로 이미 찾음. translation 추가가 정보 증가 없음
- 실제 필요: **reranker가 crosslingual pair**를 더 잘 학습해야 함 → bge-reranker-v2-m3의 한계 영역
### 평가셋 v0.2 완전 작성
- 시간 제약 + 정답 doc_id 수동 라벨링 필요
- 후속 작업으로 분리
## Phase 2 기여 commits (시간순)
```
d28ef2f Phase 2.1 QueryAnalyzer + LRU cache + confidence 3-tier (초기)
c81b728 async-only 구조 전환 (철학 수정)
324537c LLM_TIMEOUT_MS 5000 → 15000 (실측 반영)
1e80d4c setup_logger 수정 (prewarm 로그 보이도록)
f5c3dea Phase 2.2 multilingual + query embed cache
21a78fb semaphore concurrency=1 + run_eval --analyze 파라미터
e595283 multilingual news/global 한정 narrowing
e91c199 Phase 2.3 soft_filter boost (초기)
01f144a soft_filter boost 약화 (0.01, doctype 제거)
```
## 다음 단계 선택지 (사용자 결정)
### A. Phase 2 종료 + Phase 3 진입 (권장)
- Phase 2 성과: 아키텍처 + 회귀 0 + news 영역 개선 + 실측 기반 철학 확립
- Recall/crosslingual 정체는 **Phase 2 범위 밖** — embedding/reranker 교체 혹은 Phase 3 evidence extraction으로 우회
- Phase 3 (evidence extraction + grounded synthesis + `/api/search/ask`) 착수
### B. Phase 2 iteration — embedding 실험
- bge-m3 → 다른 embedding (e.g., multilingual-e5-large-instruct, jina-embeddings-v3) 교체 실험
- 대규모 재인덱싱 필요 (1022 docs × chunks)
- 인프라 변경이므로 infra_inventory.md drift 발생
### C. Phase 2 iteration — 평가셋 v0.2 작성
- queries_v0.2.yaml 작성 (filter 쿼리 + graded relevance)
- 현재 Phase 2 코드의 filter 효과 측정
- 단, Recall/crosslingual 근본 해결은 아님
## Soft Lock 준수 확인 (infra_inventory.md)
-`config.yaml` 변경 없음 (GPU local override 그대로)
-`docker compose restart` 사용 안 함 (`up -d --build fastapi`만)
- ✓ Ollama 모델 pull/remove 없음 (bge-m3, exaone3.5 그대로)
- ✓ Reranker 모델 변경 없음 (TEI bge-reranker-v2-m3 그대로)
- ✓ Mac mini MLX 설정 변경 없음