ADR 0018 — 親子チャンクのストアレベル展開
small-to-big コンテキストを永続化し、Chunker のシームをインジェスト経路に配線し、RESTRICTED フィルタリングの後にのみ展開を行いつつ、子チャンクの引用の誠実さを保つ。
ステータス: accepted
日付: 2026-07-14
不変の記録。ドリフト追跡の対象外(
coversなし)。編集せず、置き換え(supersede)で対応すること。
本記録は、ADR 0016 で先送りされたフォローアップを完了するものである。すなわち、親子チャンクの挙動を検索時のストア展開へ移行する。本決定はエンジン ADR 0005(新機能をバイト同一のデフォルトから隔離する)およびエンジン ADR 0010(モデル出力に依存しない決定論的隔離)の制約を受ける。
背景
ADR 0016 は ParentChildChunker を追加した。チャンク化の時点で、子チャンクは window_text(親セクションの全文)と parent_locator(そのセクションの実際の段落スパン)を保持していた。しかし Chunker のシームはナラティブのインジェスト経路に配線されておらず、これらのフィールドは永続化されていなかった。そのため、エンドツーエンドの small-to-big——細粒度の子チャンクにヒットし、生成のために親コンテキストを提供する——は、実際の保存経路上では一度も動作していなかった。
本記録はこのギャップを埋める。階層/ウィンドウのフィールドを永続化し、chunker を両方のインジェスト経路に配線し、検索の出口で展開を行う。その際、バイト同一のデフォルト挙動、捏造防止、出典追跡、および RESTRICTED の二重出口による隔離を維持する。
決定
追加のみの変更を 3 つ導入し、いずれもオプトインである。デフォルト経路はバイト同一のままである。
永続化:追加カラムのみで、旧データベースは読み取り可能なまま
narrative_chunk に parent_id、heading、window_text、parent_locator を追加する。いずれも TEXT NOT NULL DEFAULT '' である。ChunkStore.init_schema は追加型のマイグレーションを実行する。すなわち PRAGMA table_info を実行した後、カラムごとに ALTER TABLE ADD COLUMN を行う。既存の行には空のデフォルト値が入り、読み取り可能なまま維持される。破壊的な再構築は行わない。StoredChunk は空のデフォルト値を持つこの 4 フィールドをミラーする。
スキーマバージョンによる再構築は却下された。追加カラムのほうが単純であり、旧ライブラリを稼働状態のまま保てるためである。
インジェストが Chunker のシームを配線する
両方の書き込み側は chunker: Chunker | None を受け取る。デフォルトの None は組み込みの chunk_document に委譲し、バイト同一のままである。
- バッチパイプライン向けの
ingest_narrative(..., chunker=)。 - 検索側のインジェストと埋め込み向けの
NarrativeIndex(chunker=)。
ServiceConfig.chunker は make_chunker を通じて選択を行い、デフォルトは "none" である。ナラティブワーカーのペイロードがこの選択を運ぶ。parent_child/small_to_big を指定すると、子チャンクは window_text/parent_locator を伴って到着し、ストアがそれらを永続化する。
検索は A ライン隔離出口の後に展開する
narrative_link._to_snippet において、空でない window_text は独立した prompt_text キーになる。エージェントの _snippet_text は、生成コンテキストとしてすでにこのキーを優先する。text、source_locator、chunk_id はヒットした子チャンクのものを維持する。parent_locator は、親スパンへの追加的な出典追跡の逆参照である。
展開が変えるのは生成コンテキストのみである。それがヒットになること、順位を変えること、結果を追加することは決してない。デフォルトの chunker は空の値を生成するため、キーは追加されず、スニペットのマッピングはバイト同一のままである。これは、検索後圧縮のために確立された prompt_text のレイヤリングを再利用するものである。
RESTRICTED ウィンドウに関する固定決定
window_text は子チャンクの既存の RESTRICTED 二重出口に相乗りする。子チャンクが RESTRICTED である場合、親ウィンドウを含むスニペット全体が拒否される。
_to_snippet は、link/ 出口が sensitivity == RESTRICTED のチャンクを除去した後にのみ実行される。RESTRICTED な子チャンクがウィンドウ展開に到達することはなく、その window_text が prompt_text になることも決してない。候補となる 2 つのセマンティクス——ウィンドウのみを除去するか、スニペット全体を拒否するか——のうち、我々はスニペット全体の拒否を選択する。親コンテキストは、両方の出口を通過した子チャンクを通じてのみ公開される。
現行のドキュメントレベルの機微度モデルでは、子と親は同一の機微度を共有するため、許可された子チャンクが RESTRICTED な親セクションを持つ状況はインジェストからは生じ得ない。それでもこのメカニズムは防御的に固定しておく。test_parent_child_isolation.py はウィンドウを含む RESTRICTED なチャンクを与え、スニペット全体が漏洩なしに拒否されることを証明する。
検討した代替案(却下)
- スニペットの
textをwindow_textで置き換える:展開されたコンテキストがヒットの証拠を装うことになり、出典追跡の誠実さを壊す。子チャンクのテキスト/ロケータは引用可能なまま残し、ウィンドウは生成専用とする。 - ウィンドウを取り除きつつ RESTRICTED な子スニペットを保持する:部分的な処理はスニペット全体を拒否するより脆弱であり、既存の出口セマンティクスと衝突する。
- RESTRICTED フィルタリングの前に展開する:これは直接的な漏洩経路を開いてしまう。展開は、除去処理の下流にある
_to_snippetの中でのみ厳密に行われる。 - スキーマバージョンを追加して再構築する:追加型の
ALTER TABLEであれば、マイグレーションの儀式なしに旧データベースを読み取り可能なまま保てる。
帰結
- small-to-big がエンドツーエンドで機能する。精密な子チャンクのヒットが生成用の親セクションを供給する一方で、引用は子チャンクの真のロケータに固定されたまま維持され、親ロケータは逆参照のままである。
- オフラインのデフォルトはバイト同一である。デフォルトのチャンク化はフィールドを空のままにし、スニペットにキーを追加せず、スコアリングも出典追跡も変更しない。BM25/ベクトルのメタデータ記録が新しいフィールドを読むことは決してない。
SentenceWindowChunkerも同じ経路を通じて永続化を獲得する。- 変更された成果物には、
ChunkStore/StoredChunkの 4 フィールドとマイグレーション、2 つのchunker=インジェストシーム、ServiceConfig.chunkerとワーカーへの引き回し、narrative_link._to_snippetのウィンドウからprompt_textへの変換、そしてtests/conformance/test_parent_child_isolation.pyおよび永続化/マイグレーションのテストが含まれる。