Files
kebab/docs/superpowers/specs/2026-05-10-p9-fb-39b-embedding-upgrade-design.md
th-kim0823 2c3461c465 spec(fb-39b): embedding model upgrade design
- multilingual-e5-small (384 dim) → multilingual-e5-large (1024 dim)
- Cascade: embedding_version bump → fb-23 incremental ingest
  re-embeds all chunks
- Migration policy: dim mismatch detection at LanceVectorStore::open
  → error.v1 (code = embedding_dim_mismatch) + hint
  "kebab reset --vector-only && kebab ingest"
- Config defaults flip (model + dimensions). User TOML pinning small
  preserves backwards-compat
- bge-m3 deferred (fastembed enum 미포함, UserDefinedEmbeddingModel
  ONNX path 별도)
- Release trigger: 0.6 → 0.7 minor bump per CLAUDE.md cascade rule

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-10 22:59:03 +09:00

9.3 KiB
Raw Permalink Blame History

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-39b — Embedding model upgrade design (multilingual-e5-large) P9 kebab-embed-local + kebab-store-vector + kebab-config + kebab-app p9-fb-39b design 0.7.0 ../../docs/superpowers/specs/2026-04-27-kebab-final-form-design.md
§4 search
§5 storage
§9 versioning cascade
2026-05-10

p9-fb-39b — Embedding model upgrade

Goal

fb-39 의 lever 적용 — embedding model 을 multilingual-e5-small (384 dim) 에서 multilingual-e5-large (1024 dim) 로 업그레이드. 도그푸딩 한국어 corpus 의 retrieval precision 개선.

fb-39 가 측정 도구 (P@5 / P@10) 를 추가했으므로, 본 PR 머지 후 small vs large 비교 가능.

bge-m3 검토했으나 fastembed 4.9.1 의 EmbeddingModel enum 에 미포함 — UserDefinedEmbeddingModel ONNX 직접 로드 path 는 별도 작업 (fb-39c 후보). 본 PR scope = e5-large 만.

Behavior contract

Embedding model

  • 신규 default: multilingual-e5-large (1024 dim).
  • kebab-embed-local::resolve_model 에 신규 arm:
"multilingual-e5-large" => Ok(EmbeddingModel::MultilingualE5Large),

기존 multilingual-e5-small arm 그대로 (backwards-compat opt-out).

Config defaults

  • Config::defaults().models.embedding.model: "multilingual-e5-small""multilingual-e5-large".
  • Config::defaults().models.embedding.dimensions: 3841024.
  • kebab init 가 생성하는 config.toml 템플릿 동일 갱신.

기존 user TOML 이 model = "multilingual-e5-small" 또는 dimensions = 384 명시한 경우 그대로 유지 — serde 가 user value 우선. opt-out 가능.

Cascade

  • embedding_version: 자동 변경 (config.models.embedding.model 값 그대로 wire 에 emit). multilingual-e5-smallmultilingual-e5-large.
  • fb-23 incremental ingest: 4-input match (blake3 + parser_version + chunker_version + embedding_version) 에서 embedding_version 깨짐 → 모든 chunk 재-embed. text/parse/chunk 비용 회피, embed 비용만 발생.
  • eval_runs.config_snapshot_json: 새 version 자동 기록. 비교 시 동일 version 끼리.
  • design §9 cascade rule 의 5 키 중 embedding_version 변경 — binary release 트리거 (CLAUDE.md Versioning cascade 룰).

Migration policy

LanceDB stored vectors 의 dim 과 config.models.embedding.dimensions 가 mismatch 면:

  • LanceVectorStore::open (또는 첫 호출) 가 비교 → mismatch 시 신규 ErrorV1:
    • code = "embedding_dim_mismatch"
    • message: "vector index dim 384 vs config dim 1024"
    • hint: "기존 vector index 가 4-dim, config 는 N-dim. 'kebab reset --vector-only && kebab ingest' 로 재구축."
  • CLI: exit 1 + error.v1 stderr (또는 비---json 모드 plain stderr).
  • silent migration / auto-wipe 안 함 — 사용자 명시 동의 필요.

remediation flow:

$ kebab search "..."
error: vector index dim 384 vs config dim 1024

Hint: 기존 vector index 가 384-dim, config 는 1024-dim.
'kebab reset --vector-only && kebab ingest' 로 재구축.

$ kebab reset --vector-only
[wipe LanceDB + SQLite embedding_records]

$ kebab ingest
[full re-embed with new model — fastembed downloads e5-large ONNX (~1.3 GB) on first run]

Wire shape

신규 wire field 없음. error.v1.code 의 valid value namespace 에 "embedding_dim_mismatch" 추가 (string, enum 아님 — additive).

Allowed / forbidden dependencies

  • kebab-embed-local: 신규 dep 없음. fastembed enum variant 추가만.
  • kebab-store-vector: 신규 dep 없음. LanceDB schema reader 사용.
  • kebab-config: 신규 dep 없음. defaults 값 변경.
  • kebab-app: 신규 dep 없음. error propagation.

kebab-core 의 다른 kebab-* 의존 금지 룰 그대로.

Public surface delta

kebab-embed-local (lib.rs)

fn resolve_model(name: &str) -> Result<EmbeddingModel> {
    match name {
        "multilingual-e5-small" => Ok(EmbeddingModel::MultilingualE5Small),
        "multilingual-e5-large" => Ok(EmbeddingModel::MultilingualE5Large),  // 신규
        other => anyhow::bail!(/* ... */),
    }
}

kebab-config (defaults + TOML 템플릿)

EmbeddingCfg {
    provider: "fastembed".to_string(),
    model: "multilingual-e5-large".to_string(),
    dimensions: 1024,
    // ... 기타 ...
}

