Files
kebab/docs/superpowers/specs/2026-05-29-dogfood-search-quality-eval-design.md
altair823 2429189447 docs(spec): reflect search-quality critic round-1 (eval --config, lang-filter non-goal, curation)
Incorporates all critic (opus) round-1 findings into the dogfood
search-quality eval design spec:

BLOCKER-1: §4.4 execution commands now use --config /build/dogfood/config.toml
  (Task A facade-rule patch makes this the canonical path).  §5.1 re-titled
  from "(후속 패치)" to "Task A로 적용됨 — 권장 운영 경로"; XDG workarounds
  demoted to "패치 전 fallback".  Intro paragraph updated accordingly.

MAJOR-1: §3 Non-Goals gains an explicit bullet: lang/media/code_lang
  SearchFilters validation is out of scope for this harness (runner uses
  SearchFilters::default(), runner.rs:151).  §4.1 "code 검색" row no longer
  claims code_lang filter coverage.

MINOR-1: §4.3 step 3 now names kebab inspect doc <id> as the primary
  chunk-selection path (breaks chunk-level curation loop); search hits
  demoted to "보조 확인용".

MINOR-2: §4.1 golden category table gains two new rows — 한국어 N-gram
  fallback query (복합어/신조어 coverage) and 영어 whole-token exact query
  (separates substring artefacts).

MINOR-3: §4.1 YAML header note added: record corpus_revision in golden
  file so stale-bail root cause is immediately traceable.

NIT: §9 References line numbers corrected (runner.rs:31, metrics.rs:116/144);
  runner.rs:151 SearchFilters::default() reference added.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-29 03:43:00 +00:00

18 KiB
Raw Blame History

title, created, status, contract_sections
title created status contract_sections
dogfood 검색 품질 검증 — golden suite + 정성 검증 인프라 2026-05-29 accepted
§5.7 eval runs + metrics
§6 filesystem+config layout

dogfood 검색 품질 검증 — golden suite + 정성 검증 인프라

1. Summary

dogfood corpus(/build/dogfood/corpus, ~3940 docs / ~34896 chunks)에 대한 재사용 가능한 검색 품질 검증 인프라를 정의한다. 평가 엔진(kebab-eval: metrics / runner / compare)은 이미 존재하므로, 새로 만들 것은 두 가지다.

  1. dogfood KB 전용 golden query suite (/build/dogfood/golden_queries.yaml, ~1520 query) — 순환 의존을 피하는 큐레이션 절차 포함.
  2. golden suite 실행 + baseline 저장 + 정성 sanity 체크로 이루어진 검증 절차, 그리고 이를 docs/DOGFOOD.md 시나리오로 편입.

v0.20.2가 첫 적용 대상이며, 이 run을 baseline으로 eval_runs에 영속화한다. 이후 release는 kebab eval compare로 baseline 대비 회귀를 감지한다.

본 spec 작성 중 코드 재확인에서 두 가지 사실을 확정했고, 설계에 반영했다(§4, §9 참조):

  • kebab-eval의 aggregate metric에는 NDCG가 존재하지 않는다. 측정 가능한 metric은 §4.2 목록이 전부다.
  • kebab eval run CLI 경로는 원래 --config를 thread하지 않았으나, **Task A(facade-rule 정합 패치)**로 run_eval_with_config / compute_aggregate_with_config 경로가 적용됐다. dogfood KB 평가는 §4.4 명령 블록(--config /build/dogfood/config.toml)으로 실행한다(§5.1).

2. Background

2.1 이미 존재하는 평가 인프라 (crates/kebab-eval/)

모듈 역할
types.rs GoldenQuery, EvalRunOpts, EvalRun, QueryResult 공개 타입
loader.rs golden YAML 로드 + expected_* 실재 검증 (stale ref면 runner bail)
runner.rs query별 kebab_app facade 호출 → eval_runs + eval_query_results + runs_dir/<run_id>/per_query.jsonl 영속화
metrics.rs eval_query_results row → AggregateMetrics 집계
compare.rs 두 run의 metric delta 리포트 (Markdown / JSON)

GoldenQuery (types.rs:14) — required: id, query. optional(default empty/None): lang, expected_doc_ids: Vec<DocumentId>, expected_chunk_ids: Vec<ChunkId>, must_contain: Vec<String>, forbidden: Vec<String>, difficulty: Option<String>.

