ADR 0068 — Oracle-evidence 주입을 컴포넌트 천장(ceiling) 측정 표면으로
ADR 0068 — Oracle-evidence 주입을 컴포넌트 천장(ceiling) 측정 표면으로
- Status: Accepted
- Implemented: #1282 —
rag_core.run_rag_query_with_oracle_evidence+_phase_oracle_inject,eval/run_eval.build_oracle_evidence,oracle_evidence_sourceablation 필드 - Date: 2026-05-22
- Authors: Hyunsoo Kim
- Related: ADR 0001 (naive_baseline byte-identical), ADR 0005 (private/public eval 분리), ADR 0045 (phase 분해 + back-edge 0), ADR 0054 (conditional-on-answer scorer semantics), ADR 0059 (failure-mode classifier)
- Issue: #1282
Current-policy note (2026-06-03): this ADR remains an accepted historical decision record for the oracle-evidence ceiling measurement surface. 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
real-100 단독 측정 표면(ADR 0052, n=221)에서 파이프라인은 직렬 의존 _phase_analyze → _phase_retrieve_loop → _phase_build_answer (ADR 0045) 를 따른다. 상류(검색)가 하류(검증·답변) 천장을 가둔다 — 즉 “검색이 완벽했다면 답변·검증이 얼마나 맞힐 수 있었나?”를 현재 측정 표면은 답할 수 없다.
실측 신호가 이를 뒷받침한다 (#1282 batch1, n=221 hashing offline): retrieval_miss = 83/221 (38%) 가 모든 검색-time row-knob arm(full_kiwi / hierarchical / no_metadata_first)에서 거의 불변 — 검색 누락이 row-knob 이 아니라 임베딩에 묶여 있음을 시사한다. 그러나 “검색을 고정·완벽화했을 때 verifier_false_negative(49) 와 정확도가 어디까지 오르는가”는 검색과 분리해 측정할 수단이 없었다. 컴포넌트별 headroom 을 모르면 무거운 real-100 런 예산을 어디에 쓸지 triage 할 수 없다.
Decision
- 신규 eval-only 진입점
run_rag_query_with_oracle_evidence(index, query, oracle_evidence, *, ...)(rag_core.py).- 실제
_phase_analyze와_phase_build_answer를 실행하되_phase_retrieve_loop만_phase_oracle_inject로 대체 — 주입된 oracle evidence 로 검색을 우회. - 실제
verify_evidence와select_supporting_evidencebranch 를 그대로 실행 → 검증·답변 천장을 정직하게 측정 (검색만 우회). - LangGraph/ReAct dispatch 미경유 — 직렬 phase 경로 강제.
- 실제
-
_phase_oracle_inject(ctx, oracle_evidence)는_phase_retrieve_loop의 post-condition (writes:stage_attempts,retry_count,plan,evidence,verified,verification_reasons,retrieved_chunk_ids) 을 그대로 미러링 →_phase_build_answer가 동형(同型)ctx를 소비. ADR 0045 mutation-contract (tests/test_phase_mutation_contract.py) 가 lock-in. - gold → oracle evidence 투영
build_oracle_evidence(case, index)(eval/run_eval.py).- 기존
derive_gold_chunk_ids(chunk-recall scorer 와 동일 gold 집합) 재사용 +rag_retrievalevidence-item shape 으로 투영.score=1.0(verifier low-score floor 위),retrieval_mode="oracle". - 빈 gold (abstention 케이스) →
[]→ verify 자연 실패 → 답변 abstain (올바른 천장 동작).
- 기존
- 신규 ablation 필드
oracle_evidence_source(값"gold"= 주입 발화, 미설정/""= OFF).eval/run_eval.normalize_run_config에서 raw run dict 에서 직접 파싱 —resolve_pipeline_config/PIPELINE_CONFIG_KEYS를 거치지 않음 → production code path 미도달.
- 기본 OFF — opt-in. 어떤 arm 도
oracle_evidence_source를 설정하지 않으면evaluate_run은 기존run_rag_query를 그대로 호출 → 기존 summary byte-equal.
Why these specific choices
| 결정 | 근거 |
|---|---|
eval 레이어(evaluate_run)에서 분기, run_rag_query 미변경 |
case dict(gold)는 eval 하네스에만 존재. production 진입점을 건드리지 않아 ADR 0001 byte-identity 가 by-construction 보존. |
PIPELINE_CONFIG_KEYS 비경유 |
oracle 은 파이프라인 knob 이 아니라 eval-harness knob. resolve_pipeline_config 에 넣으면 preset resolution 표면이 오염 → 기본 경로 회귀 위험. raw row 에서 직접 읽어 격리. |
실제 verify_evidence 실행 (verify 우회 안 함) |
목적은 검증·답변 천장. verify 까지 우회하면 측정이 무의미. 검색만 우회해야 “검색 완벽 시 verifier+generator 천장”이 나옴. |
| 기존 phase 헬퍼 재사용, 병렬 파이프라인 신설 금지 | ADR 0045 분해를 그대로 활용. CLAUDE.md “shadow dict 모델 추가 금지” 정신 — _phase_oracle_inject 는 _phase_retrieve_loop 의 대체이지 새 계약 아님. |
derive_gold_chunk_ids 재사용 |
chunk-recall scorer 의 gold 집합과 동일 정의 → oracle 천장과 recall 진단이 같은 gold 위에서 정합. |
score=1.0, score_parts={} |
verifier low_top_score floor(rag_verifier.py:153, <0.18) 회피 — oracle 은 정의상 완벽 검색이므로 floor 미적용이 맞음. |
Consequences
- 신규 측정 표면 1차원 추가 —
oracle_evidence_source: goldarm 이 검색을 우회한 답변·검증 천장 (accuracy / abstention / failure_category) 을 emit. Wave 0 headroom triage 와 Wave 3(verifier/synthesis) 컴포넌트 분리의 prerequisite 충족. - 검색이 묶고 있던 하류 천장을 분리 측정 가능 → real-100 런 예산을 “천장 근처(개선 여지 작음)” vs “천장 멀음(개선 여지 큼)” 컴포넌트로 triage.
- production code path 0 변경 —
run_rag_query,api/,eval/config.yaml,eval/real_config.local.yaml무수정. 기본 오프라인 경로 SSoT 불변.
Invariance check
- ADR 0001 (
naive_baselinebyte-identical) — production 진입점run_rag_query미변경, oracle 은 별도 진입점이며 기본 OFF. naive_baseline golden + langgraph JSON-identity 회귀 테스트 통과로 입증. - ADR 0003 (answer dict
schema_version=2) — oracle 경로도 동일_phase_build_answer를 거쳐 동일 answer dict 계약 생산. 변경 없음. - ADR 0005 (private/public 분리) — oracle evidence 는 런타임에 gitignored local config 의 gold 필드에서 파생, 신규 커밋 데이터 0. per-case 출력은 기존 trace 와 동일 boundary (gitignored), aggregate 만 commit.
- ADR 0045 (phase 분해 + back-edge 0) —
_phase_oracle_inject는rag_core내부 leaf, supporting 모듈로의 back-edge 미추가. mutation-contract 테스트가 ctx-write 집합 고정. - ADR 0054 (conditional-on-answer scorer semantics) — 빈 gold →
[]→ abstain branch 가 ADR 0054 의 None-skip semantic 과 정합.
Verification
Out-of-scope
- oracle 천장 실측 런 —
oracle_fullarm 은 현재 승인된 private-eval 표면에서만 재측정한다. 기본 후속은real100_v2aggregate-only evidence 이며, legacy real-100 재실행은 maintainer 가 명시적으로 re-enable 한 경우에만 허용한다. - comparison query_type balance —
derive_gold_chunk_ids가 per-entity 균형을 강제하지 않아 비교 케이스 천장이 과소평가될 수 있음. v1 은 비교 케이스도 그대로 측정하되 해석 시 주의. 정밀화는 별 issue. - oracle-subqueries → retrieval 천장 — planner 우회 검색 천장(plan dict 주입)은 본 PR 의 evidence 주입과 별개 표면. 필요 시 별 ADR.