feat(chunk): md-heading-v2 — 예산 초과 청크 일반 분할 #209

Merged
altair823 merged 1 commits from feat/md-heading-v2-oversize-split into main 2026-06-24 01:26:53 +00:00
Owner

요약

markdown 청커 md-heading-v2 신규 변종 추가 + 기본값 승격. v1 의 "블록 미분할" 한계를 일반화해, 거대 list/code/table/paragraph 가 한 청크로 임베더 컨텍스트를 초과해 임베딩이 통째로 실패하던 문제를 해소한다. v2 는 v1 과 모든 출력이 동일하되, 마지막에 청크의 실제 임베드 text 크기(text.len()/3)가 max_chunk_tokens(신규 config, byte/3, default 4000)를 넘는 청크만 줄(\n) 경계로 — 단일 거대 줄은 UTF-8 char 경계로 — 잘라 각 조각이 예산 이하가 되게 한다.

동기: strict 임베더(AMD Lemonade /api/embed)는 oversize 입력을 truncate 가 아니라 거부(500 too large)한다 — ollama 가 조용히 truncate 하던 걸 청커가 애초에 안 만들도록.

설계: docs/superpowers/plans/2026-06-24-md-heading-v2-oversize-split.md

변경

  • kebab-chunk: 신규 MdHeadingV2Chunker(md-heading-v2). v1-동등 청킹 후 generic oversize post-pass. 분할 조각 chunk_id 는 동일 block_ids 라 #seg{i} 접미사로 충돌 회피(저장 policy_hash 는 bare). max_chunk_tokens 는 v2 policy_hash 에만 fold(공유 ChunkPolicy 미변경).
  • 분할 판정 = 실제 text 크기(저장 token_estimate 아님): ImageRef/AudioRef 청크는 image-only 규약으로 token_estimate=0 이지만 OCR/caption text 는 클 수 있음 — 일치 재테스트에서 발견·수정.
  • kebab-config: [ingest.chunking] max_chunk_tokens(default 4000, serde default) + 기본 chunker_version v1→v2 + env override KEBAB_CHUNKING_MAX_CHUNK_TOKENS + migrate 주석.
  • kebab-app: 마크다운/이미지 dispatch site 를 v2 로 + max_chunk_tokensingest_config_signature 에 fold(값 변경 시 영향 자산 자동 재청크).
  • docs cascade: HOTFIXES / release-notes v0.30.0 draft / plan(2026-06-24) / normalize-chunk README / README / SMOKE / HANDOFF.
  • Cargo.toml 0.29.0 → 0.30.0 (신규 config 키 + 청커 동작 변경 = pre-1.0 minor + 도그푸딩 트리거).

검증

  • kebab-chunk / kebab-config / kebab-app 전체 테스트 green (40 test 바이너리, 0 failed), clippy -D warnings 0.
  • 단위 테스트: oversize_list_block_splits, oversize_paragraph_single_line_char_splits(다국어 UTF-8 경계), oversize_code_block_still_splits, oversize_image_ocr_chunk_splits(token_estimate=0 이미지 OCR), non_oversize_identical_to_v1(v1 parity), split_pieces_unique_deterministic_ids(1000-iter 결정성), budget_in_policy_hash.
  • 도그푸딩(실험 KB, arctic-embed-l-v2 @ Lemonade): v2 전 620 중 2 doc(거대 jira list 블록) 임베드 실패 → v2 후 620/620 errors=0, 7114 청크 전부 ≤ 4000. "WiredTiger excessive memory cache size" 질의에 거대 doc SERVER-22906 가 1위(0.977) — "임베드 불가" 에서 "최상위 검색 결과" 로.
  • 사용자 실 config 일치 재테스트(이미지 OCR + PDF OCR paddle-onnx ON): 624 자산 errors=0, image-OCR 구멍 발견·수정. budget=2000 데모에서 dense 이미지 OCR(token_estimate=0)이 1→2 청크로 분할, 전 코퍼스 초과 0.

