Choose a chunking strategy by document type¶
Draft: not validated
Not yet dry-run. Do not use on a live system until the validation record is complete and signed off.
1. Summary¶
This decision guide establishes the strategy, window size, overlap, and metadata extraction model for chunking documents into search-ready passages. It classifies enterprise documents into four distinct archetypes:
- Structured manuals: Technical documentation, SOPs, and policies structured with hierarchical headings.
- Tables and forms: Financial filings, spec sheets, and forms dominated by tabular structures.
- Long continuous prose: Narrative reports, research papers, and legal transcripts lacking distinct Markdown headings.
- Short records: FAQs, customer support tickets, and catalog items under 1,000 characters.
The recommended default for enterprise corpora is structure-aware Markdown heading chunking with layout extraction (Azure Document Intelligence prebuilt-layout with LangChain MarkdownHeaderTextSplitter and a secondary recursive character splitter at 2,000 characters / ~500 tokens with 25% overlap).
Facts checked against Microsoft Learn and AWS documentation on 27 October 2026. The Document Layout skill for Azure AI Search is preview (REST API 2024-11-01-preview or 2024-11-30-preview ).
2. When to use and when not to¶
Use this guide when:
- Starting any new RAG project or designing an ingestion pipeline.
- Introducing a new document archetype or file format into an existing corpus.
- Retrieval evaluation indicates answers are split across chunk boundaries, tables are severed, or search results contain high duplicate chunk density.
- Benchmarking candidate chunk sizes and overlaps against retrieval metrics (
recall@10,nDCG@10) and token budgets.
Do not use it when:
- You are writing the layout-aware extraction and chunking code. Use PB-RAG-01-02.
- You are re-chunking and re-embed a live production corpus without downtime. Use PB-RAG-01-03.
- You are conducting a production change review or troubleshooting indexing failures. Use PB-RAG-01-04.
- You are selecting the retrieval mode (keyword, vector, hybrid, semantic ranker). Use PB-RAG-08-01.
3. What you must know first¶
- The chunking trade-off. Small chunks (128–256 tokens) maximise vector specificity and pinpoint retrieval, but risk severing multi-step procedures or table rows. Large chunks (1,024–2,048 tokens) preserve contextual coherence and tabular integrity, but dilute semantic vector similarity and consume more of the LLM context window. Microsoft Learn recommends starting near 512 tokens (~2,000 characters) with ~25% overlap.
- Layout preservation before chunking. Plain text extraction strips Markdown headings, table boundaries (
<table>,<tr>), and page numbers. A naive splitter cuts tables mid-row, resulting in nonsensical numerical answers. Always parse structured documents into Markdown via a layout engine (Document Intelligenceprebuilt-layout) before chunking. - Token size versus character count. Embedding models enforce strict token limits (for example,
text-embedding-3-largesupports up to 8,191 tokens; check current limits in the docs). Splitters measure characters by default (typically 1 token ≈ 4 characters in English). Pass a model tokenizer length function when strict token ceilings are required. - Overlap rules and constraints. Overlap preserves semantic continuity across chunk borders. Overlap must always be strictly less than chunk size; in the Azure AI Search
TextSplitskill, overlap must be less than half the maximum page length. Overlap above 30% (team default recommendation) inflates vector storage quota and creates duplicate search hits. - Metadata lineage for citations and security. Chunks cannot exist in isolation. Every chunk must retain
source_path,page,heading_path(breadcrumb), and a deterministicchunk_id. This metadata is mandatory for source citations (PB-RAG-06) and security trimming / ACL filters (PB-RAG-04). - Re-chunking triggers a full re-index. Changing chunk size, overlap, or strategy changes all document chunk IDs and vector embeddings. It is not an in-place update; it requires re-indexing the corpus into a new index with an index alias switch (PB-RAG-01-03 and PB-RAG-09-01).
4. Options¶
Decision tree¶
Use this decision tree to identify the appropriate strategy for each document archetype:
graph TD
Start["New document type or corpus"] --> Q1{"Is record standalone and < 1,000 chars?"}
Q1 -- Yes --> OptD["Option D: One chunk per record<br/>(short_records)"]
Q1 -- No --> Q2{"Does content contain multi-row tables or forms?"}
Q2 -- Yes --> OptC["Option C: Table-protected layout chunking<br/>(tables_and_forms)"]
Q2 -- No --> Q3{"Does content have hierarchical headings?"}
Q3 -- Yes --> OptB["Option B: Markdown heading chunking<br/>(structured_manual - DEFAULT)"]
Q3 -- No --> OptA["Option A: Recursive character chunking<br/>(long_prose)"]
The 9-stage evaluation process¶
Follow this workflow to select, benchmark, and document the chunking strategy:
- Classify the corpus. Group incoming documents into archetypes (
structured_manual,tables_and_forms,long_prose,short_records). Verify: Every document in the intake batch is mapped to one of the four archetypes. - Select candidate profiles. Load starting profiles from
snippets/chunking-profiles.yaml. Verify:snippets/chunking-profiles.yamlloads and contains profile definitions for each archetype present in the corpus. - Extract layout sample. Extract 10–50 representative sample files per archetype with
prebuilt-layoutinto cached Markdown. Verify: The local cache contains.mdfiles preserving#headings,<table>blocks, and<!-- PageBreak -->markers. - Generate candidate chunks. Run candidate profiles against the sample Markdown using
snippets/chunk_layout.py. Verify: Output chunks passreport(chunks, profile)with 0over_maxand 0broken_tables. - Populate scratch index. Index each candidate chunk set into a dedicated sandbox Azure AI Search index using the target embedding model. Verify: Scratch index document count equals the candidate chunk count and index status reports healthy.
- Execute golden query set. Run 30–100 representative queries with labelled relevance to evaluate
recall@10,nDCG@10, and chunk boundary split rate. Verify: Evaluation run completes with computed retrieval accuracy metrics for each candidate profile. - Evaluate capacity and cost. Measure total chunk count, vector index size, and embedding token consumption against service partition limits. Verify: Projected corpus vector size is within partition quota limits (check current limits in the docs).
- Record decision. Document the chosen profiles, parameters, and benchmark evidence in
snippets/chunking-decision-record.md. Verify: The decision record has all candidate scores, final profile assignments, and operational rationale recorded. - Obtain sign-off. Gain RAG Lead sign-off before implementing the profile in the production ingestion pipeline. Verify: The sign-off table in the decision record has dated approvals from the author and RAG Lead.
Destructive: modifying production chunking requires a full re-index
Never apply chunking parameter changes directly to a live production index. Re-chunking alters all document IDs and vectors, leaving orphaned chunks and degrading search results. Always test candidates in a scratch index and execute production changes using the zero-downtime index alias switch procedure in PB-RAG-01-03.
Option A: Recursive character chunking (long_prose)¶
Splits text hierarchically on paragraph breaks (\n\n), newlines (\n), sentence boundaries ('.' followed by a space), and spaces until chunks fall within character limits.
- Best for: Unstructured continuous prose, academic research papers, narrative reports, legal briefs, and interview transcripts lacking distinct Markdown headings.
- Cost and risk: Low compute cost; runs locally without layout extraction when source text is clean. Risk: will cut across logical sections if applied to structured manuals.
- Carried out by: LangChain
RecursiveCharacterTextSplitteror Azure AI SearchTextSplitskill (splitMode: pages).
Option B: Structure-aware Markdown heading chunking (structured_manual)¶
A two-pass strategy: first splits content along Markdown headings (#, ##, ###) to respect author-defined topic boundaries, then applies a secondary recursive splitter to sections exceeding the target chunk size. Prepends the hierarchical heading breadcrumb to each chunk.
- Best for: Technical documentation, standard operating procedures (SOPs), knowledge base articles, and corporate policy manuals.
- Cost and risk: Requires layout analysis (
prebuilt-layout) to generate Markdown. Drastically reduces chunk boundary cut rates and enriches chunks with structural metadata. - Carried out by: PB-RAG-01-02 using
MarkdownHeaderTextSplitterandchunk_layout.py.
Option C: Table-protected layout chunking (tables_and_forms)¶
Extracts documents to layout Markdown with HTML tables (<table>...</table>). Masks tables prior to text splitting, keeping tables whole up to 4,000 characters. Tables exceeding chunk size are split along row boundaries (<tr>), repeating the table header row on each fragment.
- Best for: Financial statements, balance sheets, product specification matrices, and forms dominated by tabular figures.
- Cost and risk: Requires Document Intelligence
prebuilt-layout. Eliminates numerical hallucination from severed table rows. Risk: wide or deeply nested tables require dedicated table processing (PB-RAG-10-01). - Carried out by: PB-RAG-01-02 with
keep_tables_whole: true.
Option D: One chunk per record (short_records)¶
Ingests each record, FAQ pair, or database entry as an atomic chunk without internal splitting.
- Best for: Support ticket solutions, FAQ question-answer pairs, CRM contact logs, and product catalog cards under 1,000 characters.
- Cost and risk: Zero chunking overhead; zero risk of severed context. Risk: records exceeding the embedding model context limit will fail ingestion.
- Carried out by: Direct ingestion script without text splitting. Option D does not use
chunk_layout.py.
Option E: Semantic chunking by embedding distance¶
Breaks text into sentences, computes vector embeddings for each sentence, and places split points where the cosine distance between adjacent sentences exceeds a statistical percentile threshold (e.g. 95th percentile).
- Best for: Multi-topic essays, meeting transcripts, and stream-of-consciousness documents where topic transitions occur without explicit headings.
- Cost and risk: High compute cost and latency (requires embedding every sentence individually). Benchmark evidence rarely justifies the compute overhead on structured corporate documents.
- Carried out by: LangChain
SemanticChunker(experimental).
Comparison of options¶
| Feature | Option A (long_prose) |
Option B (structured_manual) |
Option C (tables_and_forms) |
Option D (short_records) |
Option E (Semantic) |
|---|---|---|---|---|---|
| Extractor required | None or prebuilt-layout |
Document Intelligence prebuilt-layout |
Document Intelligence prebuilt-layout |
None (Direct read) | None or Text extract |
| Split logic | Paragraphs / sentences | Markdown headings (#, ##, ###) |
Markdown headings + Table masking | None (Atomic record) | Sentence cosine distance |
| Table protection | No (cuts mid-table) | Partial (unless masked) | Yes (Atomic or row-split) | Not applicable | No |
| Heading breadcrumb | No | Yes (prefix_heading: true) |
Yes | No | No |
| Pre-processing cost | Negligible | Low (layout model only) | Low (layout model only) | None | High (N embeddings) |
| Recommended size | 2,000 chars / 500 overlap | 2,000 chars / 500 overlap | 4,000 chars / 0 overlap | Full record (< 1,000 chars) | Variable by distance |
Snippet 1: Chunking profiles by archetype¶
Load starting configurations from snippets/chunking-profiles.yaml. A short representative profile for the default structured_manual archetype:
# Representative profile from snippets/chunking-profiles.yaml
structured_manual:
profile_id: structured_manual
extractor: prebuilt-layout
strategy: markdown_headers
headers: ["#", "##", "###"]
header_levels: 3
chunk_size: 2000
chunk_overlap: 500
prefix_heading: true
The complete set of starting profiles for all four archetypes is defined in snippets/chunking-profiles.yaml.
Snippet 2: Decision record template¶
Record benchmark evaluation metrics and final profile choices in snippets/chunking-decision-record.md. An illustrative excerpt:
# Chunking Decision Record: ${CORPUS_NAME}
- **Project:** ${PROJECT_NAME} | **Author:** ${AUTHOR_NAME}
- **Embedding model:** ${EMBEDDING_MODEL} (default 3072 dims)
- **Target latency budget:** ${LATENCY_TARGET_P95}
## Benchmark Results on Golden Query Set
| Candidate Profile | Archetype Tested | Recall@10 | nDCG@10 | Chunk Cut Rate (%) |
|---|---|---|---|---|
| Candidate A (Fixed 2000/500) | structured_manual | ${CAND_A_R10} | ${CAND_A_NDCG} | ${CAND_A_CUT_PCT}% |
| Candidate B (Markdown H1-H3) | structured_manual | ${CAND_B_R10} | ${CAND_B_NDCG} | ${CAND_B_CUT_PCT}% |
The full template with inventory tables, rationale notes, and approval signatures is maintained in snippets/chunking-decision-record.md.
5. Decision table¶
| Situation | Choose | Why |
|---|---|---|
| Standard operating procedure (SOP) or technical guide with H1–H3 headings | Option B (structured_manual) |
Preserves section boundaries, prevents splitting numbered procedure steps, and prepends heading paths for retrieval grounding. |
| Financial reports, balance sheets, and spec sheets with multi-column tables | Option C (tables_and_forms) |
Keeps <table> blocks whole up to 4,000 characters. Prevents broken rows which cause numerical hallucination. |
| Narrative whitepapers, legal depositions, and reports lacking standard headings | Option A (long_prose) |
Recursive splitting falls back gracefully to paragraph and sentence boundaries without failing on missing headings. |
| FAQ articles, CRM interactions, or catalog cards under 1,000 characters | Option D (short_records) |
Preserves the complete context in a single passage with zero boundary splitting or duplicate chunk overhead. |
| Mixed PDF corpus containing both narrative sections and multi-row tables | Option C (tables_and_forms) with secondary split |
Protects tables atomically while recursively splitting narrative text under Markdown headings. |
| Dense technical text where queries target exact part numbers or codes | Option B with 1,000 chars / 200 overlap | Smaller chunks concentrate vector similarity around the target entity. Use hybrid search (PB-RAG-08-01) for exact lexical matches. |
| High-level research queries requiring synthesis across multiple paragraphs | Option B with 4,000 chars / 400 overlap | Larger chunk size provides broader contextual background for the LLM to synthesise themes without requiring high k retrieval. |
| Documents using decorative bold text instead of actual Markdown headings | Option A (long_prose) with header_levels: 0 |
Markdown header splitters cannot detect bold text as headers, which can result in huge un-split chunks. |
| Benchmark reveals answers frequently severed between two consecutive steps | Option B with 500 character overlap | Increasing overlap from 200 to 500 characters preserves continuity across adjacent procedural steps. |
| Multi-topic meeting transcripts or essays with no heading structure | Option E (Semantic chunking) | Identifies natural semantic shift points where cosine similarity between consecutive sentences drops. |
6. Recommended default and when to deviate¶
Recommended default:
Deploy Option B (structured_manual) with layout extraction:
- Extractor: Azure Document Intelligence
prebuilt-layout - Primary splitter: LangChain
MarkdownHeaderTextSplitter(header_levels: 3) - Secondary splitter:
RecursiveCharacterTextSplitter - Target chunk size: 2,000 characters (~500 tokens)
- Chunk overlap: 500 characters (~25% overlap)
- Metadata prefix:
prefix_heading: true(attaches heading breadcrumb to text)
When to deviate:
- Deviate to Option C (
tables_and_forms) when more than 20% of the corpus consists of financial tables, specification sheets, or forms. - Deviate to Option A (
long_prose) when documents contain flat prose without Markdown headings, or when heading analysis reveals decorative pseudo-headings. - Deviate to Option D (
short_records) when ingested entities are individual discrete records under 1,000 characters. - Deviate chunk size downwards (256 tokens / 1,000 characters) when queries target discrete factual values, dates, or item codes.
- Deviate chunk size upwards (1,024 tokens / 4,000 characters) when queries demand expansive narrative context or document summarisation.
- Never deviate based on intuition. Require a benchmark on the golden query set demonstrating a measurable increase in
recall@10(> 3%, team default gate) before approving a non-default configuration.
Rule of thumb: Start with Markdown layout chunking at 512 tokens with 25% overlap; never deviate without benchmark evidence on a golden set showing higher recall@10.
7. Known issues and fixes¶
| Symptom | Likely cause | Fix |
|---|---|---|
| Answers miss the second half of a numbered procedure | Fixed-size character chunks severed steps across an arbitrary length boundary | Switch to Option B (structured_manual) and increase overlap to 500 characters (~25%). |
| Tables retrieved as jumbled numbers disconnected from column headers | Plain text extraction stripped HTML table markup, flattening rows into disjointed strings | Extract via Document Intelligence prebuilt-layout and apply Option C (tables_and_forms) to keep tables inside <table> blocks. |
| Retrieval returns multiple near-duplicate chunks for a single query | Chunk overlap set too high (> 35%), generating duplicate passage variants in the index | Reduce chunk_overlap to 10–20% and deduplicate search results by parent document before passing context to the LLM. |
| Empty or microscopic chunks (under 50 characters) polluting the search index | Splitter triggered on consecutive empty headings or decorative visual dividers | Strip empty headings during layout cleanup and enforce a minimum chunk size filter (e.g. drop chunks under 100 characters). |
Embedding ingestion fails with HTTP 400 (max_tokens_exceeded or context_length_exceeded) |
Splitter sized chunks by character count, but dense text (code, technical tokens) exceeded model token limits | Pass a model-specific tokenizer length function (e.g. tiktoken for OpenAI models) to chunk() instead of raw character len. |
| LLM cannot cite source page numbers accurately in answers | Splitter discarded Document Intelligence <!-- PageBreak --> markers during ingestion |
Parse page break comments during splitting and attach the exact page number to every chunk metadata (see PB-RAG-01-02). |
| Vector search fails to retrieve exact part numbers or acronyms | Pure vector search over large chunks dilutes the semantic signal of short unique tokens | Reduce chunk size to 1,000 characters and implement hybrid search with BM25 keyword matching (PB-RAG-08-01). |
| Old chunks still appear in search results after a chunking change | Re-indexing was run in place without deleting orphaned chunks from the previous schema | Rebuild the index into a new index container and cut over traffic using an index alias (PB-RAG-01-03 and PB-RAG-09-01). |
8. Monitoring and alerts¶
The thresholds below are illustrative team defaults, not platform recommendations. Tune them against a measured baseline.
| Signal | Alert when (team defaults) | Action |
|---|---|---|
RecallAt10OnGoldenSet (CI evaluation pipeline) |
Drops by > 3% compared to baseline on evaluation run | Block the pull request; inspect failure queries for chunk boundary splits or missed tables. |
ChunkBoundarySplitRate (CI evaluation pipeline) |
Exceeds 5% of queries on golden set | Increase chunk overlap or upgrade from Option A to Option B. |
TableParseErrorCount (Ingestion pipeline logs) |
> 0 table parsing errors per 1,000 documents | Check Document Intelligence layout output; verify table masking regex. |
OversizedChunkDropCount (Ingestion pipeline logs) |
> 0 chunks exceed model token limit | Verify secondary recursive splitter bounds and tokenizer measure function. |
VectorQuotaUsage (Azure AI Search platform metric) |
Exceeds 80% of partition quota during indexing | Check current limits in the docs. Scale partitions before proceeding with full re-index. |
ThrottledSearchQueriesPercentage (Azure AI Search platform metric) |
Above 1% for 5 minutes during benchmark indexing | Throttle indexing worker concurrency or schedule re-indexing off-peak. |
9. Cost and capacity notes¶
- Layout analysis cost: Document Intelligence
prebuilt-layoutis billed per 1,000 pages analysed. Check current prices before approval. Caching the extracted Markdown locally makes all subsequent chunking and benchmarking experiments free. - Embedding token volume: Smaller chunk sizes with generous overlap increase the total number of chunks and vectors. For example, moving from 1,000 characters with 10% overlap (net step 900 characters) to 500 characters with 25% overlap (net step 375 characters) yields 900 / 375 = 2.4x as many chunks and vectors for the same source text, directly increasing embedding API costs and index vector quota consumption.
- Vector index quota: Vector index quota in Azure AI Search is a hard limit per partition. Check current service limits in the docs. A re-indexing migration requires holding two full indexes simultaneously during validation, requiring 2x vector and storage capacity.
- Semantic chunking overhead: Option E requires embedding every individual sentence during ingestion before computing cosine distances. This increases embedding API consumption by an order of magnitude compared to heuristic Markdown splitting.
10. AWS and open-source equivalents¶
| Platform | Equivalent | Key differences |
|---|---|---|
| Azure AI Search | Document Layout skill (preview) and Text Split cognitive skill | Document Layout skill integrates prebuilt-layout directly into Azure AI Search indexer skillsets (preview, REST API 2024-11-01-preview or 2024-11-30-preview). Text Split skill supports fixed-size page splitting. |
| Amazon Bedrock Knowledge Bases | Managed chunking strategies | Offers managed Fixed-size chunking (tokens and overlap percentage), Hierarchical chunking (parent-child), Semantic chunking, and No chunking. Amazon Textract provides layout analysis. |
| Open-source (LangChain) | MarkdownHeaderTextSplitter + RecursiveCharacterTextSplitter |
Exact equivalent to Option B. Offers SemanticChunker for Option E. Runs on local compute with no cloud vendor lock-in. |
| Open-source (LlamaIndex) | SentenceSplitter and HierarchicalNodeParser |
HierarchicalNodeParser produces parent-child chunk hierarchies where large parent chunks provide LLM context while small child chunks are indexed for vector search. |
| Open-source extraction | unstructured or marker |
Open-source layout extraction packages that convert PDFs to Markdown without external API fees, but require dedicated GPU instances for high-throughput OCR. |
11. Validation record¶
Pending. Filled in by the dry-runner (see docs/validation-protocol.md).
Sandbox scenario: A second engineer takes a representative sample of 50 documents covering two archetypes (35 structured technical manuals and 15 tabular financial reports).
They execute the 9-stage evaluation process in a sandbox Azure AI Search index using the candidate profiles in snippets/chunking-profiles.yaml and the golden evaluation query set. The engineer records benchmark metrics in snippets/chunking-decision-record.md.
Pass criteria:
- The decision table maps each document archetype to the candidate profile that achieves the highest
recall@10on the golden query set within the token budget. - The selected profiles achieve
Recall@10>= 0.85 andChunkBoundarySplitRate<= 5% on structured manuals and tabular documents. - All candidate profiles parse successfully in the reference chunker without raising
ConfigurationErrororChunkError.
12. Sources¶
Checked on 27 October 2026.
- Microsoft Learn, Chunk large documents for RAG and vector search in Azure AI Search: guidance on chunk sizes, overlap percentages, and semantic chunking.
- Microsoft Learn, Text Split cognitive skill (Azure AI Search): page splitting modes and overlap constraints.
- Microsoft Learn, Azure Content Understanding skill: AI enrichment using multimodal analyzers (preview).
- Microsoft Learn, Chunk and vectorize with Content Understanding: semantic chunking and layout parsing with Content Understanding (preview).
- Microsoft Learn, Chunk and vectorize with the Document Layout skill: Document Layout skill and automated section chunking (preview).
- Microsoft Learn, Document layout analysis (Layout model): Markdown format, table extraction, and page break representations.
- LangChain Documentation, LangChain text splitters: recursive character, markdown header, and semantic text splitters.
- AWS Documentation, How content chunking works for Amazon Bedrock knowledge bases: managed fixed-size, hierarchical, and semantic chunking options.
- Docling Documentation, Docling chunking: layout-aware and hybrid chunking strategies.
Related playbooks¶
- PB-RAG-01-02: Build layout-aware chunking with Document Intelligence and LangChain (carries out Options B and C).
- PB-RAG-01-03: Re-chunk and re-embed a live corpus without downtime (zero-downtime production cutover).
- PB-RAG-01-04: Chunking change review and troubleshooting checklist (pre-deployment audit).
- PB-RAG-08-01: Choose the retrieval mode: keyword, vector, hybrid, semantic ranker or external re-ranker.