Principles
- Respect source structure. Chunk at sections, speaker turns, or clause boundaries. Fall back to token-window chunking only when no structure exists.
- One chunk = one contract obligation. Permit conditions, lease clauses, and blocker tags each become their own chunk.
- Parent-child friendly. Every chunk carries
doc_id+section_idso retrievers can fetch the parent document when a chunk matches. - Bounded size. Target 300–800 tokens for narrative sections; hard cap 1,500 tokens with intelligent split.
- Overlap only when needed. Zero overlap for structured splits. Fixed 50-token overlap only for token-window fallback.
- Deterministic IDs.
chunk_idis SHA-256-derived viamake_chunk_id(doc_id, section_id)— re-running the chunker on unchanged text produces identical IDs, enabling incremental vector-index updates.
Chunk schema
Every chunk record has this shape, written to a per-dataset_chunks Parquet file alongside the main dataset export.
Per-dataset chunking rules
Civic Intelligence
Source material: council transcripts, agenda items, meeting minutes. Chunk at, in priority order:- Agenda item boundaries — each item is its own chunk tree.
section_id = "item.{agenda_item_number}". - Speaker-turn boundaries — inside an agenda item, chunk per speaker turn.
section_id = "item.{n}.turn.{m}". Turns under 50 tokens are merged with the next turn from the same speaker. - Blocker/condition enumeration — each blocker tag or condition becomes its own chunk with
chunk_type=condition. - Contingency DAG nodes — each node becomes a
chunk_type=contingencychunk.
parent_chunk_id set.
Events Timeline
Each event is a chunk.section_id is the event’s own ID. chunk_type=event.
Long event descriptions (e.g. dark_event with extensive kinematic detail) split at 800 tokens with 50-token overlap. All splits share the same parent_chunk_id.
Permit Signals
Two-level tree:- Permit parent — summary plus APRS envelope.
chunk_type=permit_summary. - Scope sections — each LLM-extracted scope section (e.g. “Floor 1 addition”, “Mechanical — HVAC”) becomes its own chunk with
chunk_type=permit_scope_section.
chunk_type=condition — they are frequently cited in isolation, so joining them with their scope section would hurt retrieval.
OSHA Safety
Two-level tree:- Case parent —
chunk_type=osha_case_summary. - Citation sections — one per citation, since OSHA citations are independently enforceable.
chunk_type=osha_citation.
AIS Maritime Positions
Not chunked. Positions are point-in-time records — one row equals one record. No_chunks file for this dataset.
Urban Signal Grid, LEHD Flows, POI Intelligence
Structured-only datasets. Thellm_text Markdown-KV view is the text surface and is already compact (under 400 tokens per record). Each record maps to one chunk with chunk_type=record_view.
Chunk types
Token-window fallback
When a source has no identifiable structure (rare; mostly raw scraped HTML), Codex falls back to sliding-window chunking:- Window: 600 tokens
- Stride: 550 tokens (50-token overlap)
- Chunk type:
narrative_window - Section ID:
"window.{seq_index}"
Parent-child retrieval
You can use the chunk tree directly with parent-child retrieval frameworks like LangChain or LlamaIndexParentDocumentRetriever:
Cross-encoder re-ranking
Initial retrieval over chunk embeddings is coarse. The recommended post-retrieval step is a cross-encoder re-rank over the top 50 results. Score pairs:(query, chunk_text). Drop chunks below a score_threshold of 0.3.
Codex does not run the re-ranker — this is a consumer-side pattern. The chunk structure (bounded size, clean boundaries) is designed to be compatible with standard re-ranking pipelines.
Embedding policy
- Default model:
text-embedding-3-large(OpenAI), 3072 dimensions, reduced to 1536 for storage. - Re-embedding triggers: model version bump or
chunking_versionbump. Embeddings are tagged withembedding_modelandembedding_versionso you can decide whether to re-embed. - Deterministic re-embedding: because
chunk_idis deterministic, re-embedding the same text produces the samechunk_id. Embedding rows update in place without orphans.