비범위

  • PDF 는 별도 청커 pdf-page-v1.1 — 이 oversize-split 미적용(후속 후보). 초고밀도 scanned page 한 장이 budget 초과 시 잔존.
  • 분할 조각 citation 은 블록 단위(sub-line 정밀 아님) — fenced span 이 fence 줄을 포함하는 비대칭 때문.

시험 항목 (Test Plan)

  • 업그레이드 후 kebab ingest 에서 markdown 1회 자동 재청크(코드/PDF 무영향)
  • 거대 블록 포함 문서가 검색에서 누락 없이 잡힘
  • max_chunk_tokens 변경 시 markdown 자산 재청크
## 요약 markdown 청커 `md-heading-v2` 신규 변종 추가 + 기본값 승격. v1 의 "블록 미분할" 한계를 일반화해, 거대 `list`/`code`/`table`/`paragraph` 가 한 청크로 임베더 컨텍스트를 초과해 임베딩이 통째로 실패하던 문제를 해소한다. v2 는 v1 과 모든 출력이 동일하되, 마지막에 청크의 **실제 임베드 text 크기**(`text.len()/3`)가 `max_chunk_tokens`(신규 config, byte/3, default 4000)를 넘는 청크만 줄(`\n`) 경계로 — 단일 거대 줄은 UTF-8 char 경계로 — 잘라 각 조각이 예산 이하가 되게 한다. 동기: strict 임베더(AMD Lemonade `/api/embed`)는 oversize 입력을 truncate 가 아니라 거부(`500 too large`)한다 — ollama 가 조용히 truncate 하던 걸 청커가 애초에 안 만들도록. 설계: docs/superpowers/plans/2026-06-24-md-heading-v2-oversize-split.md ## 변경 - `kebab-chunk`: 신규 `MdHeadingV2Chunker`(`md-heading-v2`). v1-동등 청킹 후 generic oversize post-pass. 분할 조각 chunk_id 는 동일 block_ids 라 `#seg{i}` 접미사로 충돌 회피(저장 policy_hash 는 bare). `max_chunk_tokens` 는 v2 policy_hash 에만 fold(공유 ChunkPolicy 미변경). - 분할 판정 = 실제 text 크기(저장 token_estimate 아님): ImageRef/AudioRef 청크는 image-only 규약으로 token_estimate=0 이지만 OCR/caption text 는 클 수 있음 — 일치 재테스트에서 발견·수정. - `kebab-config`: `[ingest.chunking] max_chunk_tokens`(default 4000, serde default) + 기본 chunker_version v1→v2 + env override `KEBAB_CHUNKING_MAX_CHUNK_TOKENS` + migrate 주석. - `kebab-app`: 마크다운/이미지 dispatch site 를 v2 로 + `max_chunk_tokens` 를 `ingest_config_signature` 에 fold(값 변경 시 영향 자산 자동 재청크). - docs cascade: HOTFIXES / release-notes v0.30.0 draft / plan(2026-06-24) / normalize-chunk README / README / SMOKE / HANDOFF. - `Cargo.toml` 0.29.0 → 0.30.0 (신규 config 키 + 청커 동작 변경 = pre-1.0 minor + 도그푸딩 트리거). ## 검증 - kebab-chunk / kebab-config / kebab-app 전체 테스트 green (40 test 바이너리, 0 failed), clippy `-D warnings` 0. - 단위 테스트: `oversize_list_block_splits`, `oversize_paragraph_single_line_char_splits`(다국어 UTF-8 경계), `oversize_code_block_still_splits`, `oversize_image_ocr_chunk_splits`(token_estimate=0 이미지 OCR), `non_oversize_identical_to_v1`(v1 parity), `split_pieces_unique_deterministic_ids`(1000-iter 결정성), `budget_in_policy_hash`. - 도그푸딩(실험 KB, arctic-embed-l-v2 @ Lemonade): v2 전 620 중 2 doc(거대 jira list 블록) 임베드 실패 → v2 후 620/620 errors=0, 7114 청크 전부 ≤ 4000. "WiredTiger excessive memory cache size" 질의에 거대 doc SERVER-22906 가 1위(0.977) — "임베드 불가" 에서 "최상위 검색 결과" 로. - 사용자 실 config 일치 재테스트(이미지 OCR + PDF OCR paddle-onnx ON): 624 자산 errors=0, image-OCR 구멍 발견·수정. budget=2000 데모에서 dense 이미지 OCR(token_estimate=0)이 1→2 청크로 분할, 전 코퍼스 초과 0. ## 비범위 - PDF 는 별도 청커 `pdf-page-v1.1` — 이 oversize-split 미적용(후속 후보). 초고밀도 scanned page 한 장이 budget 초과 시 잔존. - 분할 조각 citation 은 블록 단위(sub-line 정밀 아님) — fenced span 이 fence 줄을 포함하는 비대칭 때문. ## 시험 항목 (Test Plan) - [ ] 업그레이드 후 `kebab ingest` 에서 markdown 1회 자동 재청크(코드/PDF 무영향) - [ ] 거대 블록 포함 문서가 검색에서 누락 없이 잡힘 - [ ] `max_chunk_tokens` 변경 시 markdown 자산 재청크
altair823 added 1 commit 2026-06-24 01:16:06 +00:00
v1 의 "블록 미분할" 한계를 일반화. 거대 list/code/table/paragraph 가 한 청크로
임베더 컨텍스트를 초과해 임베딩이 통째로 실패하던 문제를 해소한다. v2 는 v1 과
모든 출력이 동일하되, 청크의 실제 임베드 text 크기(`text.len()/3`)가
`max_chunk_tokens`(신규 config, byte/3, default 4000)를 넘는 청크만 줄(`\n`)
경계로, 단일 거대 줄은 UTF-8 char 경계로 잘라 각 조각이 예산 이하가 되게 한다.

