- commit track-spec + meta-spec/plan into branch (HIGH: dangling `amends:` ref) - inline parity evidence (cosine 1.0, max_abs_diff 2.01e-7) into HOTFIXES + release notes; drop refs to deleted IMPL_REPORT/SPIKE_REPORT (MEDIUM) - model guard: reject non-e5-large `model` before the 2GB download so model_id() can't mislabel vectors (MEDIUM) + unit test - parity test now covers BOTH query: and passage: prefixes (MEDIUM) - guard encodings.first() index; document zero-attention/pooling invariant; clarify embed_batch prefixing doc (LOW) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
5.9 KiB
5.9 KiB
Track 1 Spec — candle e5-large 임베딩 provider (NUMA-안전)
- 날짜: 2026-06-01
- 우산: meta-spec / meta-plan
- 선행: Phase 0 스파이크 PASS+독립검증 (cosine 1.000000, 스레드 캡 가능, latency ~4×). 커밋
76841af. - 브랜치:
feat/embed-candle
1. 목표
fastembed(onnxruntime) 의 "intra-op 스레드 48 하드코딩 → NUMA 힙 손상" 을 회피하기 위해, 동일 모델 multilingual-e5-large 를 candle(순수 Rust) 로 돌리는 임베딩 provider 를 추가한다. opt-in, 품질 중립, NUMA 스레드 캡 가능.
2. 확정 결정 (사용자 승인 2026-06-01)
- D-reindex:
embedding_version유지(재색인 0) 를 목표. 구현 중 candle vs onnxruntime 벡터의 차원별 max 절대오차를 측정해 사실상 동일(예: max abs diff < 1e-5)함을 확인하고, 골든 스위트로 회귀 0 을 실측해 확정. 유의미한 차이가 나오면만 version bump + 재색인. - D-default: 글로벌 default provider 는 onnxruntime 유지, candle 은 opt-in (
models.embedding.provider = "candle"). - 조기 종료: candle 이 골든 baseline 충족 시 ollama/A2 트랙 생략 (A1 stopgap 문서만 별도).
3. 아키텍처
- 신규 crate
kebab-embed-candle—kebab_core::Embedder구현. candle 의 큰 의존성 트리를 이 crate 에 격리.- 허용 deps:
candle-core/candle-nn/candle-transformers(0.10.x),tokenizers,hf-hub,kebab-core,kebab-config,anyhow,tracing. 다른kebab-*의존 금지(core/config 외) — design §8 경계.
- 허용 deps:
- 주입 분기:
kebab-app/src/app.rs의embedder()(현 :829-837,FastembedEmbedder::new무조건 생성) 를config.models.embedding.provider로 분기:"fastembed"|"onnx"| (빈값/기존) →FastembedEmbedder(default, 기존 동작 유지)."candle"→CandleEmbedder.- 알 수 없는 값 → 명확한 에러.
- facade 규칙 준수: UI crate 는
kebab-app만.kebab-app이kebab-embed-candle의존 추가.
4. CandleEmbedder 동작 (스파이크에서 검증된 파이프라인)
- 모델:
intfloat/multilingual-e5-large의model.safetensors+config.json+tokenizer.json을hf-hub으로{model_dir}/candle/(configstorage.model_dir) 아래에 캐시. candle_transformers::models::xlm_roberta::{Config, XLMRobertaModel}로 로드 (CPUDevice::Cpu).embed(): e5 프리픽스(query:/passage:,EmbeddingInputkind 기준 —kebab-embed-local의prefix_input규약과 동일) → 토크나이즈(max_len 512, batch-longest 패딩, special tokens) → forward → attention-mask 가중 mean pooling → L2 정규화.dimensions()= 1024,model_id/model_version= config 값(기존과 동일 식별자 유지).- 스레드 캡: config 신규 필드
models.embedding.num_threads(u32, 0=auto) + envKEBAB_EMBED_THREADS.CandleEmbedder::new에서rayon::ThreadPoolBuilder::new().num_threads(n).build_global()1회 적용(이미 초기화 시 무시). 0/auto 면 미설정(rayon 기본). NUMA 노드 바인딩은numactl(A1) 과 조합 — 문서화. Mutex<XLMRobertaModel>또는 forward 가&self면 불필요 — candle forward 는&self가능, 단 내부 가변 없으면Send+Sync보장 확인.
5. config 변경
EmbeddingModelCfg에num_threads: u32(default 0) 추가. envKEBAB_EMBED_THREADS.provider허용값 문서화:fastembed(default)/candle.- default toml +
Config::default()갱신, 기존 테스트 영향 확인.
6. 버전/캐스케이드
- D-reindex 에 따라
embedding_version유지 (벡터 동일). cascade(design §9) 트리거 안 함 — 기존 색인 재사용. (max abs diff 확인 실패 시에만 bump.) - wire schema 변경 없음.
7. 테스트 (산출물)
- 단위(
kebab-embed-candle):dimensions()==1024;embed()출력 L2≈1; 빈 입력 빈 출력; 프리픽스 적용 확인. - 패리티 테스트(
#[ignore], 모델 2GB+네트워크 필요): candle vsFastembedEmbedder동일 문장 cosine ≥ 0.9999 + max abs diff 보고. CI 기본 제외, 수동/도그푸딩에서 실행. - 통합(
kebab-cli또는kebab-app):provider="candle"로 소량 fixture ingest → 청크/임베딩 카운트 > 0, 검색 1건 성공. (모델 필요 →#[ignore]또는 feature.) - 스레드 캡:
num_threads=4설정 시rayon::current_num_threads()==4확인. - 회귀: 기존 fastembed 경로 default 동작 불변(provider 미지정 시).
- clippy
-D warnings, 빌드 직렬-j 4.
8. 품질 게이트 (머지 전)
kebab-eval골든 스위트(/build/dogfood/golden_queries.yaml) 를 provider=candle 로 실행 → MRR/hit@k ≥ 현 baseline (회귀 0). feedback_search_quality_dogfood- 패러프레이즈 robustness(#195/#196) 스폿 확인.
9. 문서/릴리스 (머지 시 동일 PR)
- README: Configuration 에
provider=candle+num_threads/KEBAB_EMBED_THREADS추가. SMOKE config 예시 동기화. feedback_readme_sync_rule - ARCHITECTURE: crate 그래프 + 디렉터리에
kebab-embed-candle추가. - HANDOFF: 머지 후 한 줄(임베딩 백엔드 다변화).
- HOTFIXES: 본 날짜 dated entry (NUMA double-free 진단 + candle provider 도입 + 스파이크 패리티 증거).
- 버전 bump: 신규 config surface(provider=candle, num_threads) = pre-1.0 minor bump (0.21.1 → 0.22.0), release notes.
10. 범위 밖 / 후속
- candle crate feature-gate 로 빌드 비용 격리 (후속).
- NUMA 노드 자동 바인딩(현재는 numactl 외부 조합).
- ollama/A2/A1 트랙 (candle 게이트 통과 시 생략).
11. 잔여 게이트 (사용자 실행, Claude 불가)
- 그 듀얼소켓 NUMA 서버에서
provider=candle로 5150-doc ingest double-free 없이 EXIT=0 완주. PR 머지 전/후 검증 예약. (meta-spec §4.3)