diff --git a/app/services/search_telemetry.py b/app/services/search_telemetry.py index 91ebd14..3260420 100644 --- a/app/services/search_telemetry.py +++ b/app/services/search_telemetry.py @@ -81,8 +81,16 @@ def compute_confidence(results: list[Any], mode: str) -> float: Phase 0.3 임시 구현. Phase 2에서 QueryAnalyzer 결과 + reranker score로 교체. - 하이브리드/텍스트 모드는 score가 가중치 합산이라 unbounded → match_reason과 결합. - 벡터 모드는 score가 코사인 유사도(0..1)라 그대로 사용. + score 의미 정리 (search.py 기준): + - mode=vector → score = 코사인 유사도 [0..1] + - mode=fts/trgm/hybrid에서 텍스트 매치 → score = 가중치 합산 (unbounded) + 가중치: title=3.0 / tags=2.5 / note=2.0 / summary=1.5 / content=1.0 / fts bonus≈2.0 + - mode=hybrid에서 텍스트 0건 → 벡터 결과만, score는 코사인 그대로 + - mode=hybrid 텍스트+벡터 동시 매치 → score = 텍스트가중치 + 0.5*코사인, + match_reason = "<텍스트reason>+vector" + + 핵심: match_reason이 정확히 'vector'(=문자열 "vector")면 텍스트 매치 0건인 vector-only. + 이 경우 score는 raw 코사인이므로 amplify 금지. """ if not results: return 0.0 @@ -93,12 +101,9 @@ def compute_confidence(results: list[Any], mode: str) -> float: if mode == "vector": # 코사인 유사도 그대로 - return max(0.0, min(1.0, top_score)) - - # text / hybrid: match_reason 강도 + score를 함께 본다 - # search.py의 가중치: title=3.0, tags=2.5, note=2.0, summary=1.5, content=1.0, fts bonus=2.0 - # vector boost(hybrid 합산)는 +0.5*cosine + return _cosine_to_confidence(top_score) + # text / hybrid: 강한 텍스트 매치 우선 판정 if "title" in reason and top_score >= 4.0: return 0.95 if any(k in reason for k in ("tags", "note")) and top_score >= 3.0: @@ -109,15 +114,36 @@ def compute_confidence(results: list[Any], mode: str) -> float: return 0.65 if "fts" in reason and top_score >= 1.0: return 0.55 - if "vector" in reason: - # vector-only hit (텍스트 매칭 실패) → 코사인 유사도 환산 - # hybrid 합산 시 vector 단독 점수는 score * 0.5로 들어옴 - cosine = top_score / 0.5 if top_score < 1.0 else top_score - return max(0.2, min(0.6, cosine * 0.7)) - # 약한 매치 + + # vector-only hit (텍스트 0건 → 코사인 raw, amplify 금지) + if reason == "vector": + return _cosine_to_confidence(top_score) + + # 그 외(약한 매치 또는 알 수 없는 reason) return 0.3 +def _cosine_to_confidence(cosine: float) -> float: + """bge-m3 임베딩 코사인 유사도 → confidence 환산. + + bge-m3는 무관한 텍스트도 보통 0.3~0.5 정도 코사인을 만든다. + 따라서 0.5는 "약하게 닮음", 0.7+는 "꽤 관련", 0.85+는 "매우 관련"으로 본다. + """ + if cosine >= 0.85: + return 0.95 + if cosine >= 0.75: + return 0.80 + if cosine >= 0.65: + return 0.65 + if cosine >= 0.55: + return 0.50 # threshold 경계 + if cosine >= 0.45: + return 0.35 + if cosine >= 0.35: + return 0.20 + return 0.10 + + # ─── 로깅 진입점 ─────────────────────────────────────────