docs(fb-39b): design + HOTFIXES + new task spec + INDEX + README + SMOKE
Tasks 4 + 5: comprehensive doc update for embedding upgrade (multilingual-e5-large). - design §5 + §9: update embedding_model / dimensions references (384 -> 1024) - HOTFIXES: add fb-39b entry with user re-ingest procedure + backwards-compat notes - tasks/p9-fb-39b-embedding-upgrade.md: new task spec (completed status) - INDEX.md: add fb-39b row under RAG quality phase - fb-39 task banner: append fb-39b link as lever implementation - README: update config defaults + fastembed model size + embedding field docs - SMOKE.md: append embedding upgrade verification section with e5-small -> e5-large sequence Wire schema: no change (additive at config level, new table created by existing code). Binary version: 0.6.0 -> 0.7.0 (cascade rule: embedding_model change = minor bump). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
11
README.md
11
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 <path>` flag — 임시 워크스페이스 / 격리 테스트 시 사용. CLI / TUI 모두 honor.
|
||||
- `KEBAB_*` env — 일부 키 override (`KEBAB_RAG_SCORE_GATE`, `KEBAB_EVAL_GOLDEN`, `KEBAB_COMMIT_HASH` 등).
|
||||
|
||||
@@ -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) 참조.
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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` 세 필드를 가짐.
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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) ✅
|
||||
|
||||
## 증상 / 동기
|
||||
|
||||
|
||||
75
tasks/p9/p9-fb-39b-embedding-upgrade.md
Normal file
75
tasks/p9/p9-fb-39b-embedding-upgrade.md
Normal file
@@ -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 대로 구현 완료.
|
||||
Reference in New Issue
Block a user