# Design Memo — QVT α: Non-saturating Quality Oracle

> **Status**: design — first installment of the Quality Verification
> Track (QVT). Follow-on to `docs/handovers/v0.4-quality-verification-track.md`
> §7 step 0.
> **작성일**: 2026-05-28
> **목적**: 비포화 품질 오라클 (non-saturating quality oracle) + 고정
> baseline 의 구체 설계. PR 게이트 패턴 정의.
>
> **선결 의존**: F9 cycle closed (#545/#546/#547/#549). step7-v4 fixture
> 의 path-GT 5 쿼리는 본 메모의 첫 axis 의 종자.

---

## 0. 한 줄 요약 (TL;DR)

> **현재 모든 품질 지표 (grounded / RAGAS) 가 포화 또는 노이즈.
> 라우터/budget/scope 레이어를 "켜도 될지" 증명할 도구가 없음.
> α = 3-axis 비포화 오라클 (graded answer · path coverage ·
> calibrated abstention) + 고정 baseline + 모든 PR 이 `quality
> delta card` 첨부.**

핵심 axiom:

| 종류 | 트리거 | 처리 |
|---|---|---|
| **A. 포화 지표** | grounded / RAGAS 1.000 / answer_relevancy noise | **삭제**, baseline 노트로만 유지 |
| **B. 비포화 지표 (α)** | 어려운 쿼리 + graded gold + 기권 calibration | **PR 게이트 + ablation 매트릭스 source of truth** |

---

## 1. Goals

1. **3 비포화 axis 정의** — 포화 천장 없는 graded scoring 으로
   "이번 PR 이 품질을 깎았는지/늘렸는지" 명확.
2. **고정 baseline 캡처 우선** — α 첫 PR 의 출력물은 한 줄 숫자가
   아니라 **commit-pinned baseline JSON**. 이후 PR 은 이 baseline
   과의 paired Δ.
3. **PR 게이트 패턴** — `core/retrieval` · `core/graph` · `core/reasoning`
   에 닿는 PR body 에 **quality delta card** 필수 (CLAUDE.md rule 2 강화).
   bench 숫자만으론 비용만 봄.
4. **v0.4 끝 ablation 매트릭스** — model × tier × layer 셀. 0-델타
   셀은 삭제 / 한쪽 tier 만 양수면 tier-gate 라우터 규칙.
5. **fixture 확장 가능** — 어려운 쿼리 추가가 schema-만 변경.

비목표 (이 PR 에서 안 함):
- D5/LEO/D1 flag-ON 결정 — α 가 측정 가능해진 뒤 v0.4 끝 별 PR.
- LLM-judge 도입 — graded gold 가 deterministic. judge 도입은
  α 의 안정성 확인 후 별 cycle (V3' protocol 의 judge 분리).
- 코딩 풀 (qwen/deepseek) 오라클 — §5 scope 경계 (QVT 본 문서)
  에 의해 별 축.

---

## 2. 세 비포화 axis

현재 step7 v4 출력에서 추출 가능한 시그널 + 1 신규:

### 2.1 Path Coverage (이미 존재, 확장 필요)

- **현재**: 5/16 쿼리 `expected_path.nodes` 보유 (q1–q4, q15).
- **포화 검사**: bge-m3 + entity-anchor 환경 q2/q3/q4/q15 = 1.0 (4/4).
  → 현재 어려운 쿼리만 비포화. 확장 안 하면 새 PR 이 회귀 신호 못 줌.
- **α 확장**: q5–q14, q16 에 `expected_path.nodes` + `min_recall`
  annotate. 추가로 `expected_path.edges` (relation tuples) — graph-RAG
  PR 이 노드는 잡았지만 관계 못 잡은 경우 (`Anthropic—develops→Claude`
  vs `Anthropic owns Claude`) 구분.
- **graded**: `path_recall` 은 이미 [0, 1] continuous. 포화 천장
  없음 (16-쿼리 fixture 에서 평균 추출).

### 2.2 Graded Answer Accuracy (신규)

- **포화 검사**: `unified_score` (현 RAGAS) = answer_relevancy +
  context_precision + faithfulness 합성. 0.86 ~ 0.93 노이즈
  band 안. 1-bit binary "맞다/틀리다" 비교 불가능.
- **α**: per-query `gold_answer_signals` — gold answer 에서
  추출한 **3 핵심 사실 (atomic claims)**. 응답이 각 사실을
  contain 한 비율 = graded accuracy.
  - 예시 (q2 "Anthropic 은 어떤 회사인가?"): gold signals =
    `["AI safety research", "Claude maker", "Constitutional AI"]`.
    응답이 3/3 = 1.0, 2/3 = 0.67, 1/3 = 0.33, 0/3 = 0.
  - matcher = deterministic substring + Korean stemming (NER-light,
    no LLM). 어려운 case 는 fixture 에 alias list 명시.
- **fixture 추가 필드**: `gold_signals: List[Dict[term, aliases]]`.
- **포화 안 함**: 어려운 쿼리는 3-claim 동시 충족이 드뭄. 천장
  도달 시 4-claim 으로 확장 (fixture-편집).

### 2.3 Calibrated Abstention (신규, gating-critical)

- **포화 검사**: 현재 abstention 신호 = `blocked: true/false`
  binary. 정책 위반은 잡지만 "내부 자료에 답이 없는 쿼리에
  hallucinate 했나" 는 측정 안 됨.
- **α**: per-query `abstention_truth` 라벨 — 코퍼스에 충분 evidence
  가 있나/없나 binary 정답.
  - `evidence_present`: 코퍼스에 답이 있음. 응답이 "모름/없음"
    이면 **incorrect abstention** (FN).
  - `evidence_absent`: 코퍼스에 답이 없음. 응답이 단정적 사실
    제시면 **hallucination** (FP).
- **graded**: per-query 0/1 → 전체 fixture 의 **F1 (precision +
  recall)**.
- **fixture 추가 필드**: `abstention_truth: {present, absent}` +
  필요시 `expected_abstention_phrasing: List[str]` (응답 안에
  "근거 부족" / "내부 자료에 없음" 등 anchors).

→ 이 axis 가 라우팅 정책에서 가장 중요. LEO evidence-scope 의
`narrow` band 가 "사실은 evidence_absent 인 쿼리" 인 경우, 시스템
은 wide-tier 로 escalate 가 아니라 **기권**해야 함 — α 가 그
판정 신호.

---

## 3. Fixture schema (step7-v5 확장)

기존 `step7_queries.json` 의 query 객체에 3 필드 추가. 호환:

```jsonc
{
  "id": 15,
  "category": "narrow",
  "text": "David Soria Parra가 누구야?",
  "expected_path": {                       // 기존 (Idea 1)
    "nodes": ["David Soria Parra", "MCP"],
    "edges": [                             // ⭐ 신규 (§2.1 확장)
      {"src": "David Soria Parra", "rel": "creator_of", "dst": "MCP"}
    ],
    "min_recall": 1.0
  },
  "gold_signals": [                        // ⭐ 신규 (§2.2)
    {"term": "MCP", "aliases": ["Model Context Protocol"]},
    {"term": "Anthropic", "aliases": []},
    {"term": "설계자", "aliases": ["창시자", "creator", "co-author"]}
  ],
  "abstention_truth": "present",           // ⭐ 신규 (§2.3)
  "expected_abstention_phrasing": []
}
```

baseline JSON: `eval/qvt/baseline_<sha>.json` — 첫 PR 이 합성:

```jsonc
{
  "git_sha": "4ddc81a",                    // F9.4 closure HEAD
  "env": {
    "JAMES_EMBEDDING_MODEL": "BAAI/bge-m3",
    "JAMES_ENABLE_ENTITY_ANCHOR": "1",
    "JAMES_ENABLE_QUERY_REWRITE": "1",
    "JAMES_AUTO_ROUTER": "0",              // routing layers OFF
    "JAMES_ADAPTIVE_BUDGET": "0",
    "JAMES_SCOPE_ROUTING": "0"
  },
  "fixture_version": "step7-v5",
  "n_queries": 16,
  "n_annotated": 16,
  "axes": {
    "path_coverage": {"mean_recall": 0.87, "annotated_count": 16},
    "graded_answer": {"mean_accuracy": 0.71, "annotated_count": 16},
    "abstention_f1": {"f1": 0.83, "precision": 0.92, "recall": 0.75}
  },
  "per_query": [/* ... 16 rows ... */]
}
```

(숫자는 placeholder — 첫 PR 의 baseline 캡처가 실제 값을 박음.)

---

## 4. Per-PR quality delta card (게이트 패턴)

CLAUDE.md rule 2 (`core/retrieval` / `core/graph` / `core/reasoning`
에 닿는 PR 은 bench numbers 첨부) 의 **확장**. PR body 에 추가:

```markdown
## Quality Delta vs baseline_<sha>

| axis | baseline | this PR | Δ | sign |
|---|---|---|---|---|
| Path Recall (mean) | 0.87 | 0.91 | +0.04 | ✅ |
| Graded Answer (mean) | 0.71 | 0.68 | −0.03 | ⚠️ within noise |
| Abstention F1 | 0.83 | 0.84 | +0.01 | flat |

Noise band: ±0.02 (per-run variance, paired N=3 reruns).
Verdict: net neutral; PR ships on architectural justification, not
quality.
```

**규칙**:
- 모든 PR 이 cost 만이 아니라 quality Δ 를 박음.
- 한 axis 라도 −noise_band 이상 negative → **PR 본문에 명시 정당화** 필수.
- 모든 axis 0 또는 within noise → "이 레이어 marginal contribution = 0"
  signal. 누적 시 v0.4 끝 ablation 매트릭스에서 셀 삭제 후보.

**예외 (게이트 면제)** — quality measurement 가 본 PR 의 목적이 아닌
경우 면제. 다음 라벨 중 하나 붙으면 quality delta card 생략 가능:

- `external-contributor` — 외부 협업자 (예: Ali Gemini backend,
  Robin V3'.e schema 추가) PR. 본 PR 목적이 backend wiring / fixture
  추가 / methodology 적용이라 16-쿼리 측정 강제는 마찰만 만듦.
- `joint-collab-prep` — mid-June 3-author joint piece prep PR 등
  공동 deliverable 준비용.
- `docs` / `chore` / `ci` 류 — `core/` 안 건드리면 그대로 면제 (CLAUDE.md
  rule 2 원판 정합).

면제 PR 은 PR body 에 `Quality delta: exempt (label: <label>)` 한
줄로 표시. 협업 cadence 와 게이트가 부딪치면 게이트가 양보 — 게이트는
JAMES 내부 PR 의 marginal-contribution 측정용이지, 협업자 마찰
도구가 아님.

**노이즈 밴드 산출** — α 첫 PR 이 baseline 환경에서 3-회 paired
re-run 측정. 셋 모두 fixture 동결, seed/temp 동결.

---

## 5. v0.4 끝 ablation 매트릭스 shape

α 가 약속하는 최종 산출물 — 이 표가 곧 라우터 정책:

```
                 |  small (gemma4:e4b)     medium (gemma3:12b)     large (claude)
─────────────────┼────────────────────────────────────────────────────────────────
no routing       |   baseline                  +path?                    +path?
D1 (budget)      |   Δquality vs baseline      Δ                         Δ
D5 (auto-router) |   Δ                         Δ                         Δ
LEO (scope)      |   Δ                         Δ                         Δ
D1+D5            |   Δ                         Δ                         Δ
D1+D5+LEO        |   Δ                         Δ                         Δ
```

각 셀 = 3-axis vector + 비용 (latency, $).

판정:
- **all-axis Δ ≈ 0** → 셀 삭제 (이 model × layer 조합 라우터에서 제거)
- **path↑ only** → tier-gate (이 path-fan-out 쿼리만 이 셀로)
- **graded↑ only** → tier-gate (이 difficult-answer 쿼리만 이 셀로)
- **abstention F1 ↑** → escalation 정책 source (uncertain → 기권)

매트릭스 6×3 = 18 셀, 각 셀 N=3 reruns, 16 쿼리 × ~80s/쿼리 ≈
18 × 3 × 22 min = ~20 시간. 한 번 측정 cap.

---

## 6. 이 설계가 안 푸는 것

- **judge 안정성 불충분** — graded gold signals matcher 는
  deterministic substring + alias 만. 의역 / 부분 부정 / 정량
  근사 ("약 30%" vs "29%") 못 잡음. α 안정화 후 judge LLM 도입
  은 별 cycle.
- **fixture 편향** — 16 쿼리 = 김지원 단독 큐레이션. v0.5 Domain
  Pilot 진입 시 도메인 별 fixture (legal / food / retail) 확장.
- **시간 비포화 보장 못 함** — bge-m3 + entity-anchor 가 baseline
  의 천장을 0.87 로 올렸다. 다음 cycle 에서 천장 = 0.95 도달 시
  fixture 어렵게 만들어야 함 (`expand-difficulty` 패턴).
  - 신호: `n_queries_at_full_recall / n_annotated > 0.85` 이면
    fixture 어려움 부족 → 어려운 쿼리 5 개 추가 PR.
- **abstention truth label 작성 비용** — 16 쿼리 manual 라벨.
  v0.5 에서 라벨 자동 추출 (코퍼스 evidence 검색) 검토.

---

## 7. 열린 쟁점

- **ε (graded gold signal granularity)**: 3 atomic claims per query
  가 적당한가? 더 많으면 노이즈, 더 적으면 binary 됨. ε 결정 = α
  첫 PR baseline 측정 후 분포 보고 fixture 편집.
- **ζ (noise band 측정 회수)**: paired N=3 으로 충분한가? F-class
  cycle 의 ±25% sampling noise (LEO L.D F1) 가 source. ζ = 3
  → 첫 PR baseline 측정. 후속 PR 이 outlier 의심 시 N=5 retry.
- **η (PR-gate enforcement)**: quality card 첨부 강제 = bot vs human
  review? v0.4 끝 까지 manual review 으로 유지 (false-positive 위험).
  v0.5 진입 시 `.github/workflows/quality-gate.yml` 자동 비교 검토.

---

## 8. 구현 단계 (PR 시퀀스)

### α-1 (이 메모 PR) — **이번 작업**
- `docs/design/v0.4-qvt-alpha-non-saturating-oracle.md` 추가 (본 파일)
- `docs/handovers/v0.4-quality-verification-track.md` §7 step 0
  포인터 업데이트
- 코드 변경 0. design-only PR.

### α-2 — fixture schema 확장 + manual annotation
- `eval/regression/step7_queries.json` v4 → v5
- `gold_signals` + `abstention_truth` + `expected_path.edges`
  필드 추가. 16 쿼리 manual annotation.
- schema validator 단위 테스트.
- 코드 0. data-only PR.

### α-3 — oracle module + baseline 캡처
- `eval/qvt/oracle.py` (≤ 20 KB) — 3-axis 계산 함수.
- `eval/qvt/baseline_<sha>.json` — 현재 main 환경에서 3-회 paired
  실측 (cost: ~16 × 80s × 3 ≈ 64 min). noise band 결정.
- `scripts/qvt_capture_baseline.py` — operator 가 baseline 다시
  찍을 수 있는 wrapper.
- contract tests — 각 axis 함수 단위 + integration with mock
  bench JSON.

### α-4 — PR-gate template + CLAUDE.md hook
- `.github/PULL_REQUEST_TEMPLATE.md` (또는 별 `quality-gate.md`)
  — Quality Delta Card 섹션 추가.
- `CLAUDE.md` rule 2 확장 — bench + quality delta card 둘 다.
- **§4 면제 라벨 명시** — `external-contributor` / `joint-collab-prep` /
  `docs` / `chore` / `ci`. 협업자 (Ali / Robin / 외부 contributor)
  PR 은 게이트 없이 land. 협업 cadence 보호.
- 단위 PR 들 (Sprint 5 lifecycle / F3 acceptance / 임베딩 후속)
  부터 새 패턴으로 적용.

### α-5 — ablation 매트릭스 측정 (v0.4 끝)
- `scripts/qvt_ablation_matrix.py` — 18-cell 측정 (env 조합 자동
  토글). 결과 `reports/research-runs/qvt-ablation-<sha>.json`.
- `reports/promo-assets/v0.4-qvt-ablation-matrix.md` — 셀 별
  delta + 라우팅 정책 결정.
- 0-셀 삭제 PR + tier-gate PR 시퀀스 (별 cycle).

---

## 9. CLAUDE.md 정합

| rule | 충돌? | 비고 |
|---|---|---|
| #1 (no domain) | ✅ | platform infra |
| #2 (bench numbers on core/) | ✅ → **이 트랙이 강화** | quality delta card 추가 요구 |
| #4 (architecture change) | ⚠️ | α-3 (oracle module) + α-4 (CLAUDE.md edit) 는 `architecture` label |
| #5 (≤ 20 KB) | ✅ | `eval/qvt/oracle.py` 분할 필요시 axis 별 (`path.py` / `graded.py` / `abstention.py`) |

---

## 10. Related

- `docs/handovers/v0.4-quality-verification-track.md` — QVT 본 트랙
  (§7 step 0 = 이 메모)
- `reports/promo-assets/v0.4-f9-q15-entity-anchor-result.md` —
  α 가 측정 가능해진 직전 cycle (path-axis 종자)
- `reports/promo-assets/v3prime-leo-evidence-scope-result.md` —
  LEO L.D F1 (retrieval-mode harness, α-3 baseline 캡처가 재사용)
- `docs/research/v3prime-protocol-v1.md` — V3' methodology (judge
  분리 / paired re-run 패턴 source)
- `eval/regression/step7_queries.json` v4 (5-of-16 path-annotated)

---

## 11. 한 줄 요약

> **3-axis (path · graded answer · abstention F1) × 16 query baseline
> × paired N=3 noise band × 모든 PR 의 quality delta card.
> 라우팅 레이어 켜기 전에 측정. 0-델타 셀은 ablation 매트릭스에서
> 삭제. 라우터 정책이 측정 결과의 그림자.**
