feat(app): 임베딩 캐시 전면화 — image/pdf/code 를 embed_with_cache 경유로 #216
Reference in New Issue
Block a user
Delete Branch "feat/embed-cache-handlers"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
요약
image/pdf/code 청크 임베딩을 markdown 이 이미 쓰는
embed_with_cache(content-hash derivation 캐시) 경유로 통일한다. 그동안 markdown 만 캐시를 타고 나머지 3 미디어는 직접emb.embed라, 재인덱싱 시 청크 텍스트가 byte-identical 이어도 매번 재임베딩했다. 이제 4 미디어 모두 동일 경로 — 출력은 byte-identical(fresh-dir=전부 miss=기존과 동일, 재인덱싱=캐시 히트로 동일 벡터 반환), 재인덱싱 비용만 줄어든다. 미디어 무관 version_key(doc|{model_id}|{model_version}|{dim})라 같은 텍스트는 미디어 교차로 한 엔트리를 공유한다.설계: docs/superpowers/specs/2026-06-24-cache-everywhere-ocr-caption-design.md §2
계획: docs/superpowers/plans/2026-06-24-pr1-embed-cache-everywhere.md
변경
crates/kebab-app/src/ingest.rs): image/pdf/code 의 직접emb.embed(&inputs)→ markdown 패턴 verbatimembed_with_cache(&**emb, &app.sqlite, &body_texts, &emb_version_key, …)+ upsert 직후derivation_cache_touch.records/upsert블록·version_key 는 무변경(byte-identical).tracing::info!로 노출 — 도그푸딩 시 4 미디어 전부에서 warm 재인덱싱이 embed 0 회인지 확인 가능.crates/kebab-app/tests/embed_cache_reingest.rs, AVX-gated#[ignore]): code asset 을 인덱싱 후force_reingest재인덱싱 → 임베딩 캐시 row 불변(순수 히트) + vector-search 결과 byte-identical. workspace 격리(clear_workspace)로 code 핸들러만 측정, faithful-break(직접 embed 복원) 시 실제 fail 확인.검증
cargo clippy --workspace --all-targets -- -D warnings0.cargo test -p kebab-app --test embed_cache_reingest -- --ignored)embed_with_cache/derivation_payload무수정.비범위 / 후속
Assisted-by: Claude Code
(a) embed_with_cache 를 image/pdf/code 핸들러에 배선(markdown 전용→전미디어) — PR1. (b) ocr/caption derivation_cache 네임스페이스로 OCR/caption 중간 산출물 캐싱 — 버전-캐스케이드 분리 + ollama-vision 비결정성 박제 — PR2. 리서치로 해소: provenance 는 wire/게이트에 없어 캐시 히트 시 재현 불필요(byte-identical 유지) · payload 는 full 구조체 serde(block 레벨까지 byte-identical) · derivation_cache_key_bytes 신설 · paddle ONNX 자산은 이미 레포 번들(다운로드 불필요) → 결정적 게이트 가능. 열린 항목: paddle 게이트용 text-bearing OCR 코퍼스 생성(R2, plan 에서 해소).회차 1 — subagent-driven 개발로 task 리뷰 2회 + whole-branch 최종 리뷰를 이미 거쳤고(전부 APPROVE/READY), 그 검증 사실 + 잔여 actionable 1건. REQUEST_CHANGES(actionable nit 1건 — 반영 후 APPROVE).
검증 완료 (재확인)
records(VectorRecord 필드맵)·vec_store.upsert가 pre-PR 대비 diff-proven 동일. version_keydoc|{model_id}|{model_version}|{dim}4 미디어 char-동일(미디어 무관 → 교차 캐시 재사용).embed_with_cache/derivation_payload무수정. 청크 텍스트·순서 보존.derivation_cache_touch위치: upsert 직후·?뒤 → 실패한 upsert 는 키를 used 로 안 찍음(LRU 정합).ff4f9b7): 로깅-only(카운터 hoist + tracing::info), 로직 무변경.clear_workspace로 markdown fixture 제거 → code 핸들러만 측정(없으면 markdown 이 공유 cache 채워 false-green).fusion_score.to_bits()결정적 비교.force_reingest가 skip 우회 → 재임베딩 경로 실제 실행. faithful-break(직접 embed 복원) 시 실제 fail 확인. 3중 어설션(rows>0 / rows 불변 / hits byte-identical) 약화 없음.--workspace --all-targets0.Actionable (nit, 반영 요청)
[nit] 테스트가
clear_workspace격리에만 의존 — 인덱싱 자산 수를 단언하지 않음.crates/kebab-app/tests/embed_cache_reingest.rs:49-50.embedding_cache_rows는 모든kind='embedding'row 를 센다. 지금은clear_workspace가sample.rs단일 자산을 보장해 정확하지만, 미래에TestEnv::with_embeddings()/fixture 가 비-markdown 자산을 추가하면 격리가 조용히 침식된다. 첫 인덱싱 리포트에report.new == 1단언을 더해, 격리 가정이 깨지면 loud fail 하도록 하자. (cheap, byte-neutral.)비-actionable (유지)
if hit+miss>0가드 redundancy → markdown 패턴과 동일, 유지.회차 2 — 회차 1 actionable nit 반영 확인. APPROVE.
ec2ee25): 첫 인덱싱 리포트에report.new == 1단언 → 미래 fixture/with_embeddings 변경으로 비-markdown 자산이 끼어들면 loud fail.clear_workspace격리 가정을 명시적으로 잠금.--ignored테스트 green(39s warm — rows 2/2 불변, hits byte-identical, report.new==1 통과).머지 차단 0건. byte-identical(diff-proven) + 테스트 유효성(회귀 핀, false-green 아님) 모두 확인. APPROVE.