ADR 0072 — 비교 아닌 쿼리는 단일 문서(single-doc) 내 topic grounding 요구
ADR 0072 — 비교 아닌 쿼리는 단일 문서(single-doc) 내 topic grounding 요구
- Status: Accepted
- Implemented: #1008 —
rag_verifier._max_single_doc_topic_matches+verify_evidence의 non-comparison 분기 - Date: 2026-05-23
- Authors: Hyunsoo Kim
- Related: ADR 0001 (naive_baseline byte-identical), ADR 0003 (answer dict 계약 / abstention 일급), ADR 0004 (partial-topic grounding strict→relaxed 정책), ADR 0005 (private/public eval 분리), ADR 0059 (failure-mode classifier)
- Issue: #1008
Current-policy note (2026-06-03): this ADR remains an accepted historical decision record for the single-doc topic-grounding verifier change. Its legacy
real-100/ 221-case motivating measurements are not current claim-bearing private-eval evidence. New task, PR, claim, and handoff evidence must use thereal100_v2aggregate-only surface in Surface Map, unless the maintainer explicitly re-enables another private-eval surface.
Context
Phase 5 audit finding #1 (docs/audits/verifier-false-negative-inspection.md, ADR 0059 / #1001·#1004 가 정량화)은 verifier_false_negative 를 real-100(n=221) failure 의 2위 카테고리로 측정했다 — unanswerable(answerable=False) 쿼리인데 verifier 가 sufficient 로 판정해 abstain 대신 답변을 emit 한 케이스 (failure_classifier: not answerable and not abstained).
inspection 의 핵심 신호: false-negative 케이스의 81.6% 가 multi-doc evidence 이고, 그중 다수(69.7%)는 expected doc 조차 검색되지 못한 채 여러 무관 문서에 verification topic 이 산재한 패턴이었다. 기존 verify_evidence 는 모든 evidence chunk 를 하나의 combined pool 로 합쳐 topic 등장 여부를 셌다. 따라서 topic A 가 doc1, topic B 가 doc2 에 있으면 “모든 topic 매칭됨”으로 통과 → 답변 emit → false negative. 즉 cross-doc topic spread 가 충분조건을 거짓으로 충족시켰다.
Decision
_max_single_doc_topic_matches(analysis, evidence, topics)신규 helper (rag_verifier.py).- evidence 를
doc_id로 묶어 단일 문서 내에서 매칭된 topic 수의 최댓값을 반환. - ADR 0004 / issue #687 의 cross-entity guard 동일 계승:
analysis["matched_doc_ids"]가 비어있지 않으면 pool 을 target doc 으로 먼저 제한. 빈 pool → 0.
- evidence 를
-
verify_evidence의 non-comparison 분기.query_type != "comparison"인 쿼리는 full-grounding 판정의 매칭 카운트를 combined-pool 대신_max_single_doc_topic_matches로 계산. 단일 문서가 모든 topic 을 ground 하지 못하면(cross-doc spread)topic_not_grounded→ abstain. -
partial 회복도 동일 floor 적용. non-comparison 쿼리의 last-attempt partial 회복(ADR 0004) 도 single-doc 카운트를 쓰므로, cross-doc spread 는
partial로도 회복되지 않는다 — false negative 가 partial 답변으로 새는 경로 차단. - comparison query_type 면제. 비교 쿼리는 정의상 여러 문서(entity 당 1개)에 근거가 분산되므로 기존 combined-pool 카운트를 유지하고, 이미 존재하는 entity/doc coverage 체크(
missing_comparison_entity/missing_comparison_doc)가 grounding 을 담당한다.
Why these specific choices
| 결정 | 근거 |
|---|---|
| combined-pool → single-doc max (non-comparison 한정) | 단일 문서 질문의 답은 한 문서 안에 있어야 한다. cross-doc 산재를 full grounding 으로 본 것이 false-negative 81.6% multi-doc 신호의 직접 원인. |
| comparison 면제 | 비교는 entity 당 다른 문서가 정당. single-doc 강제 시 정상 비교 답변을 over-abstain → false positive. 비교는 별도 coverage 계약 보유. |
matched_doc_ids target 제한 계승 |
issue #687 cross-entity guard 와 정합 — relaxed 단계에서 무관 기관 문서가 끌려와도 target 밖이면 grounding 불인정. |
| partial 경로도 single-doc | false negative 가 strict 를 막아도 partial 로 통과하면 여전히 not abstained → count 미감소. 두 경로 모두 동일 floor 라야 실제로 abstain. |
| recall 보존 우선 (보수적) | single-doc max 는 정답 문서가 검색됐으면 그 문서가 모든 topic 을 ground → 기존과 동일 통과. cross-doc 산재(=정답 문서 부재 다수)만 떨어뜨림. abstention(ADR 0003)은 일급 상태이므로 진짜 근거 부족은 abstain 이 정답. |
Consequences
- Historical real-100(n=221) A/B (동일 hashing 인덱스, main vs 본 변경):
verifier_false_negative76 → 68 (−8, −10.5%). 8건 전부incorrect_answer → correct_refusal로 전환. accuracy 0.161 무변 (정답 손실 0),verifier_false_positive0 유지 (근거 충분한 answerable 쿼리의 잘못된 거부 0), abstention 0.262 → 0.340 (의도된 상승, 이슈 본문[ALLOW_REGRESSION]범주). - 남은 68 의 다수는 audit 의 “expected doc 은 검색됐는데 verifier 실패”(28.9%) — 문서는 맞지만 그 안에 답이 없는 chunk-level 의미 불일치로, single-doc grounding 으로는 못 잡는다. chunk-level claim alignment(audit 가설 #4) 는 별 issue/ADR 범위.
- production 답변 계약(ADR 0003) 무변 — verified 판정만 강화, answer dict shape/
schema_version불변.
Invariance check
- ADR 0001 (
naive_baselinebyte-identical) — naive_baseline 은verify_evidence미호출(verified = bool(evidence),rag_core.py). 본 변경은verifier_retryarm(agentic_full/metadata_first) 한정.tests/test_naive_baseline_ranking_invariance.py통과. - ADR 0003 (answer dict 계약, abstention 일급) — 진짜 근거 부족은 그대로
status: insufficient. 본 변경은 cross-doc spread 라는 거짓 충분 만 제거하므로 abstention semantic 강화이지 위반 아님. - ADR 0004 (partial-topic grounding) — partial 회복의 fraction/matched floor 와 #687 cross-entity guard 를 그대로 계승, single-doc 차원만 추가.
tests/test_partial_topic_grounding.py전 케이스 통과. - ADR 0005 (private/public 분리) — 당시 측정은 gitignored local config(real-100) 기반, 신규 커밋 데이터 0. aggregate 수치만 본 ADR 에 인용(per-case 텍스트 미노출). 현재 신규 claim-bearing evidence 는 위 current-policy note 의
real100_v2aggregate-only 경계를 따른다.
Verification
Out-of-scope
- chunk-level claim alignment (audit 가설 #4) — 정답 문서는 검색됐으나 그 안에 답이 없는 28.9% 케이스. 큰 surface, 별 ADR.
- query specificity numeric-presence rule (audit 가설 #1 / Issue F) — RFP evidence 의 숫자 편재로 효과가 약해 본 PR 에서 채택하지 않음. 필요 시 topic-숫자 근접(chunk-level)과 결합한 별 검토.
- run-to-run variance source 진단 (audit 가설 #6) — 49↔65↔76 변동의 원인(검색 tie-break / embedding state). 별 issue.