ADR 0093 — Comparison groundedness 를 per-target 측정 표면으로
ADR 0093 — Comparison groundedness 를 per-target 측정 표면으로
- Status: Accepted
- Date: 2026-05-23
- Authors: Hyunsoo Kim
- Related: ADR 0001 (naive_baseline byte-identical), ADR 0003 (answer dict 계약), ADR 0005 (public/private eval 분리), ADR 0054 (conditional-on-substantive scorer semantics), ADR 0059 (failure-mode classifier 측정 표면), ADR 0070 (content-grounded gold), ADR 0072 (verifier single-doc topic grounding — comparison 을 명시적으로 면제)
- Issue: #1399
Context
핵심 포트폴리오 주장 = “agentic_full 이 비교(comparison) 질의 품질에서 naive_baseline 을 이긴다”. 그러나 이 주장을 측정할 표면 자체가 없다.
현재 groundedness (eval/scorers/case.py) 는 답변 + 전체 evidence 텍스트를 한 덩어리(combined_text)로 합친 뒤 contains_all_terms(combined_text, expected_terms) 로 전역 substring 검사를 한다. 2-target 비교 질의 (예: “기관 A와 기관 B의 AI 요구사항 차이”) 에서 이 지표는 다음 둘을 구분하지 못한다:
- 양쪽 대상이 각자의 evidence 로 독립 근거됨 (의도된 상태)
- 한쪽만 근거되고, 다른 쪽의 expected_term 은 공유 풀(pool)에 그냥 누출(leak) 됨
즉 진짜 per-target 근거를 보상하지도, 한쪽짜리 답변을 벌하지도 못한다 — 프로젝트가 개선했다고 주장하는 바로 그 품질을 측정 불가.
전체 비교 경로를 추적한 결과 검색(retrieval)은 병목이 아니다: 실측 comparison_target_recall 은 이미 1.0 (양쪽 대상 문서 다 검색됨). target 정체성은 답변 구성까지 보존된다 (build_comparison_claims 가 대상별 1 claim 생성). 병목은 측정 표면의 부재 — per-target 근거를 볼 수 없으니 하류 시스템 수정(answer-builder claim 선택)의 효과도 보이지 않는다.
ADR 0072 는 non-comparison 질의에 single-doc topic grounding floor 를 도입하면서 comparison 을 정의상 면제했고, “남은 chunk-level alignment (가설 #4) 는 별 ADR” 로 미뤘다. 본 ADR 은 그 comparison 측정 공백의 계측(measurement) 절반 을 채운다 (시스템 수정은 loop #2).
마침 per-target gold 는 이미 존재한다: expected_claim_citations (target + expected_doc_ids + expected_terms) 는 비교 케이스에 이미 있고, alignment scorer (eval/scorers/alignment.py) 가 이미 대상별로 소비 중이다. 신규 gold 필드 0.
ADR 0059 의 read-only consumer boundary 가 본 지표에도 그대로 — production code path 0 변경, eval-time scorer 출력만 확장.
Decision
-
신규 per-case 지표
comparison_groundedness(eval/scorers/case.pyscore_comparison_groundedness) 를 기존 pooledgroundedness와 나란히 추가. 기존groundedness무수정 (ADR 0001 연속성 + 회귀 baseline 보존). -
정의 (결정론적, 오프라인): 비교 케이스의 각
expected_claim_citationsspec 에 대해, 대상은 grounded 이다 ⟺doc_id ∈ spec.expected_doc_ids인 evidence chunk 중 그 spec 의expected_terms를 모두 담은 것이 존재. per-case 점수 = (grounded target 수) / (target spec 수). 기존contains_all_terms(eval/scorers/_shared.py) 재사용. - 제외(None) 규칙 — ADR 0054 의 conditional-on-substantive semantic 을 그대로 미러:
query_type != comparison→None(denominator 제외)expected_claim_citations의 target spec 이 2개 미만 (단일 대상 “비교” 케이스 포함) →Noneanswerable=False→Noneanswerable=True AND abstained→0.0(답변 가능한 비교를 거부한 것은 벌점, None 아님 — pooled groundedness 의 answerable-but-refused 경로와 일관)
-
Aggregation wiring (eval/run_eval.py
metric_block) — non-None 점수를 모아comparison_target_recall의 조건부 패턴 그대로block+ci_block(bootstrap CI 95%) 에 추가. 공유metric_block경유로by_query_type.comparison슬라이스에 자동 전파. - Additive schema (
schema_versionbump 없음) — per-casecomparison_groundedness: float | None+ aggregatecomparison_groundedness: float(비교 케이스 존재 시만, 조건부). downstream consumer (compare_eval / check_baseline_provenance) 는 신규 키 무시.
Why these specific choices
| 결정 | 근거 |
|---|---|
기존 pooled groundedness 와 병행 (교체 아님) |
ADR 0001 byte-identity + 기존 leaderboard/baseline 연속성. 교체는 모든 committed aggregate 재생성 강제 + 회귀 비교 단절. |
| per-target = doc_id 필터 + term 매칭 | gold 가 이미 그 구조 (expected_claim_citations). alignment scorer 와 동일 gold 소비 → 일관성. 신규 gold 0. |
| target spec < 2 → None | 본 지표의 구조적 목표 = multi-target 비교 근거. 단일 대상 “비교” 케이스는 일반 groundedness 로 충분 — 희석 방지. |
| abstained(answerable) → 0.0, not None | pooled groundedness 와 동일 처리 (answerable 질의를 거부하면 품질 0.0). over-abstention 이 per-target 지표를 부풀리지 않도록. |
| LLM 미사용 (결정론) | 모든 입력이 case/evidence dict 의 기존 필드. 기본 eval 경로의 결정론·오프라인 불변 (ADR 0005/0012 데이터 경계 무관). |
metric_block 조건부 emit (target_recall 패턴 재사용) |
신규 집계 코드 0 — 검증된 None-filter + conditional-key 패턴 그대로. |
Consequences
- 비교 그라운드니스의 진짜 agentic_full−naive_baseline 델타가 드러난다 — pooled 지표가 가렸던 신호. 결과가 유리하면 핵심 주장 방어, 불리하면 그것이 곧 loop #2 (answer-builder per-target claim 선택 수정) 를 정당화하는 데이터.
- 신규 measurement surface 1차원 추가 —
comparison_groundednessper-case + aggregate 키. - production code path 0 변경 —
rag_*.py,api/,eval/config.yaml무수정. naive_baseline 합성 산출물 byte-identical. - 실측(real-100) 한계: ADR 0070 이 정량화한 catalog-gold construct-validity 결함 (committed
expected_terms가 본문 부재) 이 본 지표에도 그대로 상속됨 —contains_all_terms기반이므로. 따라서 본 loop 의 load-bearing 신호는 공개 합성 (expected_terms 가 합성 본문에 존재) 비교 슬라이스이며, 실측 비교 슬라이스는 ADR 0070 content-grounded gold 의 comparison 확장 (follow-up) 이후에 의미를 갖는다. - Current private-eval boundary: 이 ADR 의
real-100/ 실측 비교 슬라이스 언급은 당시 historical measurement context 이다. 현재 claim-bearing private-eval 근거는real100_v2aggregate-only 표면으로 한정하며, legacy real-100/n=221/kordoc aggregate wording 은 archive-only 로 취급한다.
Invariance check
- ADR 0001 (naive_baseline byte-identical) — read-only scorer 확장, production 코드 0 변경 → 합성 baseline 영향 없음. 신규 키만 추가.
- ADR 0003 (answer dict
schema_version=2) — 변경 없음. 본 확장은case_results(eval scorer 출력) 이지 answer 계약과 무관. - ADR 0005 (private/public 분리) — aggregate-only commit 패턴 그대로. 실측 비교 슬라이스는 n=1 (issue #1399 의 후속 데이터 확장 범위 밖) — 본 loop 의 load-bearing 신호는 공개 합성. 현재 private-eval claim 은
real100_v2aggregate-only 근거가 필요하다. - ADR 0054 (substantive-only semantics) — None/0.0 규칙이 ADR 0054 의 conditional-on-answer semantic 을 그대로 계승.
- ADR 0070 (content-grounded gold) — 본 지표는 채점기(scorer) 측 확장, ADR 0070 은 gold 구성 측 — 직교. 단 real-100 상속 한계는 위 Consequences 에 명시.
- ADR 0072 (verifier single-doc grounding) — comparison 면제로 남긴 측정 공백을 본 지표가 계측 측면에서 보완. verifier 동작 무변경.
Verification
Out-of-scope
- Answer-builder per-target claim 선택 수정 —
build_comparison_claims의 first-match-only (entity_evidence[0]) + target-blindbest_sentence(rag_answer.py). 본 지표가 드러내는 신호로 정당화되는 loop #2 별 PR (ADR 0072 의 “가설 #4” 계열). - 실측 비교 케이스 n=1 → ≥10 확장 + content-grounded comparison gold — ADR 0070 의 comparison 확장 (follow-up). 현재 정책에서는
real100_v2aggregate-only private-eval 경계(ADR 0005/0052 및 current project policy) 안에서만 claim 근거로 사용할 수 있다. - Verifier graded scoring — 현재 binary pass/fail. 별 ADR.
- Citation↔target alignment assertion —
make_citation의 대상-인용 정합성 검사. 별 follow-up.