EvalRunOpts (types.rs:40) — suite: String, mode: SearchMode, with_rag: bool, k: usize, temperature: Option<f32>, seed: Option<u64>.

golden 경로 해석KEBAB_EVAL_GOLDEN env가 비어있지 않으면 그 경로, 아니면 CWD 기준 fixtures/golden_queries.yaml (runner.rs:142, metrics.rs:161; runner와 metrics가 동일 상수를 공유). ~ / ${...} 확장은 하지 않는다 — 직접 경로만.

2.2 metric별 ground-truth 요구사항

aggregate_from_rows (metrics.rs:184) 동작을 코드에서 확인한 결과:

  • expected_chunk_ids비어있지 않은 query만 hit_at_k / mrr / precision_at_k_chunk denominator에 들어간다.
  • expected_doc_ids비어있지 않은 query만 recall_at_k_doc에 들어간다. expected_doc_ids가 빈 query는 "should refuse" class로 간주되어 refusal_correctness(RAG run 한정)로만 평가된다.
  • must_contain / forbidden은 expected_* 없이도 동작하는 rule-based groundedness 입력이다. 둘 다 비면 해당 query는 groundedness denominator에서 제외(무설정 golden이 공짜 1.0/0.0을 얻지 않도록).

즉 ranking metric(hit@k/MRR/precision@k)을 측정하려면 query마다 expected_chunk_ids를, recall을 측정하려면 expected_doc_ids를 큐레이션해야 한다.

2.3 기존 golden과의 관계

repo fixture ./fixtures/golden_queries.yaml(현재 5 query 템플릿, expected_* 비어있음)는 fresh workspace에서 loadable하도록 의도된 템플릿이다. dogfood corpus용 golden은 존재하지 않는다. 본 spec의 dogfood golden은 repo fixture를 변경하지 않고 별도 파일(/build/dogfood/golden_queries.yaml)로 둔다.


3. Goals + Non-Goals

Goals

  • dogfood corpus를 대표하는 ~1520 query golden suite를 /build/dogfood/golden_queries.yaml에 작성.
  • 순환 의존(검색 결과가 정답을 정의) 없이 expected_chunk_ids / expected_doc_ids를 큐레이션하는 절차 확정.
  • golden suite를 dogfood KB에 대해 실행 → v0.20.2 baseline을 eval_runs에 영속화.
  • baseline 대비 회귀 감지 기준 + 정성 sanity 체크리스트 확정.
  • 위 절차를 docs/DOGFOOD.md의 검색 품질 시나리오로 편입.

Non-Goals

  • ranking 파라미터 자동 튜닝 / heuristic 조정 — project_ranking_deferred (실사용 1주+ 후 별도 brainstorm).
  • 기존 repo fixtures/golden_queries.yaml(템플릿) 변경.
  • 새 metric 구현 (NDCG 포함). 기존 kebab-eval metric만 재사용.
  • RAG 품질 자체 튜닝(prompt / NLI threshold). 본 spec은 retrieval(검색) 품질 검증이 주, RAG groundedness/refusal은 보조.
  • lang/media/code_lang 등 SearchFilters 동작 검증은 본 golden 하네스 범위 밖. 러너가 SearchFilters::default() 고정(runner.rs:151)이므로 golden의 lang 필드는 큐레이션·리포트 라벨일 뿐 retrieval에 영향을 주지 않는다.

4. 설계

4.1 golden suite — /build/dogfood/golden_queries.yaml (신규)

~1520 query. v0.20.x 검색 변경(V007 trigram → V009 한국어 형태소 + N-gram)을 집중 커버하고 corpus 대표성을 확보한다. 각 query 카테고리와 의도:

카테고리 의도 예시 query lang difficulty
한국어 2자/구 (V009 형태소) 형태소 분석이 짧은 한국어 토큰을 잡는지 "한국", "서울", "한국어 형태소 분석" ko easy~medium
한국어 N-gram fallback 형태소 분석이 분해 못 하는 복합어/신조어를 N-gram supplement가 잡는지 (V007 trigram 회귀 검증) 형태소 미분리 복합어/신조어 query ko medium
영어 whole-token exact whole-token 정확 매칭과 substring 부산물 분리 단독 complete token query en easy
영어 substring (V007→V009 회귀) trigram substring 매칭이 V009 이후에도 살아있는지 "tokenizer" en medium
hybrid 개념 query 개념적 query에서 lexical+vector fusion이 정답을 끌어올리는지 "hybrid search", "RAG architecture" en medium~hard
code 검색 코드 corpus(rust)에서 식별자/개념 검색 "rust ownership", "dispatch 함수" en/ko medium

