# PR-RAG-Time-1 — 1주 운영 관찰 Baseline (Snapshot) **측정일**: 2026-05-03 (배포 직후, main `5185501`) **대상 endpoint**: `GET /api/search/?q=...&debug=true&limit=5` **비교 시점**: 2026-05-10 경 1주 후 동일 6 쿼리 재측정 → ranking / freshness_ms / total_ms 회귀 점검 **원본 JSON**: `reports/freshness_decay_observation_baseline.json` (1주 후 자동 비교 가능) ## 4 관찰 포인트 1. **news/law_monitor 가 과도하게 boost 되지 않는지** — multiplier floor 0.7 안에서 동작하므로 base score 가 약하면 freshness 만으로 상위 못 올라가야 함. 2. **오래된 핵심 문서가 부당하게 밀리지 않는지** — 산안법/KGS Code 원문(law_monitor) 이 floor 0.7 보장에 의해 사라지지 않아야 함. 3. **drive_sync / manual / Study / ai_drafted 비적용 가드 유지** — policy=None 으로 base 그대로. 4. **freshness_ms 와 total latency 회귀 없음** — 현 baseline freshness_ms 0.4~3.1ms / total 200~350ms. 회귀 신호 (1주 후 실측): - top 3 ranking 의 doc_id 변동이 6 쿼리 중 3 이상 발생 - freshness_ms p95 > 10ms (현재 max 3.06ms 대비 3× 초과) - total_ms p95 > 500ms - 사용자 명시 피드백 ("검색 결과가 이상하다") - `policy != None` 인 row 가 비정상적으로 적게/많게 나오는 분포 변화 ## 6 쿼리 baseline 요약 ### 쿼리별 정책 적용 분포 | 쿼리 | n | law_365d | news_90d | None | freshness_ms | total_ms | |---|---:|---:|---:|---:|---:|---:| | 중대재해 사고 | 5 | 3 | 0 | 2 | 0.486 | 250 | | 최근 중대재해 | 5 | 1 | 2 | 2 | 0.487 | 287 | | 산안법 개정 | 5 | 1 | 0 | 4 | 3.055 | 200 | | KGS Code 개정 | 5 | 0 | 0 | 5 | 0.434 | 272 | | 위험성평가 최근 동향 | 5 | 0 | 0 | 5 | 0.430 | 284 | | 가스 사고 최근 사례 | 5 | 0 | 2 | 3 | 0.444 | 350 | | **합계 30** | | **5** | **4** | **21** | avg 0.89 | avg 274 | ### 쿼리별 top-5 doc_id (1주 후 비교 기준) ``` 중대재해 사고: 3854 → 10571 → 10573 → 3922 → 3877 최근 중대재해: 10571 → 11566 → 3922 → 6695 → 11825 산안법 개정: 10572 → 4026 → 10573 → 5229 → 10569 KGS Code 개정: 11647 → 13914 → 11692 → 11693 → 11691 위험성평가 최근 동향: 5243 → 5229 → 10574 → 11685 → 11568 가스 사고 최근 사례: 11684 → 11564 → 11565 → 7107 → 6707 ``` ## 발현된 정책 sample (검산) | query | doc_id | source | age (일) | base | adj | ratio | policy | |---|---:|---|---:|---:|---:|---:|---| | 중대재해 사고 | 3854 | law_monitor | 29 | 0.7133 | 0.7016 | 0.9835 | law_365d | | 중대재해 사고 | 10571 | drive_sync | 8 | 0.6859 | 0.6859 | 1.0000 | None | | 중대재해 사고 | 3922 | law_monitor | 29 | 0.6872 | 0.6759 | 0.9835 | law_365d | | 최근 중대재해 | 6695 | news | 18 | (계산) | (계산) | (계산) | news_90d | | 가스 사고 최근 사례 | 7107 | news | 17 | (계산) | (계산) | (계산) | news_90d | 검산: age=29, half_life=365 → decay = exp(-ln(2) × 29/365) = **0.9451**. multiplier = 0.7 + 0.3 × 0.9451 = **0.9835** (실측 ratio 와 일치). ✓ ## 관찰 신호 (현재 시점 메모) **중립**: - news/law_monitor 가 30 rows 중 9건 (30%) 발현 — 6 쿼리가 시사 도메인 키워드 위주이므로 적정. - floor 0.7 가드 안에 있어 base score 가 약한 row 는 절대 상위 침투 못 함 (관찰 포인트 1 통과 조건). **잠재 회귀 후보**: - "최근 중대재해" 쿼리에서 top 2 가 manual/drive_sync 8일 → 5일 짜리 학습 자료. news/law (18일) 는 4-5위. 사용자가 "최근" 키워드로 뉴스를 기대했는데 manual 이 우선이라면 1주 후 사용 패턴에 따라 base score(reranker) 재조정 필요. 단 이건 freshness 문제 아닌 reranker semantic match 의 문제. - "가스 사고 최근 사례" 도 동일 패턴 — manual(BLEVE/UVCE 학습) 이 news(폭발 사고 기사) 보다 위. ## 1주 후 비교 절차 ```bash # GPU 서버에서 1주 후 재실행 TOKEN=$(...) python3 /tmp/observe_freshness.py "$TOKEN" > /tmp/obs_week1.json # 로컬에서 baseline vs week1 diff python3 -c " import json b = json.load(open('reports/freshness_decay_observation_baseline.json')) w = json.load(open('/tmp/obs_week1.json')) for qb, qw in zip(b, w): bids = [r['id'] for r in qb['results'][:3]] wids = [r['id'] for r in qw['results'][:3]] if bids != wids: print(f'{qb[\"query\"]}: top3 변동 {bids} → {wids}') fb, fw = qb['timing_ms']['freshness_ms'], qw['timing_ms']['freshness_ms'] if fw > 3 * fb: print(f'{qb[\"query\"]}: freshness_ms 회귀 {fb}ms → {fw}ms') " ``` 회귀 발견 시 → rollback 검토 (search_pipeline.py:303~307 hook 비활성 또는 freshness_decay.py 의 `apply_freshness_decay` early-return). ## 관련 파일 - 구현: `app/services/search/freshness_decay.py` - hook: `app/services/search/search_pipeline.py:303-307` - schema: `app/api/search.py` `SearchResult.freshness_debug` - tests: `tests/test_freshness_decay.py` (31 passed) - plan: `~/.claude/plans/pr-rag-time-1-freshness-decay.md` - 배포 commit: `5185501` ## 1주 후 결과 (2026-05-12) PASS. top3 변동 0/6, freshness_ms max 0.54ms, total_ms max 413ms, policy 분포 동일 (9/30). 상세: `reports/freshness_decay_observation_week1.md`.