generated config.toml 템플릿 도 같이 갱신.

kebab-store-vector (lib.rs 또는 신규 helper)

impl LanceVectorStore {
    pub fn open(...) -> Result<Self> {
        // 기존 open 로직 ...
        let stored_dim = read_schema_vector_dim(&table)?;
        if stored_dim != config_dim {
            anyhow::bail!(StructuredError(ErrorV1 {
                code: "embedding_dim_mismatch".to_string(),
                message: format!("vector index dim {stored_dim} vs config dim {config_dim}"),
                hint: Some(format!(
                    "기존 vector index 가 {stored_dim}-dim, config 는 {config_dim}-dim. \
                     'kebab reset --vector-only && kebab ingest' 로 재구축."
                )),
                // ...
            }));
        }
        Ok(...)
    }
}

(정확한 LanceDB schema reading API 는 구현 시 확인 — Table::schema() 또는 arrow_schema::Schema 직접 inspect.)

Test plan

kind description
unit (kebab-embed-local) resolve_model("multilingual-e5-large") returns Ok
unit (kebab-embed-local) check_dim(1024, 1024) ok
unit (kebab-embed-local) check_dim(384, 1024) Err — message mentions both dims
unit (kebab-config) Config::defaults().models.embedding.model == "multilingual-e5-large"
unit (kebab-config) Config::defaults().models.embedding.dimensions == 1024
unit (kebab-config) TOML model = "multilingual-e5-small" deserialize 정상 (backwards-compat)
unit (kebab-config) 생성된 config.toml 템플릿 안 model = "multilingual-e5-large", dimensions = 1024
unit (kebab-store-vector) mismatch fixture (384-dim stored + 1024 cfg) → embedding_dim_mismatch ErrorV1
통합 (kebab-cli) mismatch scenario — pre-existing 384-dim DB + new config → exit 1 + error.v1 stderr (code = embedding_dim_mismatch) + hint mentions reset --vector-only
통합 (kebab-cli) small config 로 fresh ingest + search → 정상 (backwards-compat path 검증)

multilingual-e5-large 모델 다운로드 회피 위해 unit/integration 테스트는 fixture 또는 mock — 실 모델 호출 안 함. 첫 도그푸딩 시 사용자가 fastembed cache 다운로드.

Implementation steps (high-level)

  1. kebab-embed-local::resolve_model arm + check_dim 단위 테스트.
  2. kebab-store-vector dim mismatch detection + ErrorV1 + 단위 테스트.
  3. kebab-config defaults flip + TOML 템플릿 + 단위 테스트.
  4. kebab-cli integration: mismatch error.v1 wire + backwards-compat path 통합 테스트.
  5. README + SMOKE + design + HOTFIXES + status flip.

5 task. 단일 PR, single 세션 가능.

Risks / notes

  • 첫 실행 모델 다운로드: e5-large ONNX ~1.3 GB. fastembed cache (config.storage.model_dir/fastembed/) 에 자동 다운로드 (첫 호출 시). progress 표시 없음 — 사용자 침묵 latency. kebab doctor 또는 README 에 경고 안내.
  • Search/ingest latency: e5-large 가 e5-small 대비 ~3-4× embedding 시간. ingest 비용 증가 (one-time + 신규 docs). search 시 query embed per-call 증가.
  • Disk usage: vector dim 2.6× → LanceDB 약 2.7× 증가.
  • HOTFIXES entry: dim mismatch UX (error.v1 + reset --vector-only flow) 가 frozen design 안 명시 안 된 신규 동작 — HOTFIXES 한 항목 추가.
  • eval comparison: fb-39 P@k 가 측정 도구. 도그푸딩 corpus + golden 의 expected_chunk_ids 채워서 small vs large 정량 비교 별도 (PR 안 의무 아님).
  • fb-23 incremental ingest 와의 상호작용: embedding_version 변경 → 모든 doc 재-embed. fb-23 의 unchanged path 는 한 번도 hit 안 함 (예상 동작).
  • release trigger: design §9 cascade rule 의 embedding_version 변경 → CLAUDE.md Versioning cascade 룰에 따라 binary 0.6 → 0.7 minor bump 필요.

Out of scope

  • bge-m3 또는 user-defined ONNX path (fb-39c 후보).
  • Other lever (RRF / cross-encoder / chunk policy).
  • Auto-migration / background re-vector.
  • LanceDB schema migration tooling (별도 wipe + re-ingest).
  • multi-model coexistence (한 KB 안 small + large 동시).
  • precision 정량 비교 의무 (별도 도그푸딩).

Documentation updates (implementation PR 동시)

  • README.md [models.embedding] config 섹션 — default 변경 + small opt-out 안내 + dim mismatch 시 reset 명령 안내.
  • docs/SMOKE.md — upgrade walkthrough (kebab reset --vector-only && kebab ingest 시퀀스 + 첫 ONNX 다운로드 latency 경고).
  • docs/superpowers/specs/2026-04-27-kebab-final-form-design.md §5 storage / §9 versioning 적절 절 — 새 default + dim 1024 명시.
  • tasks/HOTFIXES.md — dim mismatch UX entry.
  • tasks/p9/p9-fb-39-retrieval-precision-tuning.md banner — fb-39b lever 적용 (embedding upgrade) 추가 (단 spec status 는 fb-39 frozen).
  • tasks/p9/p9-fb-39b-embedding-upgrade.md 신규 task spec (만들거나, fb-39 sub-task 로 frontmatter 처리).
  • tasks/INDEX.md — fb-39b 행 추가 .
  • 본 PR 머지 후 chore: bump version 0.6 → 0.7 + tag (CLAUDE.md release 절차).