요구사항:

  • 난이도(easy/medium/hard)와 언어(ko/en)를 섞는다.
  • 각 query: id, query, lang, difficulty는 필수 채움.
  • ranking metric 측정 대상 query는 expected_chunk_ids(+ 가능하면 expected_doc_ids)를 §4.3 절차로 큐레이션.
  • 일부 query에는 must_contain을 두어 groundedness를 함께 측정(예: "한국" → must_contain: ["한국"]은 지나치게 약하므로, RAG groundedness용 query는 답변에 반드시 등장할 핵심어를 신중히 고른다).

YAML 형식은 repo fixture와 동일(top-level query 리스트). 파일 상단 주석에 큐레이션 규칙(§4.3)과 "stale ref면 runner가 시작 시 bail"을 명시한다. 또한 golden YAML 헤더 주석에 # curated against corpus_revision=<rev> 를 기록해, 미래에 stale bail이 발생할 때 어느 DB 상태에 묶인 golden인지 즉시 판별할 수 있게 한다.

4.2 측정되는 metric (코드 확정 — AggregateMetrics, metrics.rs:56)

kebab eval aggregate가 산출하는 metric은 다음이 전부다. NDCG는 구현되어 있지 않다.

metric 타입 ground-truth 요구 비고
hit_at_k {1,3,5,10}→f32 expected_chunk_ids 정답 chunk가 top-k 안에 있으면 hit
mrr f32 expected_chunk_ids top-10 밖이면 0 기여
precision_at_k_chunk {1,3,5,10}→f32 expected_chunk_ids denominator는 k 고정 (shortfall=precision loss)
recall_at_k_doc {1,3,5,10}→f32 expected_doc_ids doc-level recall
groundedness f32 must_contain/forbidden (RAG) rule-based; with_rag 한정
citation_coverage f32 (null 가능) RAG answer 모든 citation path 非빈 + 최소 1개
refusal_correctness f32 (null 가능) expected_doc_ids 빈 query (RAG) "should refuse" + 실제 refuse
empty_result_rate f32 없음 0건 hit query 비율
total_queries / failed_queries u32 없음 run 카운트

citation_coverage / refusal_correctness는 denominator가 0이면 JSON null로 직렬화된다(예: lexical-only run은 Answer가 없으므로 둘 다 null). 검색 품질 검증의 1차 지표는 hit_at_k / mrr / precision_at_k_chunk / recall_at_k_doc 이고, groundedness/refusal/citation은 --with-rag run의 보조 지표다.

4.3 큐레이션 절차 (순환 회피 — 핵심)

검색 결과가 정답을 정의하면 평가가 무의미해진다. 따라서 정답은 도메인 판정으로, chunk_id는 조회로 얻는다.

  1. 정답 문서를 corpus 의미로 먼저 판정. 예: "한국" → markdown/korean/korea-overview.md. query 작성자가 corpus를 알고 어떤 문서가 정답인지 사람이 결정한다.
  2. 그 문서의 실제 doc_id를 조회: kebab --config <dogfood> list docs --json에서 doc_path로 매칭하거나 kebab inspect doc <id>로 확인.
  3. 그 문서 내 핵심 chunk_id를 조회: kebab inspect doc <id>로 문서의 chunk 목록을 나열해 의미상 관련 chunk를 선택한다(chunk-level 순환 회피 — 랭커에 올라오지 않은 관련 chunk도 포함 가능). kebab --config <dogfood> search "<query>" --json hit는 보조 확인용으로만 사용한다.
  4. expected_chunk_ids / expected_doc_ids에 기입.

불변식: expected_*는 큐레이션 시점의 dogfood KB에 실재하는 row여야 한다. loader 계약상 stale reference면 runner가 시작 시 bail한다(loader.rs). dogfood KB를 reset/re-ingest하면 chunk_id가 바뀔 수 있으므로(§9 재현성), golden은 특정 corpus_revision에 묶인다.

4.4 실행

전제: §5.1의 --config thread 패치(Task A)가 적용된 binary를 사용한다.

# 1차: hybrid (lexical + vector fusion)
KEBAB_EVAL_GOLDEN=/build/dogfood/golden_queries.yaml \
  /build/out/cargo-target/target/release/kebab \
  --config /build/dogfood/config.toml \
  eval run --mode hybrid --k 10