- 판정 기준은 저장 `token_estimate` 가 아니라 실제 `text` 길이: ImageRef/AudioRef
  청크는 image-only 규약으로 token_estimate=0 이지만 OCR/caption text 는 클 수
  있다(빽빽한 스크린샷). 도그푸딩 일치 재테스트에서 이 image-OCR 구멍 발견·수정.
- 분할 조각 chunk_id 는 동일 block_ids 를 공유하므로 id-input 해시에 `#seg{i}`
  접미사로 충돌 회피(저장 policy_hash 는 bare — pdf-page-v1 의 `#L` 레시피 동형).
- `max_chunk_tokens` 는 v2 의 policy_hash 에만 fold(공유 ChunkPolicy 미변경 →
  코드/PDF 청커 cascade 무영향). 값 변경 시 markdown 만 재청크.
- 미분할 청크는 v1 과 byte-identical. `chunker_version` v1→v2 → 다음 plain
  `kebab ingest` 에서 markdown 1회 자동 재청크(--force 불필요, 코드/PDF 무영향).

동기: strict 임베더(AMD Lemonade `/api/embed`)는 oversize 입력을 truncate 아닌
거부(`500 too large`) — ollama 가 조용히 truncate 하던 걸 청커가 애초에 안 만들게.

검증(실험 KB, arctic-embed-l-v2 @ Lemonade): v2 전 620 중 2 doc(거대 jira list
블록) 임베드 실패 → v2 후 620/620 errors=0, 7114 청크 전부 ≤ 4000. 사용자 실
config 일치 재테스트(이미지 OCR + PDF OCR paddle-onnx ON)에서 image-OCR 구멍
발견·수정 후 dense 이미지 OCR 텍스트가 budget 초과 시 분할(token_estimate=0 →
1청크였던 것이 실제 text 기준 다중 청크로) 실증. frozen 설계 doc / frozen p1-5
spec 미변경(설계 §9 가 md-heading-v2 라벨 bump 를 변경 메커니즘으로 명시).

known limitation: PDF 는 별도 청커 pdf-page-v1.1 이라 이 split 미적용(후속 후보).

