{
  "version": "v0.2-phase2q-result-dedup",
  "label": "phase_2q_search_result_dedup_real_effect_measurement",
  "date": "2026-05-24",
  "plan": "본 chore inline",
  "snapshot": {"doc_id_max": 25180, "chunk_id_max": 56526},
  "fix_summary": {
    "root_cause": "apply_diversity 의 top_score ≥ 0.90 시 unlimited path → 같은 doc 의 N chunks 가 results 에 박제. multi-query 의 reranker score 가 자주 0.90+ → 다수 case 에서 doc.id 중복.",
    "code_changes": [
      "app/services/search/search_pipeline.py — _dedup_results_by_doc_id() helper 신규 (doc.id first-only)",
      "search_with_rewrite() — apply_diversity(top_score_threshold=2.0) 강제 + 후속 _dedup_results_by_doc_id 적용 + rerank=False path 도 _dedup",
      "baseline (run_search) path 변경 0 — multi-query 전용 invariant"
    ],
    "test_changes": [
      "tests/test_query_rewriter.py — _dedup_results_by_doc_id 4 신규 test (55/55 PASS)"
    ]
  },
  "measurements": {
    "baseline_rewrite_null_eval_dedup_reference": {
      "label": "baseline (eval dedup 적용, fix 전과 동일 — single-query path 영향 0)",
      "graded_ndcg_at_10": 0.644,
      "graded_recall_at_10_t2": 0.699,
      "graded_recall_at_10_t3": 0.761,
      "latency_p50_ms": 378,
      "latency_p95_ms": 1931,
      "dedup_audit": "0/51 ✓ 정상"
    },
    "gemma_cold": {
      "label": "Result-Dedup fix 후 cold (cache empty)",
      "graded_ndcg_at_10": 0.663,
      "graded_recall_at_10_t2": 0.729,
      "graded_recall_at_10_t3": 0.761,
      "latency_p50_ms": 3692,
      "latency_p95_ms": 9992,
      "dedup_audit": "0/51 ✓ 정상 (fix 작동)",
      "by_category": {
        "english_only": {"recall": 0.78, "gndcg": 0.78},
        "exam": {"recall": 0.64, "gndcg": 0.71},
        "korean_only": {"recall": 0.60, "gndcg": 0.54},
        "mixed": {"recall": 0.43, "gndcg": 0.41},
        "standards": {"recall": 0.95, "gndcg": 0.87}
      },
      "csv": "reports/v0_2_phase2q_result_dedup_gemma_2026-05-24.csv"
    },
    "gemma_warm": {
      "label": "Result-Dedup fix 후 warm (cache hit)",
      "graded_ndcg_at_10": 0.659,
      "graded_recall_at_10_t2": 0.721,
      "graded_recall_at_10_t3": 0.739,
      "latency_p50_ms": 1588,
      "latency_p95_ms": 3514,
      "dedup_audit": "0/51 ✓ 정상",
      "csv": "reports/v0_2_phase2q_result_dedup_gemma_warm_2026-05-24.csv"
    }
  },
  "real_effect_delta_vs_baseline": {
    "ndcg_overall": "+0.019 (cold) / +0.015 (warm) — sub-noise level 개선",
    "recall_t2": "+0.030 (cold) / +0.022 (warm) — 소량 개선",
    "recall_t3": "0.000 (cold) / -0.022 (warm) — 동등~약간 회귀",
    "category_winners": "english_only +0.06 / standards +0.05 / mixed +0.03",
    "category_losers": "exam -0.03 / korean_only -0.03",
    "latency_p50_cold": "+3314ms (+876%) — 사용자 UX 비현실적",
    "latency_p50_warm": "+1210ms (+320%) — cache prewarm 후에도 무거움"
  },
  "measurement_chain_정정_history": [
    {"commit": "a41adb6", "phase": "Phase 3 (chunk dedup 0)", "ndcg": 0.927, "inflation": "chunk_id 중복"},
    {"commit": "b734fc5", "phase": "Rerank-Fix (chunk dedup)", "ndcg": 0.876, "inflation": "doc_id 중복 잔재"},
    {"commit": "3553573", "phase": "Eval-Dedup (eval layer dedup)", "ndcg": 0.641, "inflation": "production path 의 doc 중복 (단 eval 측정만 정정)"},
    {"commit": "TBD (본 PR)", "phase": "Result-Dedup (production dedup)", "ndcg": 0.663, "inflation": "0 ✓ 정상", "note": "이번이 정확한 production 측정값"}
  ],
  "user_decision_required": {
    "context": "multi-query 의 진짜 net 효과 = NDCG +0.019 + Recall +0.030 (sub-noise) / latency cold +876% warm +320%",
    "options": [
      {
        "label": "rollback (opt-in flag 제거 또는 영구 default null)",
        "rationale": "marginal quality 개선 (NDCG +0.019) 이 latency cost 정당화 X. 시스템 복잡도 비용 큼.",
        "action": "search.py 의 ?rewrite_backend= 제거 또는 description 갱신 (deprecated 표시). docs/phase_2q_apply_opt_in.md 의 metric 목표 정정 (NDCG ≥ 0.74 → ≥ 0.66 acceptable)."
      },
      {
        "label": "opt-in 유지 + cache prewarm (PR-2Q-Cache-Prewarm) 진입",
        "rationale": "warm path latency 1.6s = production 가용. nightly cron 으로 top-N query prewarm 시 사용자 cold path 회피 가능.",
        "action": "PR-2Q-Cache-Prewarm 별 plan + nightly cron + 1주 운영 후 metric 재평가."
      },
      {
        "label": "현 상태 유지 + 1주 운영 관찰 종료 후 (2026-05-31) 재결정",
        "rationale": "실제 사용자 query 분포에서 어떤 카테고리가 dominant 인지 확인 후 결정. opt-in 이라 production 영향 0.",
        "action": "현 상태 유지. 2026-05-31 metric 분석 후 별 PR."
      }
    ]
  }
}
