Skip to content

feat(embedding): 임베딩 모델 불일치 가드 — 교차모델 벡터 silent 저하 방지 - #142

Merged
hang-in merged 3 commits into
mainfrom
feat/embedding-model-guard
Jul 6, 2026
Merged

feat(embedding): 임베딩 모델 불일치 가드 — 교차모델 벡터 silent 저하 방지#142
hang-in merged 3 commits into
mainfrom
feat/embedding-model-guard

Conversation

@hang-in

@hang-in hang-in commented Jul 6, 2026

Copy link
Copy Markdown
Owner

배경

기본 임베딩이 bge-m3qwen3-embedding:0.6b(#120)로 바뀌면서, 기존 bge-m3 벡터를 가진 사용자가 업그레이드하면 쿼리는 qwen 으로 임베딩돼 교차모델 불일치가 발생합니다. 두 모델 다 1024-dim 이라 차원 에러는 안 나고, 유사도만 무의미해져 시맨틱/하이브리드 검색이 조용히 저하됩니다 (#121 위키 버그와 동일 메커니즘). 지금은 아무 경고도 없어 사용자가 검색이 망가진 줄도 모릅니다.

변경

  • DEFAULT_OLLAMA_EMBED_MODEL 상수화 (search/embedding.rs) — magic string 제거.
  • EmbeddingConfig::embedding_identity()backend:model 정규화 식별자 (모델/백엔드 변경 감지 기준).
  • Database::get/set_embedding_model()config KV 에 벡터 생성 모델 마커 저장 (schema_version 과 동일 패턴).
  • secall embed — 재임베딩(--all)/최초 embed 후 마커 저장. incremental 시 마커≠현재모델(불일치)이거나 마커 없이 벡터만 있으면(v0.7.0 이전 DB) 경고.
  • secall status — 동일 불일치/미표시 경고.
  • do_status(REST/MCP)embedding_model 마커 노출 (web UI 활용 가능).

마이그레이션 안내

  • secall embed --all — qwen 으로 전체 재임베딩 (권장) → 마커 확정 + 경고 해소
  • secall config set embedding.ollama_model bge-m3 — 기존 모델 유지
  • (BM25 키워드 검색은 임베딩을 안 쓰므로 무관)

검증

  • cargo test 744 pass (신규 2: embedding_identity · embedding_model_marker_roundtrip)
  • clippy --workspace --all-targets (-Dwarnings) clean

후속 (별건)

  • secall sync 의 ingest embed 경로에도 동일 경고 연결 (현재는 embed/status 만).

머지 전

  • CI green + CodeRabbit 확인

Summary by CodeRabbit

  • New Features
    • 상태 화면에서 임베딩 모델 정합성(저장된 모델 vs 현재 설정) 검증과 불일치 경고/재임베딩 권장을 추가했습니다.
    • 임베딩 설정을 백엔드별 규칙에 맞춘 식별자(embedding identity)로 정규화해 모델 비교 정확도를 높였습니다.
  • Bug Fixes
    • DB에 임베딩 모델 마커가 없거나 이전 데이터인 경우에도 재임베딩 안내가 더 정확해졌습니다.
  • Documentation
    • 기본 임베딩 모델(v0.6.5+) 변경에 따른 업그레이드 안내와 secall embed --all 또는 기존 모델 유지 방법, BM25는 영향 없음 안내를 추가했습니다.

기본 임베딩이 bge-m3 → qwen(#120)으로 바뀌면서, 기존 bge-m3 벡터를 가진
사용자가 업그레이드하면 쿼리는 qwen 으로 임베딩돼 교차모델 불일치가 발생한다.
차원이 같아(1024) 에러는 안 나고 유사도만 무의미해져 시맨틱 검색이 조용히
저하된다(#121 위키 버그와 동일 메커니즘). 이를 감지·경고한다.

- `DEFAULT_OLLAMA_EMBED_MODEL` 상수화 (embedding.rs, magic string 제거).
- `EmbeddingConfig::embedding_identity()` — `backend:model` 정규화 식별자.
- `Database::get/set_embedding_model()` — config KV 에 벡터 생성 모델 마커 저장.
- `secall embed`: 재임베딩(--all)/최초 embed 후 마커 저장. incremental 시 마커와
  현재 모델이 다르거나(불일치) 마커가 없는데 벡터가 있으면(v0.7.0 이전 DB) 경고.
- `secall status`: 동일 불일치/미표시 경고 출력.
- `do_status`(REST/MCP): `embedding_model` 마커 노출 (web UI 활용).

마이그레이션: 기존 사용자는 `secall embed --all`(qwen 재임베딩) 또는
`secall config set embedding.ollama_model bge-m3`(기존 유지). BM25 키워드는 무관.

검증: cargo test 744(신규 2: identity·marker roundtrip), clippy -Dwarnings clean.
후속: sync 의 ingest embed 경로에도 동일 경고 연결(현재는 embed/status 만).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LVEL1SnCQBtUMeWVy99ebj
@coderabbitai

coderabbitai Bot commented Jul 6, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 2759b4fa-cdc4-4fc3-971d-d101bd8fb419

📥 Commits

Reviewing files that changed from the base of the PR and between 96253fa and 94d8cf9.

📒 Files selected for processing (1)
  • README.en.md
✅ Files skipped from review due to trivial changes (1)
  • README.en.md

📝 Walkthrough

Walkthrough

임베딩 모델 식별자를 정규화해 계산하고, DB에 저장된 마커와 비교해 embedstatus 흐름에서 불일치를 감지하도록 바뀌었습니다. MCP 상태 응답에도 저장된 임베딩 모델이 포함되고, 관련 안내 문구와 테스트가 추가되었습니다.

Changes

임베딩 모델 identity 추적

Layer / File(s) Summary
임베딩 identity 계산 및 기본 모델 상수
crates/secall-core/src/vault/config.rs, crates/secall-core/src/search/embedding.rs, README.md, README.en.md
EmbeddingConfig::embedding_identity()가 backend별 식별자를 생성하고, DEFAULT_OLLAMA_EMBED_MODEL 상수가 기본값 계산에 사용된다. 관련 업그레이드 안내 문서도 함께 추가된다.
DB embedding_model 마커 저장/조회
crates/secall-core/src/store/db.rs
Databaseget_embedding_model(), set_embedding_model()가 추가되어 config 테이블의 임베딩 모델 식별자를 조회/저장하며, roundtrip 테스트가 함께 추가된다.
embed 명령의 마커 검증 및 갱신
crates/secall/src/commands/embed.rs
run이 실행 전 저장된 모델과 현재 identity 불일치/마커 부재를 경고하고, 실행 후 조건에 따라 DB 마커를 갱신한다.
status 및 MCP 응답의 정합성 경고/노출
crates/secall/src/commands/status.rs, crates/secall-core/src/mcp/server.rs
status 명령이 저장된 임베딩 모델과 현재 identity 불일치나 마커 부재를 경고하고, do_status JSON 응답에 embedding_model 필드가 추가된다.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Sequence Diagram(s)

sequenceDiagram
  participant CLI as secall embed/status
  participant Config as EmbeddingConfig
  participant DB as Database
  participant MCP as mcp::server

  CLI->>Config: embedding_identity()
  CLI->>DB: get_embedding_model()
  CLI->>DB: count embedded vectors
  CLI->>DB: set_embedding_model(identity)
  MCP->>DB: get_embedding_model()
  MCP->>Config: embedding_identity()
Loading
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed 임베딩 모델 불일치 감지 및 경고로 교차모델 벡터 저하를 막는 핵심 변경을 잘 요약합니다.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/embedding-model-guard

Comment @coderabbitai help to get the list of available commands.

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces embedding model mismatch detection to prevent semantic search degradation caused by cross-model embeddings. It adds database functions to store and retrieve the embedding model marker, a normalized identifier generator (embedding_identity), and warnings in the embed and status commands when a mismatch is detected. The review feedback highlights three key areas for improvement: avoiding platform/user-dependent paths in the model identifier by using only the file name, and properly propagating database errors using the ? operator in both the MCP server and the status command instead of silently ignoring them.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment on lines +387 to +394
"ort" | "openvino" => format!(
"{}:{}",
self.backend,
self.model_path
.as_ref()
.map(|p| p.display().to_string())
.unwrap_or_else(|| "BAAI/bge-m3".to_string())
),

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

p.display().to_string()를 사용하여 전체 경로를 식별자로 사용하면 다음과 같은 문제가 발생할 수 있습니다:

  1. 플랫폼 의존성: Windows(역슬래시)와 Unix(슬래시) 간 경로 구분자가 달라 동일한 상대 경로라도 식별자가 다르게 생성됩니다.
  2. 사용자/기기 의존성: 절대 경로(예: ~/.cache/...)를 사용할 경우, 사용자 홈 디렉토리 경로가 달라져 기기 간 동기화 시 불일치 경고가 발생합니다.

seCall은 여러 기기나 OS 간에 볼트(Vault) 및 DB를 동기화하여 사용하는 경우가 많으므로, 경로 전체 대신 p.file_name()을 통해 모델 디렉토리명(예: bge-m3-onnx)만 추출하여 식별자로 사용하는 것이 훨씬 안전하고 견고합니다.

Suggested change
"ort" | "openvino" => format!(
"{}:{}",
self.backend,
self.model_path
.as_ref()
.map(|p| p.display().to_string())
.unwrap_or_else(|| "BAAI/bge-m3".to_string())
),
"ort" | "openvino" => format!(
"{}:{}",
self.backend,
self.model_path
.as_ref()
.and_then(|p| p.file_name().map(|n| n.to_string_lossy().into_owned()))
.unwrap_or_else(|| "BAAI/bge-m3".to_string())
),

.map_err(|e| anyhow::anyhow!("DB lock: {e}"))?;
let stats = db.get_stats()?;
// 벡터가 생성된 임베딩 모델 마커 (없으면 null — v0.7.0 이전 DB 이거나 벡터 없음).
let embedding_model = db.get_embedding_model().ok().flatten();

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

db.get_embedding_model()이 반환하는 데이터베이스 에러를 .ok().flatten()으로 무시하면, 실제 DB 에러가 발생했을 때 원인을 파악하기 어렵고 단순히 마커가 없는 것으로 오인될 수 있습니다. 앞선 db.get_stats()?와 마찬가지로 ? 연산자를 사용하여 에러를 상위로 전파하는 것이 안전합니다.

Suggested change
let embedding_model = db.get_embedding_model().ok().flatten();
let embedding_model = db.get_embedding_model()?;

Comment thread crates/secall/src/commands/status.rs Outdated
Comment on lines +69 to +80
match db.get_embedding_model() {
Ok(Some(stored)) if stored != embed_identity => {
println!(" ⚠ 임베딩 모델 불일치: 기존 벡터={stored} / 현재={embed_identity}");
println!(" → `secall embed --all` 로 재임베딩 권장 (BM25 키워드 검색은 무관)");
}
Ok(None) if stats.vector_count > 0 => {
println!(
" ⚠ 임베딩 모델 마커 없음 (v0.7.0 이전 DB 일 수 있음). 벡터가 현재 모델({embed_identity})과 다르면 `secall embed --all` 권장"
);
}
_ => {}
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

db.get_embedding_model() 호출 시 발생할 수 있는 데이터베이스 에러(Err)가 _ => {} 패턴 매칭에 의해 아무런 경고나 에러 전파 없이 무시되고 있습니다. run() 함수가 Result<()>를 반환하므로, ? 연산자를 사용하여 에러를 명시적으로 전파하는 것이 안전합니다.

Suggested change
match db.get_embedding_model() {
Ok(Some(stored)) if stored != embed_identity => {
println!(" ⚠ 임베딩 모델 불일치: 기존 벡터={stored} / 현재={embed_identity}");
println!(" → `secall embed --all` 로 재임베딩 권장 (BM25 키워드 검색은 무관)");
}
Ok(None) if stats.vector_count > 0 => {
println!(
" ⚠ 임베딩 모델 마커 없음 (v0.7.0 이전 DB 일 수 있음). 벡터가 현재 모델({embed_identity})과 다르면 `secall embed --all` 권장"
);
}
_ => {}
}
match db.get_embedding_model()? {
Some(stored) if stored != embed_identity => {
println!(" ⚠ 임베딩 모델 불일치: 기존 벡터={stored} / 현재={embed_identity}");
println!(" → `secall embed --all` 로 재임베딩 권장 (BM25 키워드 검색은 무관)");
}
None if stats.vector_count > 0 => {
println!(
" ⚠ 임베딩 모델 마커 없음 (v0.7.0 이전 DB 일 수 있음). 벡터가 현재 모델({embed_identity})과 다르면 `secall embed --all` 권장"
);
}
_ => {}
}

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🧹 Nitpick comments (2)
crates/secall-core/src/vault/config.rs (2)

387-400: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

ort/openvino/openai 기본값 하드코딩 중복

"BAAI/bge-m3", "text-embedding-3-large"가 각 임베더 구현체의 실제 기본값과 별도로 여기 하드코딩되어 있습니다. 한쪽만 변경되면 identity 계산이 실제 벡터 생성 모델과 어긋나게 됩니다. 공용 상수로 추출해 양쪽에서 참조하도록 리팩터링을 권장합니다.

♻️ 제안 리팩터링 방향
-            "ort" | "openvino" => format!(
-                "{}:{}",
-                self.backend,
-                self.model_path
-                    .as_ref()
-                    .map(|p| p.display().to_string())
-                    .unwrap_or_else(|| "BAAI/bge-m3".to_string())
-            ),
-            "openai" => format!(
-                "openai:{}",
-                self.openai_model
-                    .as_deref()
-                    .unwrap_or("text-embedding-3-large")
-            ),
+            "ort" | "openvino" => format!(
+                "{}:{}",
+                self.backend,
+                self.model_path
+                    .as_ref()
+                    .map(|p| p.display().to_string())
+                    .unwrap_or_else(|| DEFAULT_ORT_MODEL_PATH.to_string())
+            ),
+            "openai" => format!(
+                "openai:{}",
+                self.openai_model.as_deref().unwrap_or(DEFAULT_OPENAI_MODEL)
+            ),

실제 임베더 구현체가 정의한 상수를 재사용하도록 DEFAULT_ORT_MODEL_PATH/DEFAULT_OPENAI_MODEL을 해당 모듈에서 노출해 주세요.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/secall-core/src/vault/config.rs` around lines 387 - 400, The identity
formatting in the config logic hardcodes default model values for
ort/openvino/openai instead of reusing the defaults defined by the embedder
implementations. Update the identity-building code in config.rs to reference
shared exported constants from the relevant embedder modules, such as
DEFAULT_ORT_MODEL_PATH and DEFAULT_OPENAI_MODEL, so both identity calculation
and actual vector generation stay aligned when defaults change.

670-682: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

identity 테스트 커버리지 확장 제안

ollama_cloud/openvino/openai/none/other 분기는 테스트되지 않았습니다. 정합성 감지의 근간이 되는 함수이므로 각 분기별 기대값을 검증하는 테스트 추가를 권장합니다.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/secall-core/src/vault/config.rs` around lines 670 - 682, The
embedding_identity test currently covers only the default ollama case and one
backend switch, leaving several EmbeddingConfig branches unverified. Expand the
existing test_embedding_identity in EmbeddingConfig to add assertions for the
ollama_cloud, openvino, openai, none, and other paths, using
embedding_identity() so each variant’s expected identity string is explicitly
checked. Keep the test grouped with the current identity coverage so changes to
backend/model selection remain easy to validate.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@crates/secall-core/src/vault/config.rs`:
- Around line 381-386: The ollama_cloud identity fallback in
embedding_identity() is out of sync with the runtime model selection in vector
search. Update the identity construction so it follows the same order as the
model resolution logic in search/vector.rs: prefer cloud_model, then
ollama_model, then DEFAULT_OLLAMA_EMBED_MODEL. This ensures the marker matches
the actual embedded model when cloud_model is unset but ollama_model is
configured.

In `@crates/secall/src/commands/embed.rs`:
- Around line 78-100: 임베딩이 비활성화된 backend = "none" 상태에서도 embedding_identity()가
"none"으로 비교되어 불필요한 불일치 경고와 --all 재임베딩 권고가 나오는 문제입니다. embed 명령의 기존 모델 비교 블록에서
config.embedding.backend != "none"일 때만 stored_model와 existing_vectors를 검사하도록
감싸고, 동일한 비교 로직이 복제된 status 명령의 관련 블록도 같은 조건으로 맞춰 일관되게 처리하세요.
- Around line 78-100: `embed` command의 `get_stats()` 실패를 `unwrap_or(0)`으로 숨기지 말고
오류를 전파하도록 바꾸세요. `existing_vectors`를 계산하는 부분에서 `db.get_stats()`가 실패하면 `?`로 즉시
반환하게 하여, 이후 `stored_model.is_none() && existing_vectors == 0` 같은 마커 확정 로직이 조회
오류를 0으로 오인하지 않도록 수정하세요. 이 변경은 `embed_identity`, `stored_model`,
`existing_vectors`를 사용하는 경고/확정 흐름 전체에 적용해 안전하게 유지하세요.

---

Nitpick comments:
In `@crates/secall-core/src/vault/config.rs`:
- Around line 387-400: The identity formatting in the config logic hardcodes
default model values for ort/openvino/openai instead of reusing the defaults
defined by the embedder implementations. Update the identity-building code in
config.rs to reference shared exported constants from the relevant embedder
modules, such as DEFAULT_ORT_MODEL_PATH and DEFAULT_OPENAI_MODEL, so both
identity calculation and actual vector generation stay aligned when defaults
change.
- Around line 670-682: The embedding_identity test currently covers only the
default ollama case and one backend switch, leaving several EmbeddingConfig
branches unverified. Expand the existing test_embedding_identity in
EmbeddingConfig to add assertions for the ollama_cloud, openvino, openai, none,
and other paths, using embedding_identity() so each variant’s expected identity
string is explicitly checked. Keep the test grouped with the current identity
coverage so changes to backend/model selection remain easy to validate.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 790fd750-6d45-4135-b3c6-cbb37e7e9980

📥 Commits

Reviewing files that changed from the base of the PR and between 86b8f8b and 9958aea.

📒 Files selected for processing (6)
  • crates/secall-core/src/mcp/server.rs
  • crates/secall-core/src/search/embedding.rs
  • crates/secall-core/src/store/db.rs
  • crates/secall-core/src/vault/config.rs
  • crates/secall/src/commands/embed.rs
  • crates/secall/src/commands/status.rs

Comment on lines +381 to +386
"ollama_cloud" => format!(
"ollama_cloud:{}",
self.cloud_model
.as_deref()
.unwrap_or(DEFAULT_OLLAMA_EMBED_MODEL)
),

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# ollama_cloud 관련 필드/임베더 기본값 확인
rg -nP -B3 -A15 'cloud_model' crates/secall-core/src/search/embedding.rs
rg -nP -B3 -A15 'struct.*Cloud|OllamaCloud' crates/secall-core/src
rg -nP 'DEFAULT_OLLAMA_EMBED_MODEL' -g '*.rs'

Repository: hang-in/seCall

Length of output: 7293


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# 관련 상수/분기 위치와 기본값을 좁혀서 확인
rg -n --hidden --glob '*.rs' 'DEFAULT_OLLAMA_EMBED_MODEL|cloud_model|ollama_cloud|embedding_identity|semantic_backend|embed' crates/secall-core/src

# config.rs의 embedding_identity 주변 읽기
sed -n '340,430p' crates/secall-core/src/vault/config.rs

# embedding 관련 구현/테스트가 있으면 확인
fd -a 'embedding.rs' crates/secall-core/src
fd -a 'config.rs' crates/secall-core/src
fd -a 'test.*rs' crates/secall-core/src | rg 'embedding|vault|config'

Repository: hang-in/seCall

Length of output: 46052


🏁 Script executed:

#!/bin/bash
set -euo pipefail

sed -n '1,120p' crates/secall-core/src/search/embedding.rs
sed -n '500,540p' crates/secall-core/src/search/vector.rs
sed -n '370,405p' crates/secall-core/src/vault/config.rs

Repository: hang-in/seCall

Length of output: 6898


ollama_cloud identity의 fallback 순서를 런타임과 맞춰주세요
crates/secall-core/src/search/vector.rs:509-519에서는 cloud_model -> ollama_model -> DEFAULT_OLLAMA_EMBED_MODEL 순으로 실제 모델을 고르는데, crates/secall-core/src/vault/config.rs:381-386embedding_identity()cloud_model이 없으면 바로 DEFAULT_OLLAMA_EMBED_MODEL로 떨어집니다. cloud_model이 비어 있고 ollama_model만 설정된 경우 실제 모델과 다른 마커가 저장돼 불필요한 재임베딩/불일치 판정이 발생합니다.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/secall-core/src/vault/config.rs` around lines 381 - 386, The
ollama_cloud identity fallback in embedding_identity() is out of sync with the
runtime model selection in vector search. Update the identity construction so it
follows the same order as the model resolution logic in search/vector.rs: prefer
cloud_model, then ollama_model, then DEFAULT_OLLAMA_EMBED_MODEL. This ensures
the marker matches the actual embedded model when cloud_model is unset but
ollama_model is configured.

Comment on lines +78 to +100
// 임베딩 모델 불일치 감지: 기존 벡터가 다른 모델로 생성됐으면 교차모델 유사도가
// 무의미해 시맨틱 검색이 조용히 저하된다(차원이 같아 에러는 안 남). 재임베딩(--all)
// 이 아니면 경고만 하고 진행한다.
let embed_identity = config.embedding.embedding_identity();
let stored_model = db.get_embedding_model()?;
let existing_vectors = db.get_stats().map(|s| s.vector_count).unwrap_or(0);
if !all {
match &stored_model {
Some(m) if *m != embed_identity => {
eprintln!("⚠ 임베딩 모델 불일치: 기존 벡터={m}, 현재={embed_identity}");
eprintln!(
" 섞이면 시맨틱 검색 정확도가 떨어집니다. `secall embed --all` 로 전체 재임베딩을 권장합니다."
);
}
None if existing_vectors > 0 => {
eprintln!(
"⚠ 임베딩 모델 마커가 없습니다 (v0.7.0 이전 DB 일 수 있음). 기존 벡터가 현재 모델({embed_identity})과 다르면 `secall embed --all` 을 권장합니다."
);
}
_ => {}
}
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

backend = "none"일 때 오해 소지 있는 불일치 경고

임베딩이 비활성화된 상태(backend = "none")에서도 embedding_identity()"none"을 반환해 저장된 마커와 비교되고, "기존 벡터=... / 현재=none, embed --all 로 재임베딩 권장" 경고가 출력됩니다. 사용자가 의도적으로 임베딩을 끈 상황이라면 이 경고/권고는 혼란을 줍니다. crates/secall/src/commands/status.rs의 동일 로직(69-80행)에도 같은 패턴이 복제되어 있어 같은 문제가 발생합니다. config.embedding.backend != "none" 조건으로 이 검사 블록 전체를 감싸는 편을 권장합니다.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/secall/src/commands/embed.rs` around lines 78 - 100, 임베딩이 비활성화된
backend = "none" 상태에서도 embedding_identity()가 "none"으로 비교되어 불필요한 불일치 경고와 --all
재임베딩 권고가 나오는 문제입니다. embed 명령의 기존 모델 비교 블록에서 config.embedding.backend != "none"일
때만 stored_model와 existing_vectors를 검사하도록 감싸고, 동일한 비교 로직이 복제된 status 명령의 관련 블록도
같은 조건으로 맞춰 일관되게 처리하세요.

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

get_stats() 오류를 0으로 처리하면 마커가 잘못 확정될 위험

existing_vectorsunwrap_or(0)으로 계산되어, get_stats()가 실패하면 실제 벡터가 존재하더라도 0으로 간주됩니다. 이 값은 251행 이하 마커 확정 조건(stored_model.is_none() && existing_vectors == 0)에 그대로 쓰이므로, DB 조회 오류가 발생한 순간 기존 벡터가 있는데도 현재 모델로 마커가 확정될 수 있습니다. 이는 교차모델 오염을 감지하기 위한 이 기능의 목적을 정면으로 무력화합니다. ?로 오류를 전파하는 편이 안전합니다.

🛡️ 제안 수정
-    let existing_vectors = db.get_stats().map(|s| s.vector_count).unwrap_or(0);
+    let existing_vectors = db.get_stats()?.vector_count;

Also applies to: 251-259

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/secall/src/commands/embed.rs` around lines 78 - 100, `embed` command의
`get_stats()` 실패를 `unwrap_or(0)`으로 숨기지 말고 오류를 전파하도록 바꾸세요. `existing_vectors`를
계산하는 부분에서 `db.get_stats()`가 실패하면 `?`로 즉시 반환하게 하여, 이후 `stored_model.is_none() &&
existing_vectors == 0` 같은 마커 확정 로직이 조회 오류를 0으로 오인하지 않도록 수정하세요. 이 변경은
`embed_identity`, `stored_model`, `existing_vectors`를 사용하는 경고/확정 흐름 전체에 적용해 안전하게
유지하세요.

dghongut and others added 2 commits July 6, 2026 20:43
- ort/openvino embedding_identity 를 전체 경로 → 파일명(file_name)만 사용
  (절대경로/OS 구분자 차이로 크로스머신 동기화 시 헛불일치 방지, Gemini HIGH).
- backend="none" 시 불일치 경고 skip (embed + status 양쪽, CodeRabbit Major).
- embed: get_stats() 실패를 0 으로 뭉개지 않고 `?` 전파 (마커 오확정 방지, Major).
- README(ko/en): bge-m3 → qwen3-embedding 업그레이드 안내 추가
  (embed --all / config set 마이그레이션 + status 감지 안내).

검증: cargo test config identity pass, clippy -Dwarnings clean.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LVEL1SnCQBtUMeWVy99ebj
기존 ONNX 블록쿼트 바로 뒤 블록쿼트라 사이 빈 줄이 MD028(blank line inside
blockquote)로 걸림. 일반 문단+리스트로 변경. markdownlint 로컬 0 errors 확인.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LVEL1SnCQBtUMeWVy99ebj
@hang-in
hang-in merged commit dcea0c9 into main Jul 6, 2026
5 checks passed
hang-in pushed a commit that referenced this pull request Jul 6, 2026
…베딩 가드

#137~#142 (6 PR) + v0.7.0 릴리즈 요약. backlog(가드 sync-path·#14/#15 재설계·
스케줄러) + 인프라(DB 경로·8080/5180·정크정리) + 학습(do_get 회귀·교차모델).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LVEL1SnCQBtUMeWVy99ebj
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant