본문으로 건너뛰기

V2 사무실 기억 검색 품질 (ADR 0024)

V2 사무실 기억 검색 (ADR 0017 머지 후) 의 품질을 측정·개선하는 4 단계 인프라. ADR 0024 가 SSoT, 본 문서는 코드 위치·운영 흐름 요약.

배경

ADR 0017 V2 머지로 tenantId 격리 임베딩 + findNearest + 메타 매칭 결합 랭킹 (cosine 0.7 + meta 0.3) 까지는 완성됐지만:

  • 검색 품질이 측정되지 않고 있었음 (precision/NDCG/MRR 산출 인프라 부재)
  • 임베딩 + 메타 매칭만으로는 표층 의미 한계 — recall@10 향상 여지 명백
  • 클릭 텔레메트리는 있었지만 정합 지표 산출에 활용 안 됨

ADR 0024 가 4 PR 묶음으로 텔레메트리 → 측정 → rerank → multi-query expansion 순서 도입.

4 PR 구성 (모두 머지 완료 2026-04-26)

PR산출핵심 산출물
PR-1텔레메트리 스키마 확장reranked · rerankModel · queryExpanded · expandedQueriesCount 4 필드 + clicked 이벤트 dwellMs
PR-2precision@K · NDCG · MRR 측정implicit feedback (클릭) 기반 계산 인프라 + 대시보드 카드
PR-3LLM rerank 모듈Gemini Flash, DI 기반 fail-soft, top-N 재정렬, searchRerank 메타 플래그 (기본 OFF)
PR-4Multi-query expansiondeterministic rule-based, 1 → N (최대 4) 쿼리, MAX 점수 + appearanceBoost, queryExpansion 메타 플래그 (기본 OFF)

측정 정의

precision@K (binary relevance)

precision@K = (top-K 안 클릭 수) / min(K, cardCount)

NDCG@K (binary relevance)

DCG@K = Σ_{i=0..K-1} rel(i) / log2(i+2)
IDCG@K = 클릭 수 만큼 상위에 모은 이상적 DCG
NDCG@K = DCG@K / IDCG@K (IDCG=0 시 0)

MRR (단일 세션)

MRR = 1 / (firstClickRank + 1) [클릭 없음 → 0]

세션 매칭 규칙

  • (caseId, docKind, actorUid) 키로 shown → click 5분 window (SESSION_WINDOW_MS = 300_000)
  • 새 shown 등장 시 이전 세션 종료
  • 동일 rankIndex 중복 클릭 1회만

LLM Rerank 알고리즘 (PR-3)

input: query + ranked top-N + snippets (redactedText, ≤240 char)
output: reranked top-N + applied + modelUsed

1. 빈/단일 후보 → 즉시 fail-soft (applied=false)
2. LLM 호출 (Gemini Flash, JSON 점수 0~100)
3. LLM throw → fail-soft, 원래 ranking 유지
4. LLM 빈 점수 → fail-soft
5. 점수 내림차순 정렬, 동점은 원래 finalScore 내림차순 (안정 정렬)

fail-soft 가 핵심: rerank 가 어떤 이유로든 실패해도 baseline ranking 이 그대로 노출. P1 양보 불가 — V1 결과보다 V2+rerank 결과가 적합하면 즉시 rollback 가능해야 함.

Multi-Query Expansion 알고리즘 (PR-4)

4 variants 생성 (dedupe 후 최대 4):
V1: [recoveryType] [docType] (+ 상사) (+ bucket) — 가장 정밀
V2: [recoveryType] [docType] (+ 상사) — bucket 제거 (broader)
V3: [recoveryType] [핵심 법령 키워드] — 도메인 적합성
V4: [recoveryType] [docType] — broadest fallback

각 variant: embed → findNearest(pool=15) → rankMemories → top-N
병합:
- 각 docId 점수 = variant 결과 중 MAX
- appearanceBoost: 등장 1회 추가당 +0.05 (cap 0.15)
- finalScore + bonus 내림차순 정렬

도메인 법령 키워드 매핑 (10 도메인):

  • debt → 민법 제162조 소멸시효
  • divorce → 민법 제840조 이혼사유
  • real-estate-eviction → 부동산 인도청구 강제집행
  • inheritance-share → 민법 제1117조 유류분
  • unjust-enrichment → 민법 제741조 부당이득
  • … (전체 매핑은 코드 SSoT)

코드 위치

  • 측정 계산: apps/web/lib/search-quality/precision.ts · ndcg.ts · mrr.ts (순수 함수)
  • 세션 매칭: apps/web/lib/search-quality/session-window.ts
  • LLM rerank: apps/web/lib/search-rerank/ (DI 기반, rerankerImpl.ts)
  • Multi-query 빌더: apps/web/app/(workspace)/legacy/_lib/multi-query-builder.ts
  • 텔레메트리: tenants/{tid}/relatedMemoriesEvents/* (스키마 확장은 PR-1)
  • 대시보드 카드: apps/web/app/(workspace)/_components/DashboardSearchQualityCard.tsx

Production 활성화 순서

플래그 모두 기본 OFF. 점진 활성:

  1. PR-1, PR-2 머지 직후 → 클릭 텔레메트리 자동 기록 시작 → 1주 baseline 측정
  2. features.ai.queryExpansion = true flip → recall 변화 측정
  3. features.ai.searchRerank = true flip + client-side reranker wiring (PR-3.5 follow-up) → precision 변화 측정
  4. 대시보드 카드 baseline vs reranked Δpp · expanded vs baseline P@5 로 효과 가시화

한계 (측정 정의의 함정)

  1. 클릭 = 관련 가정의 노출 편향: 상단 클릭 우세 — precision/NDCG 가 항상 정직하지 않음. trend (전 주 대비 ↑·↓) 추적은 유효
  2. 변호사 직접 입력 시 신호 0: 카드 보지 않고 직접 작성 시 데이터 없음. 사용자 적은 tenant 에선 표본 부족
  3. 5분 window 는 임의값: 너무 짧으면 누락, 너무 길면 다른 세션과 혼동. 운영하면서 SESSION_WINDOW_MS 조정 가능
  4. rerank top-N 안 재정렬: 풀 확장 (top-2N → 재랭크 → top-N) 은 후속 최적화. 현재는 메타 ranking 으로 결정된 top-N 안에서만 LLM 이 흔듦
  5. query expansion 보너스 cap 0.15: 4 variant 모두 등장한 docId 가 너무 우세하지 않도록 보호. 값은 실측으로 조정 가능

후속 과제

  • PR-3.5: client-side rerank wiring (CLAUDE.md 규약 — Gemini 클라 전용)
  • 풀 확장 rerank: top-2N → LLM rerank → top-N
  • 운영 매뉴얼: precision/NDCG ↓ 시 대처 가이드 (operations/v2-search-quality-runbook.md, 예정)

관련 문서