diff --git a/README.md b/README.md index c293d5e..e4b27f4 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ - **Rust toolchain** ≥ 1.85 (workspace 가 edition 2024 + resolver 3 사용). [rustup](https://rustup.rs) 권장. - **Ollama** — `kebab ask` 와 이미지 OCR/caption 가 사용. `https://ollama.com/download` 에서 설치 후 `ollama serve` 실행. 기본 LLM 은 gemma4 계열 (`ollama pull gemma4:e4b`) — OCR / caption 도 같은 family 라 모델 하나만 pull 하면 됨. 더 큰 variant 원하면 `gemma4:26b` 등으로 config override. config 의 `[models.llm].endpoint` 에 host:port 명시. - **빌드 디스크** — 첫 빌드 시 `target/` 가 6–10 GB (Lance + DataFusion + fastembed). 여유 확인. -- **fastembed 모델** — 첫 `kebab ingest` 시 `multilingual-e5-small` (~470 MB) 자동 다운로드. +- **fastembed 모델** — 첫 `kebab ingest` 시 `multilingual-e5-large` (~1.3 GB, fb-39b) 자동 다운로드. `config.toml` 에서 `model = "multilingual-e5-small"` 로 명시하면 이전 모델 사용. ## 설치 @@ -133,7 +133,7 @@ flowchart TB subgraph Pipeline["도메인 + 파이프라인"] parse["parse-md / parse-pdf / parse-image"] chunker["chunker (md-heading-v1, pdf-page-v1)"] - embedder["embedder (fastembed multilingual-e5-small)"] + embedder["embedder (fastembed multilingual-e5-large)"] retriever["retriever (lexical / vector / hybrid RRF)"] rag["RAG pipeline"] end @@ -178,7 +178,12 @@ flowchart TB ## Configuration -- `~/.config/kebab/config.toml` — `kebab init` 가 XDG 경로에 생성. `[workspace]` (root, exclude — include 필드는 제거됨, 지원 형식은 자동 결정), `[storage]`, `[chunking]`, `[models.embedding]`, `[models.llm]`, `[image.ocr]`, `[image.caption]`, `[search]`, `[rag]`, `[ui]` 절. `[ui] theme = "dark" | "light"` 로 TUI 팔레트 선택 (default `"dark"`, 알 수 없는 값은 dark fallback). `[search] stale_threshold_days = 30` (p9-fb-32) — search hit / RAG citation 의 `stale` 플래그 기준 (default 30 일, `0` 으로 비활성화). 옛 config 의 `workspace.include = [...]` 은 silently 무시 + 단발 deprecation warning (p9-fb-25). +- `~/.config/kebab/config.toml` — `kebab init` 가 XDG 경로에 생성. `[workspace]` (root, exclude — include 필드는 제거됨, 지원 형식은 자동 결정), `[storage]`, `[chunking]`, `[models.embedding]`, `[models.llm]`, `[image.ocr]`, `[image.caption]`, `[search]`, `[rag]`, `[ui]` 절. + - `[models.embedding]` — + - `model` (default `"multilingual-e5-large"`, fb-39b) — 다국어 sentence embedding 모델. 1024-dim. ONNX (~1.3 GB) 첫 실행 시 fastembed cache (`config.storage.model_dir/fastembed/`) 에 자동 다운로드. `"multilingual-e5-small"` (384 dim) 는 backwards-compat 으로 사용 가능 — TOML 에 명시. + - `dimensions` (default `1024`) — 모델의 embedding 차원. config 와 LanceDB stored dim 불일치 시 검색 결과 0 건 (orphan table). 모델 변경 시 `kebab reset --vector-only && kebab ingest` 로 vector index 재구축 권장. + - `[ui] theme = "dark" | "light"` 로 TUI 팔레트 선택 (default `"dark"`, 알 수 없는 값은 dark fallback). + - `[search] stale_threshold_days = 30` (p9-fb-32) — search hit / RAG citation 의 `stale` 플래그 기준 (default 30 일, `0` 으로 비활성화). 옛 config 의 `workspace.include = [...]` 은 silently 무시 + 단발 deprecation warning (p9-fb-25). - `[rag] prompt_template_version` (default `"rag-v2"`) — RAG system prompt version. `"rag-v1"` 은 legacy backwards-compat (사용자 명시 시 유지). v2 강화 규칙: (1) fact 인용 시 [#번호] 앞에 chunk 속 원문 큰따옴표 표기, (2) 학습 지식 동원 금지, (3) 근거 모호 시 "확실하지 않다" 명시. - `--config ` flag — 임시 워크스페이스 / 격리 테스트 시 사용. CLI / TUI 모두 honor. - `KEBAB_*` env — 일부 키 override (`KEBAB_RAG_SCORE_GATE`, `KEBAB_EVAL_GOLDEN`, `KEBAB_COMMIT_HASH` 등). diff --git a/docs/SMOKE.md b/docs/SMOKE.md index 7022d45..ca6c7a7 100644 --- a/docs/SMOKE.md +++ b/docs/SMOKE.md @@ -329,4 +329,24 @@ rm -rf /tmp/kebab-smoke # 통째로 정리 - (P7-3) 한 PDF 가 N 페이지면 `kebab ingest` 가 N 개 (또는 그 이상의, 페이지 길면 multi-chunk) 의 chunk 를 한 transaction 안에서 commit. 500 페이지 책 → 500+ chunk 한 번에 → embedding throughput 가 bottleneck. 임베딩 활성 워크스페이스에서 큰 PDF 를 처음 ingest 하면 분-단위 시간 + WAL 크기 증가 가능 — P+ 스케일 hardening task 까지 정상 동작이지만 비용은 측정 가능. - (P7-3 + follow-up) 동일 path 에 byte 가 다른 PDF 를 두 번째 ingest 하면 `purge_vector_orphans_for_workspace_path` 가 옛 chunk_id 를 LanceDB 에서 먼저 삭제, 이어서 `purge_orphan_at_workspace_path` 가 옛 doc / chunks / embedding_records 를 SQLite 에서 sweep. 새 byte 가 새 `doc_id` 로 색인됨. `IngestReport` 에 그 자산만 `new+=1` (다른 자산은 `updated`). 두 store 모두 정합 — 옛 본문 검색 시 옛 chunks 가 더 이상 surface 되지 않음. +### Embedding upgrade (fb-39b) + +`multilingual-e5-small` 에서 `multilingual-e5-large` 로 업그레이드 시퀀스: + +```bash +# 기존 vector index 정리 (orphan table 회피) +kebab --config /tmp/kebab-smoke/config.toml reset --vector-only + +# config.toml 의 [models.embedding] 갱신: +# model = "multilingual-e5-large" +# dimensions = 1024 + +# 재-ingest — fastembed 가 첫 실행 시 e5-large ONNX (~1.3 GB) 자동 다운로드. +# 다운로드 시간 + 모든 chunk re-embed 시간 (e5-small 대비 ~3-4×). +kebab --config /tmp/kebab-smoke/config.toml ingest + +# fb-39 의 P@k metric 으로 small vs large 비교: +kebab --config /tmp/kebab-smoke/config.toml eval run +``` + 자세한 history 와 발견된 버그는 [tasks/HOTFIXES.md](../tasks/HOTFIXES.md) 참조. diff --git a/docs/superpowers/specs/2026-04-27-kebab-final-form-design.md b/docs/superpowers/specs/2026-04-27-kebab-final-form-design.md index 199fcc9..666e79f 100644 --- a/docs/superpowers/specs/2026-04-27-kebab-final-form-design.md +++ b/docs/superpowers/specs/2026-04-27-kebab-final-form-design.md @@ -93,7 +93,7 @@ retrieval trace grounded ✓ qwen2.5:14b-instruct rag-v1 3 chunks prompt 1184 tokens completion 312 tokens latency 1842 ms -embedding multilingual-e5-small index v1.0 +embedding multilingual-e5-large index v1.0 ``` ### 1.3 `kebab ask` (refusal — score gate) @@ -212,7 +212,7 @@ variant 별 해당 키만 채움. `path` 와 `uri` 는 항상 채움 (`uri` 는 "vector_rank": 2 }, "index_version": "v1.0", - "embedding_model": "multilingual-e5-small", + "embedding_model": "multilingual-e5-large", "chunker_version": "md-heading-v1" } ``` @@ -264,7 +264,7 @@ Per-query failure 는 `bulk_search_item.v1.error` (error.v1) 에 격리, 다른 "grounded": true, "refusal_reason": null, "model": { "id": "qwen2.5:14b-instruct", "provider": "ollama" }, - "embedding": { "id": "multilingual-e5-small", "provider": "fastembed", "dimensions": 384 }, + "embedding": { "id": "multilingual-e5-large", "provider": "fastembed", "dimensions": 1024 }, "prompt_template_version": "rag-v1", "retrieval": { "trace_id": "ret_4a8b2c1e", @@ -374,7 +374,7 @@ Per-query failure 는 `bulk_search_item.v1.error` (error.v1) 에 격리, 다른 "token_estimate": 480, "chunker_version": "md-heading-v1", "embeddings": [ - { "model": "multilingual-e5-small", "dimensions": 384, "embedding_id": "e_2f1a" } + { "model": "multilingual-e5-large", "dimensions": 1024, "embedding_id": "e_2f1a" } ] } ``` @@ -390,7 +390,7 @@ Per-query failure 는 `bulk_search_item.v1.error` (error.v1) 에 격리, 다른 { "name": "data_dir_writable", "ok": true, "detail": "~/.local/share/kebab" }, { "name": "sqlite_open", "ok": true, "detail": "kebab.sqlite (schema v1)" }, { "name": "lancedb_open", "ok": true, "detail": "lancedb/" }, - { "name": "embedding_model", "ok": true, "detail": "multilingual-e5-small (384d)" }, + { "name": "embedding_model", "ok": true, "detail": "multilingual-e5-large (1024d)" }, { "name": "ollama_reachable", "ok": true, "detail": "http://127.0.0.1:11434" }, { "name": "ollama_model_pulled", "ok": false, "detail": "qwen2.5:14b-instruct missing", "hint": "ollama pull qwen2.5:14b-instruct" } ] @@ -1209,9 +1209,9 @@ chunker_version = "md-heading-v1" [models.embedding] provider = "fastembed" -model = "multilingual-e5-small" +model = "multilingual-e5-large" version = "v1" -dimensions = 384 +dimensions = 1024 batch_size = 64 [models.llm] @@ -1474,7 +1474,7 @@ $ kebab doctor ✓ data_dir_writable ~/.local/share/kebab ✓ sqlite_open kebab.sqlite (schema v1) ✓ lancedb_open lancedb/ -✓ embedding_model multilingual-e5-small (384d) +✓ embedding_model multilingual-e5-large (1024d) ✓ ollama_reachable http://127.0.0.1:11434 ✗ ollama_model_pulled qwen2.5:14b-instruct missing hint: ollama pull qwen2.5:14b-instruct diff --git a/tasks/HOTFIXES.md b/tasks/HOTFIXES.md index 2e69e84..765b9d1 100644 --- a/tasks/HOTFIXES.md +++ b/tasks/HOTFIXES.md @@ -14,6 +14,20 @@ historical contract that was implemented; this file accumulates the deltas so phase 5+ readers can find the live behavior without diffing git history. +## 2026-05-10 — p9-fb-39b: embedding upgrade UX + +**무엇이 바뀌었나**: default embedding 이 `multilingual-e5-small` (384 dim) 에서 `multilingual-e5-large` (1024 dim) 로 변경. LanceDB 테이블은 `(model, dim)` 으로 네임스페이스되어 새 모델은 fresh 테이블에 쓰고, 옛 `chunk_embeddings_multilingual-e5-small_384` 테이블은 orphan 상태 됨. + +**user TOML 에 small 명시한 경우**: backwards-compat 유지. 사용자가 `[models.embedding] model = "multilingual-e5-small"` 로 명시했으면 그대로 small 사용 (새 default 무시). + +**idempotent re-embed**: fb-23 incremental ingest 가 embedding_version mismatch 감지하면 자동으로 이전 chunk 를 새 모델로 re-embed. 다음 `kebab ingest` 호출 시 기존 chunk 의 embedding 을 새 테이블에 재작성. + +**disk 절약**: 이전 모델의 orphan 테이블을 먼저 정리하려면 `kebab reset --vector-only` 실행 (LanceDB + SQLite `embedding_records` 모두 wipe). 이후 `kebab ingest` 가 모든 chunk 를 새 모델로 re-embed 해 새 테이블 채움. + +**search/ask 결과**: re-embed 전까지는 empty hit (새 모델에 데이터가 없음). `kebab ingest` 후 정상 검색 가능. + +**Spec contract 와의 관계**: design §5 (storage) + §9 (versioning cascade) 의 embedding_model.id / dimensions 변경. embedding_version bump 아님 — 동일 binary 에서 config 만 변경해도 진행 가능 (cascade rule 준수). + ## 2026-05-09 — p9-fb-34: search wire wrapped in search_response.v1 **무엇이 바뀌었나**: `kebab search --json` stdout 이 기존 `search_hit.v1[]` 배열에서 신규 `search_response.v1` object 로 교체. wrapper 가 `hits`, `next_cursor`, `truncated` 세 필드를 가짐. diff --git a/tasks/INDEX.md b/tasks/INDEX.md index 912b9f0..7a874b3 100644 --- a/tasks/INDEX.md +++ b/tasks/INDEX.md @@ -130,6 +130,7 @@ P0~P5 는 직렬. P6~P9 는 P5 이후 병렬 가능. ### 🎯 0.5.0 — RAG quality (cascade 동반: V00X + reindex) - [p9-fb-38 score semantics](p9/p9-fb-38-score-semantics.md) — ✅ 머지 (2026-05-10) - [p9-fb-39 retrieval precision 튜닝](p9/p9-fb-39-retrieval-precision-tuning.md) — ✅ 머지 (2026-05-10) — eval foundation only, lever 적용 deferred + - [p9-fb-39b embedding upgrade](p9/p9-fb-39b-embedding-upgrade.md) — ✅ 머지 (2026-05-10) — multilingual-e5-large default - [p9-fb-40 fact-grounded answer](p9/p9-fb-40-fact-grounded-answer.md) — ✅ 머지 (2026-05-10) ### 🎯 0.6.0 또는 P+ — reasoning diff --git a/tasks/p9/p9-fb-39-retrieval-precision-tuning.md b/tasks/p9/p9-fb-39-retrieval-precision-tuning.md index 7f9641e..166a6b0 100644 --- a/tasks/p9/p9-fb-39-retrieval-precision-tuning.md +++ b/tasks/p9/p9-fb-39-retrieval-precision-tuning.md @@ -18,6 +18,7 @@ source_feedback: 사용자 도그푸딩 2026-05-06 — Claude Code 가 kebab CLI > > - Design: [`docs/superpowers/specs/2026-05-10-p9-fb-39-eval-foundation-design.md`](../../docs/superpowers/specs/2026-05-10-p9-fb-39-eval-foundation-design.md) > - Plan: [`docs/superpowers/plans/2026-05-10-p9-fb-39-eval-foundation.md`](../../docs/superpowers/plans/2026-05-10-p9-fb-39-eval-foundation.md) +> - fb-39b (lever 적용 — embedding upgrade): [`tasks/p9/p9-fb-39b-embedding-upgrade.md`](./p9-fb-39b-embedding-upgrade.md) ✅ ## 증상 / 동기 diff --git a/tasks/p9/p9-fb-39b-embedding-upgrade.md b/tasks/p9/p9-fb-39b-embedding-upgrade.md new file mode 100644 index 0000000..6a4e990 --- /dev/null +++ b/tasks/p9/p9-fb-39b-embedding-upgrade.md @@ -0,0 +1,75 @@ +--- +phase: P9 +component: kebab-embed-local + kebab-config + kebab-store-vector + docs +task_id: p9-fb-39b +title: "Embedding model upgrade (multilingual-e5-large)" +status: completed +target_version: 0.7.0 +depends_on: [p9-fb-39] +unblocks: [] +contract_source: ../../docs/superpowers/specs/2026-04-27-kebab-final-form-design.md +contract_sections: [§4 search, §5 storage, §9 versioning cascade] +source_feedback: 사용자 도그푸딩 2026-05-06 — Claude Code 가 kebab CLI 사용 후 "rank 5+ 노이즈 섞임" 지적 (fb-39 의 lever 적용 측면). +--- + +# p9-fb-39b — Embedding model upgrade + +> ✅ **구현 완료.** fb-39 의 lever 후보 4개 중 embedding model 업그레이드 lever 적용. P@k metric (fb-39) 으로 small vs large 비교 가능. +> +> - Design update: [`docs/superpowers/specs/2026-04-27-kebab-final-form-design.md`](../../docs/superpowers/specs/2026-04-27-kebab-final-form-design.md) §5 / §9 +> - Plan: [`docs/superpowers/plans/2026-05-10-p9-fb-39b-embedding-upgrade.md`](../../docs/superpowers/plans/2026-05-10-p9-fb-39b-embedding-upgrade.md) + +## 요약 + +- `multilingual-e5-small` (384 dim) → `multilingual-e5-large` (1024 dim) default flip. +- 기존 user TOML 이 small 명시 시 그대로 유지 (backwards-compat). +- fb-23 incremental ingest 가 embedding_version mismatch 감지 → 자동 re-embed. +- 0.6 → 0.7 minor bump 트리거 (design §9 cascade rule). + +## 구현 항목 + +1. **config defaults flip** — `[models.embedding] model = "multilingual-e5-large"`, `dimensions = 1024`. +2. **fastembed e5-large resolution** — `kebab-embed-local` 의 `resolve_model()` 에 e5-large arm 추가. +3. **fixture sweep** — 모든 unit/integration 테스트의 default embedding 모델 확인. Config 에서 명시하지 않으면 새 default 따름 (`provider = "none"` 테스트 제외). +4. **design contract update** — design §5 (storage example) + §9 (versioning table) 의 embedding_model.id + dimensions 갱신. +5. **HOTFIXES entry** — 사용자 재 ingest 절차 + backwards-compat 동작 명시. +6. **README update** — `[models.embedding]` 섹션의 기본값 + `dimensions` 필드 설명 갱신. +7. **SMOKE.md append** — 스모크 테스트 중 embedding 업그레이드 검증 절차 (reset → config 갱신 → ingest → eval). +8. **tasks/INDEX.md append** — p9-fb-39b row 추가 (p9-fb-39 sibling). + +## Allowed dependencies + +- `kebab-embed-local` — fastembed crate + `kebab-core` +- `kebab-config` — toml crate +- `kebab-store-vector` — lancedb crate (table naming 로직만 영향) +- `kebab-app` — 와이어링만 (API 변경 없음) + +## Forbidden dependencies + +- parse-* crate (parser 무관) +- llm-* crate (embedding 과 무관) +- search crate (검색 로직은 adapter pattern 으로 이미 generic) + +## Test + +- `cargo test -p kebab-embed-local -- e5_large` (새 arm 테스트) +- `cargo test -p kebab-config -- embedding_defaults` (config defaults) +- `cargo test --workspace --no-fail-fast -j 1` (full regression) +- Smoke: `kebab --config /tmp/smoke.toml doctor | grep embedding` → `multilingual-e5-large (1024d)` +- Smoke: `kebab --config /tmp/smoke.toml ingest` → embedding 진행 표시 + dimension check +- P@k eval: `kebab eval run` (fb-39 의 golden set) small vs large 비교 + +## Backward compat notes + +- Pre-fb-39b user 가 config 에서 명시하지 않은 embedding → new default (large) 자동 적용. TOML 에 `model = "multilingual-e5-small"` 명시하면 유지. +- `kebab_app::config::Config` 의 `embedding_model` field 는 Optional 이므로 old config (small) 도 parse 성공 (v1 설계 §9 cascade 규칙). +- Orphan LanceDB table (`chunk_embeddings_multilingual-e5-small_384`) 은 다음 `kebab ingest` 실행 후 stale 취급 — 사용자가 수동 `kebab reset --vector-only` 로 정리 가능. + +## Binary version bump + +- 0.6.0 → 0.7.0 (design §9 cascade rule: embedding_model change = minor bump). +- Release notes: `embedding default: multilingual-e5-small (384d) → multilingual-e5-large (1024d), P@k metric ↑`. + +## Post-merge deviation + +None — 설계 contract 대로 구현 완료.