feat(search): heading_path FTS5 text column filter (default text-only matching)
v0.17.0 trigram tokenizer entry 가 미수정으로 남겨둔 heading_path_json JSON 노이즈 (HOTFIXES 2026-05-24) closure. trigram 이 chunks_fts.heading_path 컬럼 (V002/V007 트리거가 chunks.heading_path_json 그대로 INSERT) 의 JSON 표기 + 안의 path 세그먼트 (app, src) 까지 3-gram 색인해서 query 가 우연히 false positive hit 하는 문제. column filter 채택 — heading 색인 유지 (V007 verbatim 불변), 매칭 대상만 text 컬럼 한정. - build_match_string 가 non-raw 분기에서 combined expression 을 `text : (<expr>)` 로 wrap. FTS5 column filter syntax 가 OR/AND sub-expression 허용. - Raw mode (`'...'`) 는 그대로 — 사용자가 명시 의도로 `'heading_path : agent'` 같은 explicit opt-in 가능 (escape hatch). - 8 기존 build_match_string unit test expected string 갱신 + `build_match_string_raw_mode_preserves_heading_filter` 신규. - `lexical_heading_only_token_does_not_hit_default_mode` 신규 회귀 핀 (heading-only unique token 이 default mode 에서 0 hit). - `lexical_raw_mode_can_opt_into_heading_path_filter` 신규 — 같은 fixture 가 raw mode 로 hit 확인 (escape hatch 동작 핀). 사용자 영향: lexical / hybrid 검색의 본문 precision ↑. recall 변화 없음 (text 본문 token 매칭은 동일). re-ingest 불필요 (FTS query 시점 매칭만 변경). lexical_snapshot_run_1 + hybrid_snapshot 도 fixture regenerate 불필요 (text 본문 매칭 query 라 BM25 동일). HOTFIXES: 2026-05-24 v0.17.0 entry 의 `heading_path_json` 노이즈 항목 closure 표기 + 새 2026-05-25 post-v0.17.1 dogfood entry 추가. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -49,6 +49,29 @@ v0.17.1 entry 의 첫 번째 미진행 항목 closure. LLM 쪽이 v0.17.1 에서
|
||||
|
||||
Cross-link: `crates/kebab-config/src/lib.rs::OcrCfg::request_timeout_secs`, `crates/kebab-parse-image/src/ocr.rs::OllamaVisionOcr::build`.
|
||||
|
||||
## 2026-05-25 — post-v0.17.1 dogfood: `heading_path` FTS5 column filter (text-only matching, closure of 2026-05-24 `heading_path_json` 노이즈)
|
||||
|
||||
v0.17.0 의 한국어 trigram tokenizer 채택 entry (2026-05-24 위) 가 미수정으로 남겨둔 `heading_path_json` JSON 노이즈 closure. trigram 이 `chunks_fts.heading_path` 컬럼 (V002/V007 트리거가 `chunks.heading_path_json` 을 그대로 INSERT) 의 JSON 표기 (`[`, `"`, `,`) + 안의 path 세그먼트 (`app`, `src`) 까지 3-gram 색인해서 query 가 우연히 false positive hit 하는 문제. 사용자 결정 (column filter vs 평문 heading 변환): **column filter** — `heading_path` 색인은 V007 verbatim 그대로 유지, 매칭 대상만 `text` 컬럼으로 한정. V008 migration / design §5.5 verbatim 블록 변경 불필요.
|
||||
|
||||
**변경**:
|
||||
- `crates/kebab-search/src/lexical.rs::build_match_string` 가 non-raw 분기에서 combined expression 을 `text : (<expr>)` 로 wrap. FTS5 column filter syntax (`column:expr`) 가 OR/AND sub-expression 허용 — 한국어 trigram 빌더의 `(whole) OR (token_and)` 형태가 그대로 들어감.
|
||||
- Raw mode (`'...'`) 는 변경 없음 — 사용자가 명시 의도로 `'heading_path : agent'` 같은 explicit column filter opt-in 가능 (escape hatch).
|
||||
- 9 신규 / 갱신 unit test:
|
||||
- `build_match_string_*` 8 expected string 갱신 (column filter prefix 추가)
|
||||
- `build_match_string_raw_mode_preserves_heading_filter` 신규 — raw mode 가 `heading_path : ...` 보존
|
||||
- `lexical_heading_only_token_does_not_hit_default_mode` 신규 (`crates/kebab-search/tests/lexical.rs`) — heading-only unique token 이 default mode 에서 0 hit
|
||||
- `lexical_raw_mode_can_opt_into_heading_path_filter` 신규 — 같은 fixture 가 raw mode 로 hit 확인
|
||||
|
||||
**사용자 영향**:
|
||||
- 기본 lexical / hybrid 검색에서 heading 만 매칭되던 false positive 차단. 한국어 / 영어 substring 매칭의 recall 은 그대로 (text 본문에 있는 token 은 변함없이 hit). 본문 검색의 precision 가 올라감.
|
||||
- heading 으로 일부러 검색하던 사용자는 `'heading_path : <token>'` 형태로 raw mode 진입. CLI / TUI / MCP 모든 surface 동일.
|
||||
- `kebab.sqlite` 크기 변화 없음 (색인 column 그대로 유지). re-ingest 불필요 (FTS query 시점의 매칭 범위만 변경).
|
||||
- BM25 score 영향: `lexical_snapshot_run_1` + `hybrid_snapshot_run_1` 둘 다 column filter 적용 후에도 점수 동일 (text 본문에만 매칭되던 query 라 column filter 가 점수 분포에 영향 안 줌). fixture regenerate 불필요.
|
||||
|
||||
**MCP / agent 가시성**: `search_response.v1` 의 wire shape 변경 없음. 사용자가 heading 검색을 명시 의도하던 케이스는 raw mode 안내 — `integrations/claude-code/kebab/SKILL.md` 의 search 절은 v0.17.0 의 raw mode 안내 (`'foo OR bar*'`) 가 그대로 적용. 별도 SKILL.md 갱신 불필요 (raw mode 가 이미 documented escape hatch).
|
||||
|
||||
Cross-link: `crates/kebab-search/src/lexical.rs::build_match_string`, `migrations/V007__fts_trigram.sql` (verbatim 유지), design §5.5 (verbatim 유지, query-time 동작만 변경).
|
||||
|
||||
## 2026-05-24 — v0.17.0: 한국어 trigram FTS5 tokenizer 채택 (closure of 2026-05-22 한국어 lexical)
|
||||
|
||||
V007 migration 으로 `chunks_fts` 의 tokenizer 를 `unicode61` → `trigram` 으로 교체. `chunks` 원본 + embedding + vector index 는 그대로, FTS shadow 만 재구축 + 자동 backfill — 사용자는 `kebab ingest` 재실행 불필요 (binary 만 교체하면 다음 open 시 V007 가 즉시 적용). 같은 라운드의 다른 두 follow-up (`code_lang_chunk_breakdown`, C typedef) 은 별 PR (PR-C / PR-B).
|
||||
@@ -61,7 +84,7 @@ V007 migration 으로 `chunks_fts` 의 tokenizer 를 `unicode61` → `trigram`
|
||||
|
||||
**디스크 용량**: trigram 인덱스는 unicode61 대비 통상 2-10배. V007 자동 backfill 후 `kebab.sqlite` 파일 크기 증가 (도그푸딩 KB 기준 ~2-5배 또는 수백 MB). release notes 명시.
|
||||
|
||||
**`heading_path_json` JSON 노이즈 (관찰, 미수정)**: trigram 이 JSON 표기 (`[`, `"`, `,`) 와 그 안의 단어 (`app`, `src`) 까지 3-gram 색인 → query 가 우연히 JSON 구문 / 흔한 경로 단어와 겹쳐 false positive 가능. v0.17.0 에서는 컬럼 구성 유지, 도그푸딩 후 column filter (`{text} : <q>` 한정) 또는 평문 heading 변환 결정. 후속 도그푸딩 entry 로 등재 예정.
|
||||
**`heading_path_json` JSON 노이즈 (관찰, 미수정)**: trigram 이 JSON 표기 (`[`, `"`, `,`) 와 그 안의 단어 (`app`, `src`) 까지 3-gram 색인 → query 가 우연히 JSON 구문 / 흔한 경로 단어와 겹쳐 false positive 가능. v0.17.0 에서는 컬럼 구성 유지, 도그푸딩 후 column filter (`{text} : <q>` 한정) 또는 평문 heading 변환 결정. 후속 도그푸딩 entry 로 등재 예정. → **closure**: 아래 2026-05-25 v0.17.1 post-dogfood heading text column filter entry 참조 (column filter 방식 채택, V008 migration 불필요).
|
||||
|
||||
**MCP / agent 가시성**: `search_response.v1` 에 `hint: Option<String>` additive 필드. 결과가 비어 있고 query trimmed.chars().count() < 3 + raw mode 아닐 때만 set (helper `kebab_app::short_query_hint`). `integrations/claude-code/kebab/SKILL.md` 의 search 절에 "한국어 lexical 은 3자 이상 권장, `hint` 필드 확인" 안내 추가.
|
||||
|
||||
|
||||
Reference in New Issue
Block a user