refactor: 제거 기능 stale surface 정리 — 세션/wire 필드/rag_multi_turn/crate수 (PR #214 회차 1)

cut 으로 제거된 기능이 diff 밖 contract surface 에 남긴 잔재 정리:
- integrations/claude-code/kebab/SKILL.md: 제거된 --session/session_id 안내,
  answer.v1 의 conversation_id/turn_index 필드 목록, rag-v2 기본 pin, kebab tui
  언급 제거. 기본 템플릿 rag-v4(대체 rag-v3)로 갱신.
- docs/mcp-usage.md: ask 입력 스키마의 session_id, 'Session 관리(multi-turn)'
  섹션 전체(chat_sessions/chat_turns 포함), 예시의 conversation_id/turn_index 제거.
- docs/wire-schema/v1/answer.schema.json + answer_event.schema.json: Answer 가
  더는 방출 않는 conversation_id/turn_index 속성 제거(producer/doc 괴리 해소).
- schema.rs + wire.rs: capabilities.rag_multi_turn true→false(search_cache 와
  동일 처리; frozen v1 맵이라 키는 유지, 값만 flip).
- CLAUDE.md: 24→22 crates(tui+candle crate 제거 반영).
This commit is contained in:
2026-06-24 15:53:50 +00:00
parent 0ae739eeaf
commit cebfc893ad
7 changed files with 12 additions and 89 deletions

View File

@@ -4,7 +4,7 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co
## Project
Single-user local-first knowledge base + RAG. Rust 2024 workspace, 24 crates, single binary (`kebab`). All inference is local (Ollama + fastembed + whisper.cpp).
Single-user local-first knowledge base + RAG. Rust 2024 workspace, 22 crates, single binary (`kebab`). All inference is local (Ollama + fastembed + whisper.cpp).
The repo's documentation is split by audience — don't duplicate across them:

View File

@@ -151,7 +151,7 @@ fn capabilities_snapshot() -> Capabilities {
json_mode: true,
ingest_progress: true,
ingest_cancellation: true,
rag_multi_turn: true,
rag_multi_turn: false,
search_cache: false,
incremental_ingest: true,
streaming_ask: true,

View File

@@ -337,7 +337,7 @@ mod tests {
json_mode: true,
ingest_progress: true,
ingest_cancellation: true,
rag_multi_turn: true,
rag_multi_turn: false,
search_cache: false,
incremental_ingest: true,
streaming_ask: false,

View File

@@ -176,7 +176,7 @@ stdio JSON-RPC MCP 표준을 따르는 모든 host 가 지원. 위 형식 (`comm
| | |
|---|---|
| Input | `{ "query": string, "session_id"?: string, "mode"?: "lexical"\|"vector"\|"hybrid" }` |
| Input | `{ "query": string, "mode"?: "lexical"\|"vector"\|"hybrid" }` |
| Defaults | `mode = "hybrid"` |
| Output | `answer.v1` (single object) |
@@ -186,8 +186,7 @@ stdio JSON-RPC MCP 표준을 따르는 모든 host 가 지원. 위 형식 (`comm
{
"name": "ask",
"arguments": {
"query": "What's our internal Kubernetes ingress setup?",
"session_id": "ops-onboarding-2026-05"
"query": "What's our internal Kubernetes ingress setup?"
}
}
```
@@ -201,16 +200,12 @@ stdio JSON-RPC MCP 표준을 따르는 모든 host 가 지원. 위 형식 (`comm
"citations": [ ... ],
"grounded": true,
"refusal_reason": null,
"model": { ... },
"conversation_id": "...",
"turn_index": 0
"model": { ... }
}
```
**`grounded: false` 처리**: KB 에 충분한 context 없음. `refusal_reason` 확인 후 사용자에게 \"KB 에 정보 없음\" 으로 안내, 본인 지식 fallback 또는 source 요청. **paraphrase 하면 안 됨** (hallucination 위험).
multi-turn 은 [Session 관리](#session-관리-multi-turn-ask) 참조.
### `schema` — capability discovery
| | |
@@ -233,7 +228,7 @@ multi-turn 은 [Session 관리](#session-관리-multi-turn-ask) 참조.
"wire": { "schemas": ["answer.v1", "search_hit.v1", ...] },
"capabilities": {
"json_mode": true,
"rag_multi_turn": true,
"rag_multi_turn": false,
"mcp_server": true,
"streaming_ask": false,
...
@@ -398,65 +393,6 @@ tool dispatch 가 `Err` 반환 시. content 의 `error.v1` JSON 의 `code` 로
---
## Session 관리 (multi-turn ask)
`ask` tool 의 `session_id` 가 multi-turn RAG context 활성화. 같은 `session_id` 로 연속 호출 시 이전 Q/A history 가 새 query 의 retrieval expansion + prompt context 에 포함.
### session_id 명명
`<topic>-<date>` 형식 권장 — 사용자 친화 + uniqueness:
- `ops-onboarding-2026-05`
- `kubernetes-ingress-debug-2026-05-07`
- `agent-research-session-1` (auto-numbered)
session_id 는 임의 string — kebab 이 처음 보는 id 면 새 session 생성, 기존 id 면 history append.
### 언제 새 session 시작?
- 주제 완전 전환 (KB 의 다른 도메인) — 이전 history 가 noise.
- 사용자 명시 reset 요청.
- Long session (50+ turn) 의 context bloat — 새 session 으로 fresh start.
### Session lifetime
session 데이터는 SQLite `chat_sessions` + `chat_turns` 에 영속. `kebab reset --data-only` 가 모두 wipe. session 별 삭제 명령은 없음 (P+).
### 예시 multi-turn flow
```json
// turn 1
{ "name": "ask", "arguments": {
"query": "What's our internal Kubernetes ingress setup?",
"session_id": "ops-2026-05"
}}
// → answer.v1 with conversation_id, turn_index: 0
// turn 2 — 이전 답변을 context 로 retrieval expansion
{ "name": "ask", "arguments": {
"query": "What about TLS?",
"session_id": "ops-2026-05"
}}
// → kebab 가 "TLS" 만으로 retrieval 안 함, 이전 \"Kubernetes ingress\" history 포함 query 로 검색
// turn 3 — 명시적 reference
{ "name": "ask", "arguments": {
"query": "How does that compare to AWS ALB?",
"session_id": "ops-2026-05"
}}
```
### Session vs single-shot
`session_id` 없이 `ask` 호출 = single-shot. agent host 자체가 conversation 추적하면 single-shot + agent-side context 도 OK. session 이 필요한 경우:
- KB 가 \"이전 질문\" 을 retrieval expansion 에 사용해야 정확 (e.g. follow-up 의 대명사).
- 한 session 안에서 같은 chunk 반복 fetch 회피 (kebab 가 turn 간 chunk overlap 인지).
agent host 가 conversation 추적 + 충분한 context 보유면 session 불필요.
---
## Performance
- **첫 tool call**: cold start ~1-2s (SQLite open + Lance dataset open + fastembed model load).

View File

@@ -45,15 +45,6 @@
"retrieval": { "type": "object" },
"usage": { "type": "object" },
"created_at": { "type": "string", "format": "date-time" },
"conversation_id": {
"type": ["string", "null"],
"description": "p9-fb-15: same conversation 의 turn 들이 공유. CLI single-shot / TUI 첫 turn 은 null."
},
"turn_index": {
"type": ["integer", "null"],
"minimum": 0,
"description": "p9-fb-15: 같은 conversation 안 0-based 순서. null 이면 single-shot."
},
"hops": {
"anyOf": [
{

View File

@@ -11,7 +11,6 @@
"ts": { "type": "string", "format": "date-time" },
"hits": { "type": "array", "description": "retrieval_done: search_hit.v1[]" },
"delta": { "type": "string", "description": "token: incremental string chunk" },
"turn_index": { "type": ["integer", "null"], "minimum": 0, "description": "token: matches Answer.turn_index" },
"answer": { "type": "object", "description": "final: complete answer.v1 payload" }
}
}

View File

@@ -80,13 +80,12 @@ Use when the user wants a synthesized answer, not a list of links.
Input:
```json
{ "query": "<question>", "session_id": "<optional-slug>", "mode": "hybrid", "multi_hop": false }
{ "query": "<question>", "mode": "hybrid", "multi_hop": false }
```
- Returns `answer.v1`: `answer` (markdown), `citations[]`, `grounded` (bool), `refusal_reason`, `model`, `conversation_id`, `turn_index`, `hops` (multi-hop only).
- Returns `answer.v1`: `answer` (markdown), `citations[]`, `grounded` (bool), `refusal_reason`, `model`, `hops` (multi-hop only).
- **If `grounded == false`** → KB doesn't have enough context. Don't paraphrase the refusal as if it were an answer. Tell the user the KB came up dry and fall back to your own knowledge or ask for the source.
- For follow-up turns on the same topic, pass `session_id` (e.g. `"team-onboarding-2026-05"`) and reuse it across the conversation. Sessions persist until `kebab reset --data-only`.
- p9-fb-40: 기본 `prompt_template_version = "rag-v2"`. 답변이 더 strict — fact 인용 시 verbatim span, 학습 지식 동원 금지, 근거 모호 시 "확실하지 않다" 출현 가능. user 가 `[rag] prompt_template_version = "rag-v1"` 명시 시 legacy 동작.
- p9-fb-40: 기본 `prompt_template_version = "rag-v4"`. 답변이 strict — fact 인용 시 verbatim span, 학습 지식 동원 금지, 근거 모호 시 "확실하지 않다" 출현 가능. `[rag] prompt_template_version = "rag-v3"` 로 이전 템플릿 선택 가능.
- **p9-fb-41 `multi_hop: true`** — opt the ask into the multi-hop pipeline. The query is decomposed into sub-questions, each retrieved independently (LLM-driven decide loop, up to `rag.multi_hop_max_depth` iters), then synthesized over the merged chunk pool. Cost trade-off: 25× LLM calls vs. single-pass. **Use** for compound questions ("X 와 Y 의 차이는?", prereq chains, cross-doc reasoning where one chunk alone is insufficient). **Don't** for simple fact-finding (single-pass is faster + cheaper). When set, `answer.v1.hops[]` carries the per-hop trace (`{iter, kind, sub_queries[], context_chunks_added, forced_stop, llm_call_ms}`) — surface a brief "Searched in N hops" note when the trace is non-trivial. Decompose-failure (model emitted non-JSON) → `refusal_reason = "multi_hop_decompose_failed"`; treat like any other refusal.
- **v0.18+ multi-hop NLI verification** — multi-hop ask (`mcp__kebab__ask` with `multi_hop: true`) runs a post-synthesize NLI groundedness gate when `[rag] nli_threshold > 0` is set in the user's config. `answer.v1.verification.nli_passed == true` means the generated answer is entailed by the retrieved chunks (grounded); `false` means the answer is refused with `refusal_reason = "nli_verification_failed"` and the `verification` block still ships so the agent can show what entailment score was rejected. Threshold tuning: 0.5 is the production default, 0.9 is strict mode. If the NLI model download / inference fails the pipeline emits `refusal_reason = "nli_model_unavailable"` — user-side workaround is `[rag] nli_threshold = 0` then retry multi-hop. Single-pass `ask` (multi_hop: false / unset) is unaffected — it keeps the LLM self-judge gate as the only verification.
@@ -113,7 +112,6 @@ If MCP tools aren't in scope (host without MCP support, or `mcp.json` not config
```bash
kebab search "<query>" --mode hybrid --json 2>/dev/null
kebab ask "<question>" --json 2>/dev/null
kebab ask "<question>" --session <stable-id> --json 2>/dev/null
kebab ask "<question>" --stream # ndjson answer_event.v1 on stderr, final answer.v1 on stdout
```
@@ -151,9 +149,9 @@ Claude Code spawns `kebab mcp` at session start; the process stays alive across
## Capability discovery
Before using streaming or multi-turn features, probe what this binary supports — call `mcp__kebab__schema` (or CLI `kebab schema --json`):
Before using streaming features, probe what this binary supports — call `mcp__kebab__schema` (or CLI `kebab schema --json`):
Returns `schema.v1`: `wire.schemas` (supported wire ids), `capabilities` (bool flags — e.g. `streaming_ask`, `rag_multi_turn`), `models` (version cascade 6-axis + v0.20.1 `active_parsers` / `active_chunkers` arrays for multi-version corpora), `stats` (doc/chunk/asset count + last_ingest_at, plus p9-fb-37 health surface: `media_breakdown` per-kind doc counts (5 zero-padded keys: markdown / pdf / image / audio / other), `lang_breakdown` per BCP-47 lang (NULL keyed as the literal string `"null"`), `index_bytes.{sqlite,lancedb}` on-disk byte sums, `stale_doc_count` for docs older than `config.search.stale_threshold_days`). Gate streaming / session flows on `capabilities.streaming_ask` / `capabilities.rag_multi_turn` being `true`. Cheap call (no LLM), once per session.
Returns `schema.v1`: `wire.schemas` (supported wire ids), `capabilities` (bool flags — e.g. `streaming_ask`, `rag_multi_turn`), `models` (version cascade 6-axis + v0.20.1 `active_parsers` / `active_chunkers` arrays for multi-version corpora), `stats` (doc/chunk/asset count + last_ingest_at, plus p9-fb-37 health surface: `media_breakdown` per-kind doc counts (5 zero-padded keys: markdown / pdf / image / audio / other), `lang_breakdown` per BCP-47 lang (NULL keyed as the literal string `"null"`), `index_bytes.{sqlite,lancedb}` on-disk byte sums, `stale_doc_count` for docs older than `config.search.stale_threshold_days`). Gate streaming flows on `capabilities.streaming_ask` being `true`. Cheap call (no LLM), once per session.
## Quick health check
@@ -198,4 +196,3 @@ For files already on disk the user references, prefer `mcp__kebab__ingest_file`
- Don't auto-invoke `mcp__kebab__ingest_file` / `mcp__kebab__ingest_stdin` / `kebab ingest` / `kebab reset` / `kebab init`. Those mutate state — the user must explicitly request.
- Don't pass user-supplied raw text into the query without trimming — long queries (> a few hundred chars) waste embedding budget. Extract the question.
- Don't fabricate `doc_path`s. If you didn't see a doc in `search` / `ask` output, it's not in the KB.
- Don't use `kebab tui` from a skill — it's interactive only.