#    → run_id 출력. 이어서 aggregate:
KEBAB_EVAL_GOLDEN=/build/dogfood/golden_queries.yaml \
  /build/out/cargo-target/target/release/kebab \
  --config /build/dogfood/config.toml \
  eval aggregate <run_id> --json \
  | tee /build/dogfood/logs/eval-hybrid-v0.20.2.json

# 2차: lexical (순수 FTS5 — V009 형태소 동작 격리 측정)
KEBAB_EVAL_GOLDEN=/build/dogfood/golden_queries.yaml \
  /build/out/cargo-target/target/release/kebab \
  --config /build/dogfood/config.toml \
  eval run --mode lexical --k 10
KEBAB_EVAL_GOLDEN=/build/dogfood/golden_queries.yaml \
  /build/out/cargo-target/target/release/kebab \
  --config /build/dogfood/config.toml \
  eval aggregate <run_id> --json \
  | tee /build/dogfood/logs/eval-lexical-v0.20.2.json

CLI flag (코드 확정 — EvalWhat::Run, main.rs:406):

flag 기본값 비고
--suite <s> golden eval_runs.suite 라벨
--mode <lexical|vector|hybrid> lexical ModeFlag enum (main.rs:441)
--k <n> 10 retrieval top-k (with_rag 시 AskOpts.k)
--with-rag off query마다 kebab_app::ask도 호출, groundedness/citation/refusal 측정
--temperature <f> none with_rag 결정성: 0.0 권장
--seed <u64> none with_rag 결정성: 고정 seed 권장

중요: eval run은 metric을 집계하지 않는다run_id만 출력하고 eval_runs/eval_query_results에 영속화한다. metric은 eval aggregate <run_id>가 산출하고 eval_runs.aggregate_json에 write한다. 즉 검색 품질 측정은 run → aggregate 2단계다.

산출물:

  • eval_runs row (config_snapshot_json: chunker/embedding/llm/prompt/index version + fusion params) + eval_query_results rows.
  • runs_dir/<run_id>/per_query.jsonl.
  • /build/dogfood/logs/eval-{hybrid,lexical}-v0.20.2.json (aggregate JSON, dogfood 보관소 정책).

4.5 baseline + 회귀 기준

  • 첫 v0.20.2 (run + aggregate) = baseline. 절대 임계값보다 baseline 대비 회귀 감지가 1차 기준이다(어떤 절대값이 "좋은지"는 corpus 의존적이라 단정 불가).
  • 정성 sanity (golden 큐레이션 후 즉시 눈으로):
    • 한국어 2자 query("한국" 등)의 정답 chunk가 top-3 근처에 오는지.
    • 명백히 무관한 문서가 top에 올라오지 않는지.
    • lexical run에서 V009 형태소가 짧은 한국어 토큰을 0건으로 떨구지 않는지(empty_result_rate 확인).
  • 미래 release: kebab eval compare <baseline_run> <new_run> (compare.rs)로 hit@k / MRR / recall delta. chunker_version이 다르면 기본은 doc-id fallback, --strict-chunker-version로 거부 가능.

4.6 정성 보조 검증

golden 외 ad-hoc query 몇 개의 top-k를 mode별(lexical / vector / hybrid)로 눈으로 확인해 ordering이 자연스러운지 본다. 이 결과를 docs/DOGFOOD.md의 검색 품질 시나리오 섹션에 추가한다:

  • golden suite 실행 명령(§4.4)
  • 정성 체크리스트(§4.5)
  • mode별 ad-hoc top-k 관찰 기록 위치(/build/dogfood/logs/)

5. 운영 전제 / 알려진 제약

5.1 kebab eval --config thread — Task A로 적용됨 (권장 운영 경로)

Cmd::Eval dispatch(main.rs:1340~)는 패치 전 cli.config를 전혀 thread하지 않았다:

  • EvalWhat::Runkebab_eval::run_eval(&opts) 내부 Config::load(None)(runner.rs:31).
  • EvalWhat::Aggregatecompute_aggregate / store_aggregate 내부 Config::load(None)(metrics.rs:110, 138).
  • EvalWhat::CompareConfig::load(None) 직접 사용 (main.rs:1399). run / aggregate와 동일한 XDG 기본 config 한계.

