fix(cli): honor --config flag in kb ask (P4-3 follow-up) #24

Merged
altair823 merged 1 commits from fix/cli-ask-honor-config-flag into main 2026-05-01 16:08:25 +00:00
Owner

변경 요약

P3-5 hotfix가 --config flag를 ingest / search / list / inspect / doctor에 wire했지만 P4-3가 ask 본체를 추가할 때 kb-cli의 Cmd::Ask arm이 여전히 kb_app::ask(query, opts)을 직접 호출하고 있었습니다. 같은 버그 패턴이 ask에서 silent하게 재현 — --config <path>을 패스해도 ~/.config/kb/config.toml로 fallback.

발견 경위

P4-3 머지 후 /tmp/kb-smoke/을 192.168.0.47 Ollama (gemma4:26b)에 연결해서 ask 스모크 수행 중 발견:

$ target/debug/kb --config /tmp/kb-smoke/config.toml --json ask "..." --mode hybrid
{ ... "model": {"id": "qwen2.5:14b-instruct", ...}, ... }   # 사용자 XDG default

/tmp/kb-smoke/config.tomlgemma4:26b인데 응답에서는 사용자 XDG의 qwen2.5:14b-instruct가 surface. score_gate, data_dir, embedding 모두 XDG default. --config 자체가 무시되고 있었습니다.

수정

P3-5 hotfix와 동일 패턴:

let cfg = kb_config::Config::load(cli.config.as_deref())?;
let ans = kb_app::ask_with_config(cfg, query, opts)?;

검증

수정 후 동일 query:

{
  "model": {"id": "gemma4:26b", "provider": "ollama"},
  "embedding": {"id": "multilingual-e5-small", "provider": "fastembed", "dimensions": 384},
  "answer": "Rust 소유권 모델의 핵심 규칙은 다음과 같은 세 가지입니다: ...",
  "citations": [{"marker": "[2]", "citation": {"path": "rust/ownership.md", ...}}],
  "grounded": true,
  "refusal_reason": null,
  "retrieval": {"score_gate": 0.005, "top_score": 0.0164, "chunks_returned": 10, ...}
}

스모크 4개 시나리오 모두 통과:

시나리오 결과
한국어 query → 한국어 corpus (Rust ownership 핵심 규칙) grounded [#2] rust/ownership.md
영어 query → 영어 corpus (RRF fusion) grounded [#1] arch/rag-architecture.md
한국어 query → 영어 corpus (e5 prefix) grounded [#1] arch/embeddings.md
out-of-corpus query (airspeed velocity) LlmSelfJudge refusal "근거가 부족하다"

워크스페이스 319 passed, clippy clean.

후속으로 발견된 별도 버그 (이 PR 밖)

config.rag.score_gate default 0.05이 hybrid mode RRF 점수 범위와 incompatible. RRF top은 (0, 2/k_rrf] 즉 default k_rrf=60에서 ≤ 0.033 bounded이라 default 0.05이면 모든 hybrid query에서 ScoreGate refusal. 워크어라운드는 사용자 config에서 0.005로 낮추는 것. 장기 수정은 (a) per-mode gate config 또는 (b) RRF 점수를 [0, 1]로 정규화. 별도 task로 분리.

변경 파일

  • crates/kb-cli/src/main.rs (2줄: Config::load + ask_with_config)
## 변경 요약 P3-5 hotfix가 `--config` flag를 ingest / search / list / inspect / doctor에 wire했지만 P4-3가 ask 본체를 추가할 때 kb-cli의 `Cmd::Ask` arm이 여전히 `kb_app::ask(query, opts)`을 직접 호출하고 있었습니다. 같은 버그 패턴이 ask에서 silent하게 재현 — `--config <path>`을 패스해도 `~/.config/kb/config.toml`로 fallback. ## 발견 경위 P4-3 머지 후 `/tmp/kb-smoke/`을 192.168.0.47 Ollama (`gemma4:26b`)에 연결해서 ask 스모크 수행 중 발견: ``` $ target/debug/kb --config /tmp/kb-smoke/config.toml --json ask "..." --mode hybrid { ... "model": {"id": "qwen2.5:14b-instruct", ...}, ... } # 사용자 XDG default ``` `/tmp/kb-smoke/config.toml`은 `gemma4:26b`인데 응답에서는 사용자 XDG의 `qwen2.5:14b-instruct`가 surface. score_gate, data_dir, embedding 모두 XDG default. `--config` 자체가 무시되고 있었습니다. ## 수정 P3-5 hotfix와 동일 패턴: ```rust let cfg = kb_config::Config::load(cli.config.as_deref())?; let ans = kb_app::ask_with_config(cfg, query, opts)?; ``` ## 검증 수정 후 동일 query: ``` { "model": {"id": "gemma4:26b", "provider": "ollama"}, "embedding": {"id": "multilingual-e5-small", "provider": "fastembed", "dimensions": 384}, "answer": "Rust 소유권 모델의 핵심 규칙은 다음과 같은 세 가지입니다: ...", "citations": [{"marker": "[2]", "citation": {"path": "rust/ownership.md", ...}}], "grounded": true, "refusal_reason": null, "retrieval": {"score_gate": 0.005, "top_score": 0.0164, "chunks_returned": 10, ...} } ``` 스모크 4개 시나리오 모두 통과: | 시나리오 | 결과 | |---|---| | 한국어 query → 한국어 corpus (Rust ownership 핵심 규칙) | grounded `[#2]` rust/ownership.md | | 영어 query → 영어 corpus (RRF fusion) | grounded `[#1]` arch/rag-architecture.md | | 한국어 query → 영어 corpus (e5 prefix) | grounded `[#1]` arch/embeddings.md | | out-of-corpus query (airspeed velocity) | LlmSelfJudge refusal "근거가 부족하다" | 워크스페이스 319 passed, clippy clean. ## 후속으로 발견된 별도 버그 (이 PR 밖) `config.rag.score_gate` default `0.05`이 hybrid mode RRF 점수 범위와 incompatible. RRF top은 `(0, 2/k_rrf]` 즉 default `k_rrf=60`에서 ≤ `0.033` bounded이라 default 0.05이면 모든 hybrid query에서 ScoreGate refusal. 워크어라운드는 사용자 config에서 0.005로 낮추는 것. 장기 수정은 (a) per-mode gate config 또는 (b) RRF 점수를 [0, 1]로 정규화. 별도 task로 분리. ## 변경 파일 - `crates/kb-cli/src/main.rs` (2줄: Config::load + ask_with_config)
altair823 added 1 commit 2026-05-01 15:57:24 +00:00
The earlier P3-5 hotfix wired --config through ingest / search / list /
inspect / doctor by switching kb-cli to call the *_with_config
companions. P4-3 added the ask body but kb-cli's Cmd::Ask arm still
called bare kb_app::ask(query, opts) — same bug as before, ask
silently fell back to ~/.config/kb/config.toml regardless of what the
user passed.

Caught during the post-P4-3 smoke against /tmp/kb-smoke/ pointed at
192.168.0.47 Ollama with gemma4:26b: the answer's wire JSON reported
`model.id = "qwen2.5:14b-instruct"` (the user's XDG default) instead
of `gemma4:26b` from the explicit --config, plus the score_gate /
data_dir / model fields all reflected XDG defaults. After this fix
the same invocation correctly returns model.id=gemma4:26b,
embedding=multilingual-e5-small (from the smoke config), grounded=true
with `[#2]` citation pointing at rust/ownership.md.

Same minimal pattern as the P3-5 hotfix:
- Build the Config once via Config::load(cli.config.as_deref()).
- Call kb_app::ask_with_config(cfg, query, opts) instead of
  kb_app::ask(query, opts).

Workspace 319 tests pass; cargo clippy --workspace --all-targets --
-D warnings clean.

Smoke verified across four scenarios:
- Korean→Korean-body lookup: grounded with rust/ownership.md citation.
- English→Korean-body cross-language: grounded with arch/rag-
  architecture.md citation.
- Korean→English-body cross-language: grounded with arch/embeddings.md
  citation.
- Out-of-corpus query: LlmSelfJudge refusal with "근거가 부족하다."

Out of scope (filed for follow-up):
- config.rag.score_gate default 0.05 is incompatible with hybrid
  RRF scores. RRF top score is bounded by 2/k_rrf (≈0.033 at k_rrf
  =60), so the spec default trips ScoreGate on every hybrid query.
  Workaround: lower the gate to 0.005 in the user's config; long-
  term fix needs either per-mode gate config or RRF score
  normalization to [0,1]. Tracked separately.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
claude-reviewer-01 reviewed 2026-05-01 15:57:42 +00:00
claude-reviewer-01 left a comment
Member

P4-3 follow-up 핫픽스 코드 리뷰 — 셀프 머지 게이트로 인해 COMMENT only.

P3-5에서 닫았던 "--config 무시" 버그가 P4-3의 ask path에서 silent하게 재현되고 있었습니다. 한 줄 수정으로 같은 패턴 적용. 직접 워크스페이스 (192.168.0.47 Ollama + gemma4:26b)에서 4개 시나리오 모두 검증:

  • 한국어 query → 한국어 corpus: grounded [#2]
  • 영어 query → 영어 corpus: grounded [#1]
  • 한→영 cross-language: grounded [#1]
  • out-of-corpus: LlmSelfJudge refusal

워크스페이스 319 tests pass, clippy clean.

후속으로 별도 버그 발견: config.rag.score_gate default가 hybrid RRF 점수 범위와 incompatible (0.05 vs ≤0.033). 워크어라운드 = 사용자 config에서 0.005로 낮추기. 별도 task로 분리.

머지 진행해도 됩니다.

P4-3 follow-up 핫픽스 코드 리뷰 — 셀프 머지 게이트로 인해 COMMENT only. P3-5에서 닫았던 \"--config 무시\" 버그가 P4-3의 ask path에서 silent하게 재현되고 있었습니다. 한 줄 수정으로 같은 패턴 적용. 직접 워크스페이스 (192.168.0.47 Ollama + gemma4:26b)에서 4개 시나리오 모두 검증: - 한국어 query → 한국어 corpus: grounded `[#2]` - 영어 query → 영어 corpus: grounded `[#1]` - 한→영 cross-language: grounded `[#1]` - out-of-corpus: LlmSelfJudge refusal 워크스페이스 319 tests pass, clippy clean. 후속으로 별도 버그 발견: `config.rag.score_gate` default가 hybrid RRF 점수 범위와 incompatible (0.05 vs ≤0.033). 워크어라운드 = 사용자 config에서 0.005로 낮추기. 별도 task로 분리. 머지 진행해도 됩니다.

후속으로 발견된 별도 버그: config.rag.score_gate default 0.05이 hybrid mode RRF 점수 범위 (0, 2/k_rrf ≈ 0.033]과 incompatible — 모든 hybrid query에서 ScoreGate refusal. 본 PR 범위 밖이지만 별도 task로 분리해야 함. 장기 fix는 per-mode gate 또는 RRF [0,1] 정규화.

후속으로 발견된 별도 버그: `config.rag.score_gate` default 0.05이 hybrid mode RRF 점수 범위 (0, 2/k_rrf ≈ 0.033]과 incompatible — 모든 hybrid query에서 ScoreGate refusal. 본 PR 범위 밖이지만 별도 task로 분리해야 함. 장기 fix는 per-mode gate 또는 RRF [0,1] 정규화.
@@ -320,6 +320,7 @@ fn run(cli: &Cli) -> anyhow::Result<()> {
temperature,
seed,
} => {
let cfg = kb_config::Config::load(cli.config.as_deref())?;

P3-5 hotfix와 동일 패턴 — Config::load(cli.config.as_deref())로 한 번 빌드 후 *_with_config 변형에 thread. P4-3 시점에 ask body가 추가되었지만 CLI가 여전히 kb_app::ask(no-config) 직접 호출하던 회귀를 닫음. 한 줄 수정인데 사용자 입장에서는 "왜 --config가 무시되지?" 디버깅 비용이 컸던 케이스.

P3-5 hotfix와 동일 패턴 — Config::load(cli.config.as_deref())로 한 번 빌드 후 *_with_config 변형에 thread. P4-3 시점에 ask body가 추가되었지만 CLI가 여전히 `kb_app::ask`(no-config) 직접 호출하던 회귀를 닫음. 한 줄 수정인데 사용자 입장에서는 "왜 --config가 무시되지?" 디버깅 비용이 컸던 케이스.
altair823 merged commit d7f3d38d48 into main 2026-05-01 16:08:25 +00:00
altair823 deleted branch fix/cli-ask-honor-config-flag 2026-05-01 16:08:26 +00:00
Sign in to join this conversation.
No Reviewers
No Label
2 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: altair823-org/kebab#24