- AggregateMetrics 에 precision_at_k_chunk: BTreeMap<u32, f32> (P@5, P@10) 추가, binary relevance via expected_chunk_ids - Denominator = k 고정 (hits.len() < k 도 precision 손실 간주) - Empty expected_chunk_ids query 는 skip (hit_at_k 동일 정책) - Lever 적용 (chunk policy / RRF / cross-encoder / embedding) 은 본 spec 범위 외 — fb-39b 이후 별도 task - Golden set schema 무변경, shipped fixtures 헤더 주석만 강화 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
6.8 KiB
title, phase, component, task_id, status, target_version, contract_source, contract_sections, date
| title | phase | component | task_id | status | target_version | contract_source | contract_sections | date | ||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| p9-fb-39 — Eval foundation design (P@k metric) | P9 | kebab-eval + docs | p9-fb-39 | design | 0.7.0 | ../../docs/superpowers/specs/2026-04-27-kebab-final-form-design.md |
|
2026-05-10 |
p9-fb-39 — Eval foundation (P@k metric)
Goal
도그푸딩 피드백 — agent / 사용자가 "rank 5+ 부터 노이즈 섞임" 지적 (precision-at-k 저하). lever (chunk policy / RRF / score_gate / cross-encoder / embedding) 선택 전, measurement infrastructure 먼저 정비. 본 PR scope:
AggregateMetrics에precision_at_k_chunk: BTreeMap<u32, f32>추가 (P@5, P@10).- chunk-level binary relevance 기반 —
expected_chunk_ids안 chunk 가 top-k 안 등장한 비율. - Golden set schema 무변경 —
expected_chunk_ids가 ground truth (curator 책임). - 문서화 강화 —
fixtures/golden_queries.yaml헤더 주석.
Lever 적용 (chunk policy / RRF tune / cross-encoder / embedding upgrade) 은 본 spec 범위 외 — fb-39b 이후 별도 task 로 분리. 측정 도구가 먼저 있어야 lever 효과 비교 가능.
Behavior contract
Metric definition
P@k_chunk(query) = |top-k hits ∩ expected_chunk_ids| / k
Denominator = k 고정. hits.len() < k 인 경우에도 분모는 k — top-k 부족도 precision 손실로 간주 (hit_at_k 와 동일 컨벤션).
expected_chunk_ids 빈 query 는 metric 계산에서 skip (hit_at_k_chunk 와 동일 정책).
Aggregation: 모든 valid query (expected_chunk_ids 비어있지 않음) 의 P@k_chunk 평균. valid query 0 건이면 NaN → JSON null.
Wire shape
AggregateMetrics 신규 field:
pub struct AggregateMetrics {
pub hit_at_k: BTreeMap<u32, f32>,
pub mrr: f32,
pub recall_at_k_doc: BTreeMap<u32, f32>,
/// p9-fb-39: chunk-level precision at k. Binary relevance via
/// `expected_chunk_ids`. Denominator = k (fixed). Skip queries
/// with empty `expected_chunk_ids`.
#[serde(default)]
pub precision_at_k_chunk: BTreeMap<u32, f32>,
// ... 기존 필드 ...
}
#[serde(default)] — 기존 eval_runs.metrics_json (옛 binary 가 기록한) 에 field 부재 시 empty BTreeMap 로 deserialize. backwards-compat 보장.
k values
compute_aggregate_metrics 가 5, 10 두 값에 대해 계산. (기존 hit_at_k / recall_at_k_doc 가 이미 동일 k 사용 — 재사용.)
Allowed / forbidden dependencies
kebab-eval: 신규 dep 없음. metrics 모듈 확장만.- 다른 crate 무수정.
kebab-eval 의 metrics / compare 모듈은 retrieval / embedding / LLM crate 직접 import 금지 룰 그대로 (P5 inheritance).
Public surface delta
kebab-eval::metrics
pub struct AggregateMetrics {
// ... 기존 ...
#[serde(default)]
pub precision_at_k_chunk: BTreeMap<u32, f32>,
}
compute_aggregate_metrics body 안 새 누적 BTreeMap + 평균 계산 추가. NaN handling 은 기존 serialize_f32_nan_as_null 패턴 재사용 — 단, BTreeMap<u32, f32> 의 NaN 처리 패턴이 hit_at_k 와 동일하게 round_recall_map 같은 helper 통해.
Test plan
| kind | description |
|---|---|
| unit (metrics) | precision_at_k_chunk empty expected → query skip → metric BTreeMap 안 entry 부재 또는 NaN |
| unit (metrics) | exact match: 5 hits, top-3 in expected → P@5 = 3/5 = 0.6 |
| unit (metrics) | partial top-k: hits.len() = 3 < k=5, all 3 in expected → P@5 = 3/5 = 0.6 (분모 k 고정) |
| unit (metrics) | top-k 안 expected 0건 → P@5 = 0.0 |
| unit (metrics) | 모든 query expected 비어있음 → P@k entry 부재 또는 NaN → JSON null |
| unit (metrics) | AggregateMetrics serde roundtrip — precision_at_k_chunk 신규 field 보존 |
| unit (metrics) | 옛 JSON (precision_at_k_chunk 부재) deserialize → empty BTreeMap default |
| 통합 (eval runner) | runner end-to-end → eval_runs.metrics_json 안 precision_at_k_chunk 채워짐 |
snapshot tests (기존 metrics 출력 fixture 가 있다면 갱신 — cargo test -p kebab-eval 수행 후 fixture diff 확인).
Implementation steps (high-level)
kebab-eval::metrics:AggregateMetrics.precision_at_k_chunkfield 추가 + 계산 로직 + 단위 테스트.- snapshot tests 갱신 (있다면).
fixtures/golden_queries.yaml헤더 주석 강화 —expected_chunk_ids채우기 가이드.- README
kebab eval섹션 또는 design §11 eval 에 P@k 정의 한 줄 추가. - tasks/INDEX.md / spec status flip.
3-5 step PR. 단일 세션 내 완료 가능.
Risks / notes
- 분모 = k 고정 정책:
hits.len() < k인 query 가 많으면 P@k 가 항상 < 1.0. 사용자 직관과 다를 수 있음 — README/design 에 명시. - frozen design vs new metric: design §11 eval 의 metric 표 갱신 필요. frozen contract 변경 트리거 —
target_version: 0.7.0bump 명시. - lever deferral: 본 spec contract_sections 는 §3 chunking + §4 search + §7 RAG + §11 eval 인데, 실제 본 PR 은 §11 만 건드림. lever 적용 (chunk policy / RRF / cross-encoder / embedding) 은 fb-39b 이후 별도. spec status banner 에 명시.
- expected_chunk_ids 비어있는 shipped golden: 현재
fixtures/golden_queries.yaml의 g001-g005 모두 expected_chunk_ids 비어있음. P@k 계산 시 모두 skip — out-of-the-box 측정값 0건. curator 가 자기 KB 로 채워야 metric 의미 가짐. 의도 — golden set 은 workspace 의존이라 shipped fixtures 는 template, 실제 측정은 user 가 채워서 한다. - fb-23 incremental ingest 와 충돌 없음: 본 PR 은 metric 만 추가. chunker_version / embedding_version 무변경.
Out of scope
- Lever 적용 (chunk policy retune / RRF k tune / score_gate default ON / cross-encoder PoC / embedding model 업그레이드).
- NDCG / MAP / 기타 ranking metric.
- precision_at_k_doc (doc-level — recall_at_k_doc 가 이미 있음, 본 spec 은 chunk-level 만).
- Golden set 콘텐츠 확장 (g006+ 추가) — curator 책임.
- Synthetic golden generator (
kebab eval golden-from-corpus등). - Per-query relevance score (binary 0/1 만 — graded relevance 는 NDCG 도입 시 검토).
Documentation updates (implementation PR 동시)
fixtures/golden_queries.yaml— 헤더 주석에expected_chunk_idsground truth 의미 + P@k 측정 위해 채우기 권장 안내.README.md—kebab eval섹션 (있다면) 에 P@k metric 한 줄. 없으면 skip.docs/superpowers/specs/2026-04-27-kebab-final-form-design.md§11 eval — metric 표에precision_at_k_chunk한 줄 추가.tasks/p9/p9-fb-39-retrieval-precision-tuning.md—status: open → completed, 단 banner 에 "eval foundation only, lever 적용 deferred to fb-39b" 명시 + design/plan 링크.tasks/INDEX.md— fb-39 행 ✅ (eval foundation only).