Task A (fix(cli): thread --config through kebab eval run/aggregate/compare) 로 세 arm 모두 run_eval_with_config / compute_aggregate_with_config / store_aggregate_with_config / (기존) compare_runs_with_config로 교체됐다. 이는 CLAUDE.md facade-rule 정합 수정(P3-5 / P4-3와 동형)이다. 패치 적용 후 권장 운영 경로는 §4.4 명령 블록 그대로 (kebab --config /build/dogfood/config.toml eval run ...).

패치 전 XDG 우회 경로(A: XDG_CONFIG_HOME + symlink, B: KEBAB_STORAGE_DATA_DIR override)는 패치 전 fallback으로만 의미가 있다. baseline run evidence(어느 경로로 dogfood KB를 평가했는지)는 HOTFIXES + release notes에 명시한다.

5.2 dogfood 보관소 정책 (CLAUDE.md)

  • golden: /build/dogfood/golden_queries.yaml (신규).
  • 결과 로그: /build/dogfood/logs/eval-*.json.
  • config: /build/dogfood/config.toml (canonical dogfood config).
  • eval_runs / eval_query_results / runs_dir: dogfood KB(/build/dogfood/kb/) 내부.

6. Scope

본 spec = 재사용 가능한 검색 품질 검증 인프라(golden suite + 큐레이션 절차 + 실행/회귀 기준 + DOGFOOD 시나리오 편입). v0.20.2 baseline run이 첫 적용이다. golden suite는 이후 release마다 §4.5 절차로 회귀 감지에 재사용된다.


7. Testing

  • golden 로드 검증: KEBAB_EVAL_GOLDEN=/build/dogfood/golden_queries.yaml kebab eval run --mode lexical이 bail 없이 시작 → expected_* 가 dogfood KB에 실재함을 증명(loader 계약).
  • 2단계 동작 검증: eval runrun_id 출력 + eval_runs row 생성 → eval aggregate <run_id>aggregate_json 채움. eval aggregate <run_id> --json으로 metric 확인.
  • mode 분리 검증: lexical run의 citation_coverage / refusal_correctness가 null(Answer 없음)임을 확인 → with_rag 없는 run의 의도된 동작.
  • 정성 sanity: §4.5 체크리스트를 baseline run 직후 수행, 결과를 HOTFIXES + DOGFOOD에 기록.
  • 본 spec은 인프라/절차 문서이므로 새 단위 테스트를 추가하지 않는다 — 기존 kebab-eval 테스트(metrics.rs 18개 unit test)가 metric 계산을 커버한다.

8. 결과 기록 (cascade)

도그푸딩 evidence는 두 곳에:

  1. tasks/HOTFIXES.md dated entry — golden suite 적용 + baseline metric 표(hit@k/MRR/recall mode별) + §5.1 config 경로 결정 + 정성 sanity 결과.
  2. docs/release-notes/v0.20.2-draft.md(또는 gitea release body) — 검색 품질 검증 인프라 도입을 사용자 영향 관점으로 기술.

또한 docs/DOGFOOD.md에 검색 품질 시나리오 섹션 추가(§4.6).


9. References

  • crates/kebab-eval/src/types.rsGoldenQuery / EvalRunOpts 공개 surface.
  • crates/kebab-eval/src/metrics.rsAggregateMetrics(측정 metric 전체), aggregate_from_rows(metrics.rs:184), golden 경로 해석. compute_aggregate_with_config(metrics.rs:116), store_aggregate_with_config(metrics.rs:144).
  • crates/kebab-eval/src/runner.rsrun_eval(runner.rs:30) / run_eval_with_config(runner.rs:39), query 실행, SearchFilters::default() 고정(runner.rs:151).
  • crates/kebab-cli/src/main.rs:402EvalWhat(run/aggregate/compare flag), main.rs:441 ModeFlag, main.rs:1340 Eval dispatch(Task A 패치로 --config thread 적용).
  • fixtures/golden_queries.yaml — repo 템플릿(변경 대상 아님).
  • CLAUDE.md — facade rule(*_with_config), 버전 cascade(§9), dogfood trigger / 보관소 정책.
  • design §5.7 — eval_runs / eval_query_results 영속화 + config_snapshot.
  • project_ranking_deferred — ranking 자동 튜닝 deferral.
  • 형식 참조: docs/superpowers/specs/2026-05-28-v0.20.2-dogfood-findings-design.md.