Cargo.toml 0.29.0 → 0.30.0 (신규 config 키 + 청커 동작 변경 = pre-1.0 minor +
도그푸딩 트리거). docs cascade: HOTFIXES / release-notes-v0.30.0-draft /
plan(2026-06-24) / normalize-chunk README / README / SMOKE / HANDOFF.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012Mc6W1fgsrbFKTsqA6P8La
claude-reviewer-01 approved these changes 2026-06-24 01:22:19 +00:00
claude-reviewer-01 left a comment
Member

회차 1 — 최종 generic 버전 + 이미지-OCR 수정 정적 리뷰. 7개 핵심 correctness 위험(budget hard-bound / 분할 조각 chunk_id #seg 고유·1000-iter 결정성 / under-budget v1 parity / policy_hash fold 의 v2 격리 / dispatch swap 완전성·PDF 미변경 / config·signature·migration / 분할 판정이 token_estimate 가 아닌 실제 text.len 기준) 전부 line 증거로 검증 통과. Critical/High/Medium 0.

특기:

  • Open Question(다운스트림이 token_estimate==0 을 이미지 sentinel 로 쓰나) 독립 확인 결과 clean — 소비처에 해당 분기 0건, 유일 read 는 kebab-store-sqlite/src/documents.rs:150 의 SQLite 저장뿐(판별 미사용). 분할 이미지 조각의 non-zero token_estimate 무해.
  • LOW(max_chunk_tokens 최소값 검증 없음): 기존 target_tokens/overlap_tokens 도 동일하게 미검증이라 선례 일치 + 내부 budget.max(1) 클램프로 hang/divide 방어됨 → 이 PR 에선 의도적 비변경. 후속 후보: 청킹 budget 3종 공통 floor 검증.
  • PDF(pdf-page-v1.1) oversize 미적용은 known limitation 으로 HOTFIXES/release-notes 에 명시됨(별도 후속).

chunk_v1_equivalent 가 실제 MdHeadingV1Chunker 와 cross-check(non_oversize_identical_to_v1)되고, 도그푸딩에서 발견된 image-OCR hole 이 회귀 테스트(oversize_image_ocr_chunk_splits)로 고정된 점이 특히 견고함. 머지 동의.

회차 1 — 최종 generic 버전 + 이미지-OCR 수정 정적 리뷰. 7개 핵심 correctness 위험(budget hard-bound / 분할 조각 chunk_id `#seg` 고유·1000-iter 결정성 / under-budget v1 parity / policy_hash fold 의 v2 격리 / dispatch swap 완전성·PDF 미변경 / config·signature·migration / 분할 판정이 token_estimate 가 아닌 실제 text.len 기준) 전부 line 증거로 검증 통과. Critical/High/Medium 0. 특기: - Open Question(다운스트림이 `token_estimate==0` 을 이미지 sentinel 로 쓰나) 독립 확인 결과 clean — 소비처에 해당 분기 0건, 유일 read 는 `kebab-store-sqlite/src/documents.rs:150` 의 SQLite 저장뿐(판별 미사용). 분할 이미지 조각의 non-zero token_estimate 무해. - LOW(`max_chunk_tokens` 최소값 검증 없음): 기존 `target_tokens`/`overlap_tokens` 도 동일하게 미검증이라 선례 일치 + 내부 `budget.max(1)` 클램프로 hang/divide 방어됨 → 이 PR 에선 의도적 비변경. 후속 후보: 청킹 budget 3종 공통 floor 검증. - PDF(`pdf-page-v1.1`) oversize 미적용은 known limitation 으로 HOTFIXES/release-notes 에 명시됨(별도 후속). `chunk_v1_equivalent` 가 실제 `MdHeadingV1Chunker` 와 cross-check(`non_oversize_identical_to_v1`)되고, 도그푸딩에서 발견된 image-OCR hole 이 회귀 테스트(`oversize_image_ocr_chunk_splits`)로 고정된 점이 특히 견고함. 머지 동의.
altair823 merged commit 565ee9ef35 into main 2026-06-24 01:26:53 +00:00
altair823 deleted branch feat/md-heading-v2-oversize-split 2026-06-24 01:26:55 +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#209