feat(search): Phase 0.3 검색 실패 자동 로깅

검색 실패 케이스를 자동 수집해 gold dataset 시드로 활용.
wiggly-weaving-puppy 플랜 Phase 0.3 산출물.

자동 수집 트리거 (3가지):
- result_count == 0           → no_result
- confidence < 0.5            → low_confidence
- 60초 내 동일 사용자 재쿼리   → user_reformulated (이전 쿼리 기록)

confidence는 Phase 0.3 휴리스틱 (top score + match_reason).
Phase 2 QueryAnalyzer 도입 후 LLM 기반으로 교체 예정.

구현:
- migrations/015_search_failure_logs.sql: 테이블 + 3개 인덱스
- app/models/search_failure.py: ORM
- app/services/search_telemetry.py: confidence 계산 + recent 트래커 + INSERT
- app/api/search.py: BackgroundTasks로 dispatch (응답 latency 영향 X)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Hyungi Ahn
2026-04-07 08:29:12 +09:00
parent 7fa7dc1510
commit f005922483
5 changed files with 286 additions and 1 deletions

View File

@@ -2,7 +2,7 @@
from typing import Annotated
from fastapi import APIRouter, Depends, Query
from fastapi import APIRouter, BackgroundTasks, Depends, Query
from pydantic import BaseModel
from sqlalchemy import text
from sqlalchemy.ext.asyncio import AsyncSession
@@ -11,6 +11,7 @@ from ai.client import AIClient
from core.auth import get_current_user
from core.database import get_session
from models.user import User
from services.search_telemetry import record_search_event
router = APIRouter()
@@ -38,6 +39,7 @@ async def search(
q: str,
user: Annotated[User, Depends(get_current_user)],
session: Annotated[AsyncSession, Depends(get_session)],
background_tasks: BackgroundTasks,
mode: str = Query("hybrid", pattern="^(fts|trgm|vector|hybrid)$"),
limit: int = Query(20, ge=1, le=100),
):
@@ -52,6 +54,9 @@ async def search(
vector_results = await _search_vector(session, q, limit)
results = _merge_results(results, vector_results, limit)
# Phase 0.3: 실패 자동 로깅 (응답 latency에 영향 X — background task)
background_tasks.add_task(record_search_event, q, user.id, results, mode)
return SearchResponse(
results=results,
total=len(results),