Skip to content

feat(rag): contextual ingest done right — heading paths, context stored beside the body (#216)#243

Merged
jrosskopf merged 1 commit into
mainfrom
rag/contextual-ingest
Jul 5, 2026
Merged

feat(rag): contextual ingest done right — heading paths, context stored beside the body (#216)#243
jrosskopf merged 1 commit into
mainfrom
rag/contextual-ingest

Conversation

@jrosskopf

@jrosskopf jrosskopf commented Jul 5, 2026

Copy link
Copy Markdown
Contributor

Summary

Completes Variant A of #216 per the issue's own 'Open question' recommendation. #224 had shipped a [title › p.N] prefix that overwrote blocks.body; this PR moves the context OUT of the display text and adds the heading path:

  • Prefix [<title> › <heading path…> › p.<page>] — deterministic markdown-outline derivation, no LLM (Variant B stays behind future config).
  • The storage split: blocks.body verbatim (snippets/expand clean); new blocks.context column carries the prefix; dense embeddings embed context\nbody; FTS indexes both fields; the rerank passage concatenates them — one consistent indexed representation, zero consumer churn on the search tool.
  • Cutover follows repo precedent: SCHEMA_VERSION 5→6 (manifest guard refuses mismatched artifacts), ensure_block_context adds the column idempotently on boot, and rebuild re-derives chunks+contexts from retained blobs — flipping ESCUREL_INGEST_CONTEXTUALIZE + rebuild is the operator story. Rows ingested under feat(rag): contextual retrieval Variant A (structural) — prefix chunks with [title › p.N] #224's baked-in prefix normalize on rebuild.

Test plan (red-first, no mocks — crates/escurel-index/tests/contextual_retrieval.rs)

title_token_retrieves_chunk_but_display_body_stays_clean, stored_matrix_body_verbatim_context_carries_prefix (exact prefix pinned), heading_context_disambiguates_similar_chunks (the e2e point of the feature), rerank_passage_carries_structural_context, contextualize_off_stores_no_context, plus document.rs unit tests. All ranking-sensitive suites pass unmodified. Four-command gate green.

Not in this PR

Variant B (planned later, behind config); the eval on/off delta — the eval harness ingests via its own pre-embedded pass and needs a small follow-up to feed contextualized chunks (command documented in the issue comment).

🤖 Generated with Claude Code


View with Codesmith Autofix with Codesmith
Need help on this PR? Tag /codesmith with what you need. Autofix is disabled.

…de a verbatim body

Completes Variant A of #216 (structural contextual retrieval) per the
issue's recommended storage split, extending the first cut from #224:

- New `blocks.context` column (sql/0007, `Migrator::ensure_block_context`
  on every boot, SCHEMA_VERSION 5 → 6): the situating prefix now lives
  BESIDE the chunk instead of overwriting it. `blocks.body` is verbatim
  again — snippets, `expand` and byte-span provenance are clean.
- Prefix format grows the heading path: `[<title> › <heading path> ›
  p.<page>]`, derived deterministically from the markdown ATX hierarchy
  open at each chunk's byte_start (`heading_path_at`); a heading equal
  to the title is deduped. No inference, no new deps.
- The contextualised text feeds retrieval only: dense embeddings embed
  `context\n body` (`IndexChunk::embed_text`), the FTS index covers
  ('body', 'context') in both the fresh migration and `refresh_fts`,
  and the rerank passage is `concat_ws(chr(10), context, body)`.
- `materialize_document` / `write_document_blocks` take `&[IndexChunk]`
  ({context, body}); live ingest and rebuild share
  `contextualized_chunks`, so a from-scratch rebuild reproduces
  identical rows — rebuild is also the operator's re-embed cutover
  after flipping `ESCUREL_INGEST_CONTEXTUALIZE` (off | structural,
  default structural, unchanged from #224).
- Variant B (LLM-situated context) stays out of scope behind future
  config, per the issue.

Tests (red first, real DuckDB + FsStore + HashEmbedder, no mocks) in
crates/escurel-index/tests/contextual_retrieval.rs:
- title_token_retrieves_chunk_but_display_body_stays_clean
- stored_matrix_body_verbatim_context_carries_prefix
- contextualize_off_stores_no_context
- heading_context_disambiguates_similar_chunks
- rerank_passage_carries_structural_context
plus document.rs unit tests for heading_path_at / the prefix format /
the storage split. Full gate green: fmt, clippy -D warnings, workspace
tests, release build.

Refs #216.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant