feat(search): Phase 1.3 TEI reranker 통합 (코드 골격)
데이터 흐름 원칙: fusion=doc 기준 / reranker=chunk 기준 — 절대 섞지 말 것.
신규/수정:
- ai/client.py: rerank() 메서드 추가 (TEI POST /rerank API)
- services/search/rerank_service.py:
- rerank_chunks() — asyncio.Semaphore(2) + 5s soft timeout + RRF fallback
- _make_snippet/_extract_window — title + query 중심 200~400 토큰
(keyword 매치 없으면 첫 800자 fallback)
- apply_diversity() — max_per_doc=2, top score>=0.90 unlimited
- warmup_reranker() — 10회 retry + 3초 간격 (TEI 모델 로딩 대기)
- MAX_RERANK_INPUT=200, MAX_CHUNKS_PER_DOC=2 hard cap
- services/search_telemetry.py: compute_confidence_reranked() — sigmoid score 임계값
- api/search.py:
- ?rerank=true|false 파라미터 (기본 true, hybrid 모드만)
- 흐름: fused_docs(limit*5) → chunks_by_doc 회수 → rerank_chunks → apply_diversity
- text-only 매치 doc은 doc 자체를 chunk처럼 wrap (fallback)
- rerank 활성 시 confidence는 reranker score 기반
- tests/search_eval/run_eval.py: --rerank true|false 플래그
GPU 적용 보류:
- TEI 컨테이너 추가 (docker-compose.yml) — 별도 작업
- config.yaml rerank.endpoint 갱신 — GPU 직접 (commit 없음)
- 재인덱싱 완료 후 build + warmup + 평가셋 측정
This commit is contained in:
@@ -133,6 +133,7 @@ async def call_search(
|
||||
mode: str = "hybrid",
|
||||
limit: int = 20,
|
||||
fusion: str | None = None,
|
||||
rerank: str | None = None,
|
||||
) -> tuple[list[int], float]:
|
||||
"""검색 API 호출 → (doc_ids, latency_ms)."""
|
||||
url = f"{base_url.rstrip('/')}/api/search/"
|
||||
@@ -140,6 +141,8 @@ async def call_search(
|
||||
params: dict[str, str | int] = {"q": query, "mode": mode, "limit": limit}
|
||||
if fusion:
|
||||
params["fusion"] = fusion
|
||||
if rerank is not None:
|
||||
params["rerank"] = rerank
|
||||
|
||||
import time
|
||||
|
||||
@@ -165,6 +168,7 @@ async def evaluate(
|
||||
label: str,
|
||||
mode: str = "hybrid",
|
||||
fusion: str | None = None,
|
||||
rerank: str | None = None,
|
||||
) -> list[QueryResult]:
|
||||
"""전체 쿼리셋 평가."""
|
||||
results: list[QueryResult] = []
|
||||
@@ -173,7 +177,7 @@ async def evaluate(
|
||||
for q in queries:
|
||||
try:
|
||||
returned_ids, latency_ms = await call_search(
|
||||
client, base_url, token, q.query, mode=mode, fusion=fusion
|
||||
client, base_url, token, q.query, mode=mode, fusion=fusion, rerank=rerank
|
||||
)
|
||||
results.append(
|
||||
QueryResult(
|
||||
@@ -404,6 +408,13 @@ def main() -> int:
|
||||
choices=["legacy", "rrf", "rrf_boost"],
|
||||
help="hybrid 모드 fusion 전략 (Phase 0.5+, 미지정 시 서버 기본값)",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--rerank",
|
||||
type=str,
|
||||
default=None,
|
||||
choices=["true", "false"],
|
||||
help="bge-reranker-v2-m3 활성화 (Phase 1.3+, 미지정 시 서버 기본값=true)",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--token",
|
||||
type=str,
|
||||
@@ -434,6 +445,8 @@ def main() -> int:
|
||||
print(f"Mode: {args.mode}", end="")
|
||||
if args.fusion:
|
||||
print(f" / fusion: {args.fusion}", end="")
|
||||
if args.rerank:
|
||||
print(f" / rerank: {args.rerank}", end="")
|
||||
print()
|
||||
|
||||
all_results: list[QueryResult] = []
|
||||
@@ -441,21 +454,21 @@ def main() -> int:
|
||||
if args.base_url:
|
||||
print(f"\n>>> evaluating: {args.base_url}")
|
||||
results = asyncio.run(
|
||||
evaluate(queries, args.base_url, args.token, "single", mode=args.mode, fusion=args.fusion)
|
||||
evaluate(queries, args.base_url, args.token, "single", mode=args.mode, fusion=args.fusion, rerank=args.rerank)
|
||||
)
|
||||
print_summary("single", results)
|
||||
all_results.extend(results)
|
||||
else:
|
||||
print(f"\n>>> baseline: {args.baseline_url}")
|
||||
baseline_results = asyncio.run(
|
||||
evaluate(queries, args.baseline_url, args.token, "baseline", mode=args.mode, fusion=args.fusion)
|
||||
evaluate(queries, args.baseline_url, args.token, "baseline", mode=args.mode, fusion=args.fusion, rerank=args.rerank)
|
||||
)
|
||||
baseline_summary = print_summary("baseline", baseline_results)
|
||||
|
||||
print(f"\n>>> candidate: {args.candidate_url}")
|
||||
candidate_results = asyncio.run(
|
||||
evaluate(
|
||||
queries, args.candidate_url, args.token, "candidate", mode=args.mode, fusion=args.fusion
|
||||
queries, args.candidate_url, args.token, "candidate", mode=args.mode, fusion=args.fusion, rerank=args.rerank
|
||||
)
|
||||
)
|
||||
candidate_summary = print_summary("candidate", candidate_results)
|
||||
|
||||
Reference in New Issue
Block a user