Chunking Pipeline
Purpose
Transform a parsed canonical document into retrieval-optimised chunk records that carry enough context for both dense (vector) and lexical (BM25) search.
Entry Point
ChunkDocumentService.execute(ChunkDocumentCommand) — called by
IngestionJobRunner in the chunk stage after successful parsing.
Stage Flow
parsed document
→ ChunkingBuilder.build_chunks(document)
→ ChunkingService (section + table + document chunks)
→ ExtractiveSummaryBuilder (preamble source for the document chunk)
→ [optional] AssistantBackedDocumentSummarizer (abstractive summaries)
→ ChunkSet attached to document aggregate
Chunk Layers
Every chunk record carries a layer field identifying its origin:
Layer |
Source |
Purpose |
|---|---|---|
|
A heading-bounded text section of the document |
Core retrieval unit for prose |
|
A row or row group from a parsed table |
Structured data retrieval |
|
The whole document synthesised into one chunk |
Workspace-level overview retrieval |
Section Chunks
ChunkingService walks the canonical SectionNode tree. For each section it
builds one ChunkRecord whose retrieval_text is structured as:
Document summary: <extractive or abstractive overview>
Document: <title>
Section summary: <section summary if available>
Content:
<section text>
Each section chunk carries two ordinal fields:
ordinal— global sequential position of the section across the whole document (1, 2, 3, … regardless of heading level or numbering)section_ordinal— the number embedded in the heading itself, extracted byterminal_heading_number()(e.g. “Chapter 3” →3, “Foreword” →0)
section_ordinal is what the execution planner emits and the document retriever
filters on. It maps user intent (“first chapter”, “Chapter 3”) to the correct
set of chunks without relying on position in the parse tree.
Table Chunks
TableChunker builds one or more ChunkRecord entries per table. Each row
group becomes a chunk carrying the table caption, column headers, and row data.
parent_table_id links table-row chunks back to their source table for abstractive
summary injection.
Document Chunk
One document-layer chunk is built per document. It carries:
the document-level summary (extractive or abstractive) as its primary content
a
summary_packetin its metadata withglobal_synopsis,overview_summary, andretrieval_lenses(overview, table summary, section guide)
This chunk is used for workspace-level “what is this about?” queries without needing to retrieve individual sections.
Extractive Summary Source
ExtractiveSummaryBuilder selects a representative subset of sections and tables
to feed into the document-level summary. It avoids:
orphan sections (TOC entries, index pages, back matter detected by the
_orphanssection id suffix)injecting the TOC into every chunk’s preamble — long narrative sequences (books) are capped to one representative section to prevent the table of contents from dominating the preamble
The builder detects long narrative sequences by counting numbered heading sequences. When a document has more than twice the representative section limit of numbered headings (as in a novel with many short chapters), only one section is used in the extractive preamble.
Abstractive Summaries (Optional)
When AIBS_CHUNKING_ABSTRACTIVE_SUMMARIES_ENABLED=true, after extractive chunking
AssistantBackedDocumentSummarizer uses the dedicated AIBS_LLM_SUMMARIZER_*
assistant lane. It sends one overview call, then section batches of 3, then
table batches of 3. The calls are sequential within one document, while the
summarizer lane has its own concurrency gate so ingestion enrichment cannot
consume the interactive chat lane.
The summarizer receives:
overview_summary— document-level overview replacing the extractive preamblesection_summaries— one summary per section id, injected into each matching section chunk’sretrieval_textasSection summary: …table_summary— cross-table summary added to the document chunktable_summaries— one summary per table id, injected into matching table-row chunks asTable summary: …
The summarizer guards against LLM hallucination by:
validating all returned
section_idandtable_idvalues against thevalid_idsfrozenset from the extractive sourcededuplicating repeated ids with a
seen_idssetclipping per-section and per-table prompt input before each batch
passing the detected ISO language code directly when available, or asking the model to infer language from the extractive text when no code is available
raising out only when the overview call fails; malformed section/table batch responses are recorded as partial enrichment failures
If abstractive summarization fails (LLM offline, malformed JSON, timeout), the service logs a warning and falls back to the extractive summary. The chunk set is still persisted and the document advances to indexed status.
If only a section/table batch fails, the document chunk keeps the existing
metadata["abstractive_summary"] block and adds partial-enrichment fields:
overview_present, section_summary_count, table_summary_count,
missing_section_ids, missing_table_ids, failed_batches, and
degraded_summary. A degraded summary remains searchable; Stage 1 extractive
metadata is the fallback.
The gate condition requires either selected_sections or selected_tables to be
non-empty. Table-only documents (e.g. data sheets with no narrative sections)
still receive an abstractive table summary.
Chunk Metadata Fields
Key fields on ChunkRecord used downstream by retrieval and answering:
Field |
Type |
Description |
|---|---|---|
|
str |
|
|
int |
Global sequential position in the document |
|
int |
Heading-derived chapter/section number (0 = unnumbered) |
|
str |
Full structured text sent to both embedding and BM25 |
|
str |
One-sentence summary of this chunk |
|
str | None |
Section id for table-row chunks |
|
str | None |
Table id for table-row chunks |
|
str |
Section id for section-layer chunks (used as the summary key) |
Chunk Policy Version
ChunkingBuilder.build_policy_version() returns a hash of the current chunking
configuration. It is stored on the ChunkSet and used to detect when a document’s
chunks are stale relative to a new chunking policy version, enabling future
selective re-chunking without full re-ingestion.