← Back to Technical Library

Chunking Strategies for RAG

A Complete Guide to Splitting Documents for Retrieval -- From Fixed-Size to Agentic, With Trade-Offs, Code Examples, and Production Recommendations for Sovereign, On-Premise Systems

Chunking Strategies for RAG

The Art and Science of Splitting Documents for Retrieval-Augmented Generation

📄 Technical Reference ⏱️ 30 min read 📂 RAG Engineering

Chunking is the process of splitting documents into smaller, retrievable units before embedding them in a vector database. It is the single most underappreciated lever in RAG system quality. A perfectly tuned embedding model and a state-of-the-art LLM will still produce wrong answers if the chunks fed to the retriever cut a sentence in half, bury a key clause inside an unrelated paragraph, or isolate a table from its header row. This guide covers every major chunking approach in depth -- fixed-size, sentence-aware, paragraph-aware, recursive, semantic, document-aware, and agentic -- along with overlap strategies, chunk-size guidance, multi-modal chunking for audio/video/images, practical recommendations by document type, a full comparison table, and readable code examples. Every recommendation is framed for sovereign, on-premise RAG deployments where data never leaves your infrastructure.

ChunkingRAGRetrievalLangChainLlamaIndexOn-Premise
🎯 Bottom Line: There is no single best chunking strategy. The right choice depends on your document structure, retrieval precision requirements, compute budget, and whether you can afford an LLM in the chunking loop. For most on-premise RAG deployments, start with recursive chunking at 512 tokens with 10-20% overlap. If your documents have clear structure (headers, sections, tables), upgrade to document-aware chunking. If retrieval precision is critical and you have GPU capacity for an embedding model in the chunking pipeline, add semantic chunking. Reserve agentic chunking for high-value documents where every retrieval miss is expensive. Measure retrieval quality (recall@k, answer faithfulness) before and after every chunking change -- intuition about chunk quality is consistently wrong.

Why Chunking Matters

You cannot feed an entire document into a vector database as a single embedding and expect useful retrieval. A 200-page contract embedded as one vector produces a single point in vector space -- a vague average of everything the document says. A query about "termination clause" and a query about "payment terms" both match that same point equally well (or equally poorly), and the retriever has no way to pull out the specific paragraph that answers either question. The LLM receives the entire document or nothing, and for long documents, "entire document" exceeds the context window anyway.

Chunking solves this by splitting documents into smaller units -- paragraphs, sentences, sections, or topically coherent passages -- each of which gets its own embedding. When a user asks "What is the termination notice period?", the retriever can pull the specific chunk containing the termination clause and feed only that to the LLM. The answer is precise, sourced, and cheap (fewer tokens to generate over).

The Retrieval Chain

Chunking sits at the very start of the RAG pipeline, and every downstream stage depends on it:

  • Chunking: Split documents into units. This is where boundaries are decided.
  • Embedding: Each chunk is embedded independently. The embedding represents that chunk's meaning, not the whole document's.
  • Indexing: Chunks (vectors + metadata) are stored in the vector database. Chunk size affects storage and memory.
  • Retrieval: The query is embedded, nearest chunks are found. Chunk boundaries determine what gets returned.
  • Generation: Retrieved chunks are injected into the LLM prompt. Chunk size and count determine how much context the LLM sees.

Bad chunking at step one compounds through every subsequent stage. A chunk that cuts a sentence in half produces a broken embedding -- the vector represents a fragment of meaning, not a complete thought. When retrieved, it gives the LLM an incomplete picture. The LLM, lacking the missing half, either hallucinates the rest or says "I don't know." Either way, the user gets a worse answer than they should have. Bad chunking equals bad retrieval equals bad answers. This is not a theoretical concern -- it is the most common cause of RAG quality problems in production, more common than weak embedding models or small LLMs.

The Three Failure Modes of Bad Chunking: (1) Fragmented meaning: A chunk ends mid-sentence or mid-thought, so the embedding captures only partial meaning. (2) Diluted meaning: A chunk is too large and covers multiple unrelated topics, so the embedding is a blurry average that matches many queries weakly instead of one query strongly. (3) Lost context: A chunk is correctly sized but separated from its surrounding context (a table without its caption, a code block without its preceding explanation, a clause without the section header that defines its scope). All three produce retrieval that technically "matches" but gives the LLM insufficient or misleading information.

Why Not Just Use a Longer Context Window?

Modern LLMs support context windows of 128K, 200K, even 1M tokens. Why chunk at all? Why not dump the entire document into the prompt? Three reasons. First, cost: generation pricing is per input token. Feeding 100K tokens when 500 tokens would answer the question is a 200x cost penalty. Second, latency: time-to-first-token scales with input length. A 100K-token prompt is slower than a 2K-token prompt, every time. Third, attention dilution: LLMs suffer from the "lost in the middle" phenomenon -- information in the middle of a long context is retrieved and used less accurately than information at the start or end. Precise chunking plus targeted retrieval puts only the relevant 500 tokens in the prompt, maximizing accuracy, minimizing cost, and minimizing latency. Chunking is not a workaround for small context windows; it is an optimization for quality, speed, and cost at any context size.

Sovereign RAG Consideration: In on-premise deployments, compute is finite. Smaller chunks mean more vectors to store and search, but each embedding call is cheaper. Larger chunks mean fewer vectors but more tokens per generation. The optimal chunk size on a self-hosted GPU cluster with limited VRAM differs from a cloud API deployment where you pay per token. Always benchmark chunking strategies against your actual hardware, not theoretical cloud pricing.

Fixed-Size Chunking

Fixed-size chunking is the simplest strategy: split the document into chunks of exactly N characters or N tokens, where N is a constant. If the document is 10,000 characters and N is 500, you get 20 chunks of 500 characters each. That is the entire algorithm. There is no linguistic analysis, no structure awareness, no semantic reasoning. The splitter counts characters (or tokens) and cuts.

How It Works

You choose a chunk size (e.g., 500 characters or 128 tokens) and optionally an overlap (e.g., 50 characters). The splitter walks through the text and emits a chunk every N units. With overlap, the last O characters of chunk K are repeated as the first O characters of chunk K+1, creating a sliding window.

# Fixed-size chunking by character, 500 chars, 50 overlap text = "The quick brown fox jumps over the lazy dog. " * 100 chunk_size = 500 overlap = 50 chunks = [] start = 0 while start < len(text): end = start + chunk_size chunk = text[start:end] chunks.append(chunk) start = end - overlap # slide forward by (chunk_size - overlap) # Result: ~20 chunks, each 500 chars, 50 chars shared with neighbor # Problem: boundaries cut mid-word, mid-sentence, mid-thought

Advantages

  • Simplicity: No NLP libraries, no models, no dependencies. A few lines of code.
  • Predictability: Every chunk is exactly the same size. Storage and token budgets are easy to calculate.
  • Speed: O(n) in document length, no model inference required. Millions of documents per hour on a single CPU.
  • No external dependencies: Works offline, no API calls, no GPU needed. Ideal for air-gapped on-premise systems.

Disadvantages

  • Cuts sentences in half: A 500-character chunk might end at "...the plaintiff alleged that the def" and the next chunk starts "endant breached the contract on..." The embedding for each half is meaningless on its own.
  • No semantic coherence: A chunk might contain the end of one topic and the beginning of another, producing a muddy embedding that matches neither topic well.
  • Breaks structure: Tables, code blocks, lists, and headers are split arbitrarily. A table row might be separated from its header.
  • Inconsistent information density: 500 characters of a dense legal contract contains far more information than 500 characters of a chatty email. Fixed size does not mean fixed information content.

When to Use It

Fixed-size chunking is appropriate for uniform documents where structure is either absent or irrelevant -- log files, transcripts without speaker labels, homogeneous text streams, and quick prototyping where you need a baseline to compare smarter strategies against. It is also a reasonable default for very large corpora where the cost of smarter chunking (embedding model inference for semantic chunking, LLM calls for agentic chunking) is prohibitive. For a 10-million-document ingestion pipeline running on limited hardware, fixed-size chunking at 512 tokens with 10% overlap is a perfectly defensible production choice -- the retrieval quality loss is small compared to the cost of semantic chunking at that scale.

Sentence-Aware Chunking

Sentence-aware chunking splits text at sentence boundaries instead of arbitrary character counts. The algorithm first detects sentences using an NLP library (spaCy, NLTK, or a simpler regex-based sentence tokenizer), then groups sentences together until the target chunk size is reached. The result: every chunk starts at the beginning of a sentence and ends at the end of a sentence. No chunks are cut mid-sentence.

How It Works

The sentence tokenizer walks the text and identifies sentence boundaries -- periods followed by spaces and capital letters, but handling abbreviations ("Dr.", "Inc.", "U.S.") and edge cases ("What about 3.14?"). Once sentences are separated, a greedy accumulator adds sentences to the current chunk until adding the next sentence would exceed the target size. At that point, the chunk is emitted and a new one begins.

# Sentence-aware chunking using spaCy import spacy nlp = spacy.load("en_core_web_sm") doc = nlp(text) sentences = [sent.text for sent in doc.sents] chunks = [] current_chunk = "" for sentence in sentences: if len(current_chunk) + len(sentence) <= max_size: current_chunk += " " + sentence else: chunks.append(current_chunk.strip()) current_chunk = sentence if current_chunk: chunks.append(current_chunk.strip()) # Each chunk contains complete sentences -- no mid-sentence cuts. # spaCy handles abbreviations and edge cases better than regex.

Sentence Detection Libraries

  • spaCy: Production-grade NLP library with accurate sentence segmentation. Models available in multiple sizes (sm, md, lg, trf). The sm model (50MB) is sufficient for sentence boundary detection. Runs entirely on-premise, no API calls.
  • NLTK: The classic Python NLP library. nltk.sent_tokenize() uses Punkt, a pre-trained unsupervised tokenizer. Lightweight and reliable for well-formed English text.
  • Blancer / pySBD (Python Sentence Boundary Disambiguation): More accurate than NLTK for noisy text (social media, transcripts with filler). Handles edge cases like "U.S. District Court" better than naive splitters.
  • Stanza (Stanford NLP): Multi-language sentence segmentation. Essential if your corpus is not English.
  • Regex-based: A fallback that splits on [.!?]\s+[A-Z]. Fast but fragile -- mis-handles abbreviations, decimal numbers, and ellipses.

Advantages

  • Complete thoughts: Every chunk contains whole sentences. Embeddings represent complete ideas, not fragments.
  • Better retrieval precision: A query matches a chunk that actually says something, not half of something.
  • Modest cost: spaCy sentence segmentation is fast (thousands of documents per second on CPU). No GPU required.
  • On-premise friendly: spaCy, NLTK, and pySBD all run locally. No data leaves your infrastructure.

Disadvantages

  • Variable chunk sizes: Some chunks may be tiny (a one-sentence paragraph) and others near the max. This can create storage and retrieval inconsistencies.
  • Still ignores higher structure: Sentences are grouped by proximity, not by topic. A chunk may contain sentences from two different paragraphs discussing different subtopics.
  • Sentence detection is imperfect: Abbreviations, code, legal citations ("see § 12.3(b)(ii)"), and tables can confuse sentence tokenizers. A bad sentence break still produces a bad chunk.
  • Language dependency: You need the right model for each language in your corpus. A monolingual spaCy model will produce garbage on other languages.

When to Use It

Sentence-aware chunking is the right step up from fixed-size for prose documents: articles, reports, narrative documents, blog posts, emails, and any text where individual sentences carry complete meaning. It is a strong default for general-purpose RAG and is the strategy most "simple" RAG tutorials should actually use instead of fixed-size. The upgrade in retrieval quality over fixed-size is noticeable and immediate, while the added cost (a spaCy model load) is negligible.

Paragraph-Aware Chunking

Paragraph-aware chunking splits at paragraph boundaries instead of sentence boundaries. A paragraph is a natural unit of thought: the writer grouped those sentences together because they discuss a single subtopic. Respecting that boundary produces chunks that are topically more coherent than sentence-grouped chunks, which may accidentally merge two paragraphs discussing different aspects of a topic.

How It Works

The splitter detects paragraph boundaries (typically double newlines, or indentation in formatted documents) and treats each paragraph as an atomic unit. Paragraphs are accumulated into chunks until the target size is reached. If a single paragraph exceeds the target size, the splitter falls back to sentence-level splitting within that paragraph, and if a single sentence exceeds the target, it falls back to character splitting. This cascade ensures no chunk exceeds the limit while respecting the highest available structural boundary.

# Paragraph-aware chunking with fallback to sentences paragraphs = text.split("\n\n") chunks = [] current_chunk = "" for para in paragraphs: if len(current_chunk) + len(para) <= max_size: current_chunk += "\n\n" + para elif len(para) <= max_size: # Paragraph fits but current chunk is full -- emit and start new chunks.append(current_chunk.strip()) current_chunk = para else: # Paragraph itself exceeds max_size -- split by sentences if current_chunk: chunks.append(current_chunk.strip()) current_chunk = "" for sentence in split_sentences(para): if len(current_chunk) + len(sentence) <= max_size: current_chunk += " " + sentence else: chunks.append(current_chunk.strip()) current_chunk = sentence # Result: chunks align with paragraph boundaries wherever possible, # falling back to sentences only for oversized paragraphs.

Advantages

  • Topical coherence: Paragraphs are authored as units of thought. Chunks that respect paragraph boundaries are more semantically focused than sentence-grouped chunks.
  • Better for structured documents: Reports, manuals, and policies typically have well-defined paragraphs. This splitter respects that authoring intent.
  • Cheap: No NLP model needed if paragraphs are delimited by clear markers (double newlines, indentation). Pure string operations.

Disadvantages

  • Highly variable chunk sizes: A one-line paragraph and a 2000-character paragraph produce very different chunks. Some will be too small (poor information density), others too large (diluted embeddings or context overflow).
  • Depends on clean formatting: Documents where paragraphs are not clearly delimited (single newlines, inconsistent spacing, PDF extraction artifacts) will produce poor paragraph detection. PDF-to-text conversion is notorious for destroying paragraph structure.
  • No awareness of higher structure: Section headers, lists, and tables within a paragraph are not treated specially. A paragraph containing a bulleted list is still one chunk.

When to Use It

Paragraph-aware chunking is ideal for structured documents with clear sections: technical reports, policy documents, manuals, legal briefs with well-formed paragraphs, and any prose where the author's paragraph structure reflects topical organization. It is strictly better than sentence-aware chunking when paragraphs are clean and consistently formatted. It is the recommended strategy for most general-purpose RAG deployments that handle prose documents and do not want the complexity of semantic or document-aware chunking.

Recursive Chunking

Recursive chunking is the strategy popularized by LangChain's RecursiveCharacterTextSplitter and it is the default most practitioners should reach for. The idea is elegant: define a hierarchy of separators, from most-structural to least-structural, and try each in turn. First, try splitting by paragraphs. If the resulting chunks fit within the target size, you are done. If a chunk is still too large, recursively apply the next separator (sentences) to just that chunk. If still too large, try words. If still too large, try characters. The splitter descends through the hierarchy until every chunk fits.

The Separator Hierarchy

The default hierarchy in LangChain for plain text is:

# LangChain RecursiveCharacterTextSplitter default separators separators = [ "\n\n", # paragraph break (preferred) "\n", # line break " ", # space (word boundary) "" # character (last resort) ] # For Markdown, a better hierarchy: md_separators = [ "\n## ", # H2 header "\n### ", # H3 header "\n#### ", # H4 header "\n\n", # paragraph "\n", # line " ", # word "" # character ]

The algorithm tries the first separator. If any resulting piece exceeds the chunk size, it recursively applies the second separator to just that piece. This continues down the hierarchy. The result is chunks that respect the highest available structural boundary that still fits within the size limit. A document with clean paragraph breaks produces paragraph-aligned chunks. A document with one giant paragraph gets sentence-split. A document with one giant sentence gets word-split. The splitter adapts to whatever structure the document actually has.

How It Works in Practice

# LangChain RecursiveCharacterTextSplitter usage from langchain.text_splitter import RecursiveCharacterTextSplitter splitter = RecursiveCharacterTextSplitter( chunk_size=512, # target size in characters chunk_overlap=64, # overlap between chunks separators=["\n\n", "\n", " ", ""], length_function=len, ) chunks = splitter.split_text(document_text) # For Markdown documents with structure awareness: md_splitter = RecursiveCharacterTextSplitter.from_language( language=Language.MARKDOWN, chunk_size=512, chunk_overlap=64, )

Advantages

  • Adaptive: Automatically respects whatever structure the document has. No need to detect document type and pick a strategy manually.
  • Bounded chunk size: Every chunk fits within the target. No surprise 5000-token chunks from an oversized paragraph.
  • Best structural boundary used: Chunks use paragraph boundaries where possible, sentence boundaries where needed, and character boundaries only as a last resort.
  • Fast: Pure string operations. No model inference. Millions of documents per hour on CPU.
  • Framework support: Available in LangChain, LlamaIndex, and most RAG frameworks out of the box. No custom code needed.

Disadvantages

  • Still structure-blind at the semantic level: The splitter knows about paragraphs and sentences, not about topics. Two sentences in the same paragraph about different subtopics stay in the same chunk.
  • Character-count based, not token-count based: By default, chunk_size is in characters, not tokens. 512 characters is roughly 100-130 tokens for English, not 512 tokens. You must pass a token-based length function or use a tokenizer-aware splitter to get true token-bounded chunks.
  • Separator choice matters: The default separators work for clean text. For code, Markdown, HTML, or legal documents with unusual formatting, you need custom separators or the result is poor.

When to Use It

Recursive chunking is the best default for mixed-format document collections. If you are building a RAG system over a knowledge base containing PDFs, Markdown files, plain text, and Word documents, recursive chunking handles all of them without per-document-type tuning. It is the strategy most production RAG systems should start with before considering any of the more advanced approaches below. The cost-to-quality ratio is exceptionally good: near-zero compute cost, substantial improvement over fixed-size, and it degrades gracefully on messy documents.

Semantic Chunking

Semantic chunking uses embeddings to detect topic shifts and splits at those boundaries. The insight: a document is not a linear sequence of paragraphs -- it is a sequence of topics, and topic boundaries do not always align with paragraph breaks. By embedding each sentence (or small unit) and measuring the semantic distance between consecutive units, the splitter can identify where the topic changes and cut there. The resulting chunks are topically coherent: each chunk discusses one topic, not a mix of two.

The Algorithm

  1. Split into sentences: Use a sentence tokenizer to break the document into individual sentences.
  2. Embed each sentence: Run each sentence through the embedding model to get a vector.
  3. Compute consecutive distances: For each pair of adjacent sentences, compute the cosine distance between their embeddings.
  4. Detect breakpoints: Wherever the distance between sentence K and sentence K+1 is significantly larger than the local average (a "semantic jump"), mark a chunk boundary.
  5. Group sentences into chunks: Sentences between breakpoints form a chunk. Merge small chunks or split large ones to fit size constraints.
# Semantic chunking concept (simplified) from sentence_transformers import SentenceTransformer import numpy as np model = SentenceTransformer("all-MiniLM-L6-v2") # runs on CPU sentences = split_into_sentences(document_text) embeddings = model.encode(sentences, normalize_embeddings=True) # Cosine distance between consecutive sentences distances = [] for i in range(1, len(embeddings)): sim = np.dot(embeddings[i-1], embeddings[i]) # cosine sim (normalized) distances.append(1 - sim) # Detect breakpoints: distance exceeds mean + threshold * std threshold = np.mean(distances) + 1.5 * np.std(distances) breakpoints = [i for i, d in enumerate(distances) if d > threshold] # Group sentences between breakpoints into chunks chunks = [] start = 0 for bp in breakpoints: chunk = " ".join(sentences[start:bp+1]) chunks.append(chunk) start = bp + 1 chunks.append(" ".join(sentences[start:])) # Result: each chunk is a topically coherent passage. # Boundaries fall where the topic shifts, not at fixed intervals.

Variations

  • Percentile-based: Instead of mean+std, split at the 95th percentile of distances. Simpler, no distributional assumptions.
  • Windowed: Compare a sentence to the average of the previous N sentences, not just the immediate predecessor. Smoother breakpoint detection, less sensitive to single-sentence noise.
  • Cumulative: Track a running semantic embedding of the current chunk. When adding the next sentence would move the chunk's centroid beyond a threshold, start a new chunk.
  • LLM-augmented: Use the LLM to verify that a detected breakpoint is actually a topic change, not just a stylistic shift. Higher quality but adds LLM cost.

Advantages

  • Topically coherent chunks: Each chunk discusses one topic. Embeddings are focused. Retrieval precision improves measurably.
  • Structure-independent: Works even when document structure is messy or absent. Does not rely on paragraph breaks being meaningful.
  • Adapts to content: A document with long topical sections gets large chunks; a document with rapid topic changes gets small chunks. Chunk count is content-driven, not size-driven.
  • Better for long documents: Research papers, technical specs, and legal documents often have topic shifts that do not align with paragraph boundaries. Semantic chunking catches these.

Disadvantages

  • Requires an embedding model: You must run every sentence through an embedding model during chunking. For a 100K-token document, that is thousands of embedding calls. On CPU with all-MiniLM-L6-v2, this is seconds per document; on GPU, milliseconds. But it is not free.
  • Threshold tuning: The breakpoint threshold (mean + 1.5*std, or 95th percentile) is a tunable parameter. Too aggressive and you get tiny chunks; too conservative and you get one giant chunk. Needs calibration on your specific corpus.
  • Sensitive to embedding model quality: A weak embedding model produces noisy distances, leading to bad breakpoints. Use a model with good sentence-level semantic quality (all-mpnet-base-v2, bge-base-en-v1.5, or better).
  • No size guarantee: Semantic chunks can be very large (a long topically-coherent section) or very small. You still need a fallback size-based splitter to enforce min/max bounds.

When to Use It

Semantic chunking shines on long technical documents, research papers, legal contracts, and any prose where topics span multiple paragraphs or shift within them. It is the first strategy in this list that actually understands what the text is about, and the retrieval precision improvement over recursive chunking is often the difference between a RAG system that feels "pretty good" and one that feels "surprisingly accurate." For on-premise deployments, the embedding model runs locally (no API calls, no data egress), and the per-document cost is seconds of CPU time -- an excellent trade for better retrieval.

Document-Aware Chunking

Document-aware chunking respects the actual structure of the document: headers, sections, tables, code blocks, lists, footnotes, and captions. Instead of treating the document as a flat string of text, it parses the document's structure tree and uses that tree to define chunk boundaries. A Markdown H2 section becomes a chunk boundary. An HTML table stays together as one chunk (or is split at row boundaries if too large). A code block is never split mid-function. The result is chunks that match the document's authored structure.

Markdown Structure-Aware Splitting

Markdown is the easiest format for document-aware chunking because its structure is explicit in the syntax. Headers (#, ##, ###) define sections. Code fences (```) define code blocks. Tables have pipe-delimited rows. A Markdown-aware splitter walks the document's header tree and creates one chunk per section (or per subsection, depending on target size). The section's header is prepended to each chunk so the embedding and the LLM both know what section they are reading.

# Markdown-aware chunking (LangChain MarkdownHeaderTextSplitter) from langchain.text_splitter import MarkdownHeaderTextSplitter headers_to_split_on = [ ("#", "Header 1"), ("##", "Header 2"), ("###", "Header 3"), ] splitter = MarkdownHeaderTextSplitter(headers_to_split_on) md_chunks = splitter.split_text(markdown_document) # Each chunk has metadata: {"Header 1": "Installation", "Header 2": "Docker"} # The chunk text is the content under that header. # The header text is preserved, so the chunk reads naturally. # Then apply a size-based splitter to any oversized sections: from langchain.text_splitter import RecursiveCharacterTextSplitter size_splitter = RecursiveCharacterTextSplitter(chunk_size=512, chunk_overlap=64) final_chunks = size_splitter.split_documents(md_chunks)

HTML Structure-Aware Splitting

For HTML documents, an HTML-aware splitter parses the DOM tree and splits at block-level element boundaries (<div>, <section>, <article>, <table>, <pre>). It preserves table structure, does not split inside a <pre> block (code), and can carry tag-based metadata (e.g., "this chunk came from a <table>") into the vector database for filtered retrieval.

Code Structure-Aware Splitting

For source code, a code-aware splitter parses the abstract syntax tree (AST) and splits at function, class, and method boundaries. It never splits inside a function body. The chunk includes the function signature and docstring. LangChain's RecursiveCharacterTextSplitter.from_language() supports Python, JavaScript, TypeScript, Go, Rust, Java, C++, and other languages.

# Code-aware chunking (LangChain) from langchain.text_splitter import Language, RecursiveCharacterTextSplitter py_splitter = RecursiveCharacterTextSplitter.from_language( language=Language.PYTHON, chunk_size=512, chunk_overlap=64, ) # Splits at function/class boundaries. A function is never cut in half. # Each chunk is a complete function or class definition.

PDF Structure-Aware Splitting

PDFs are the hardest format for document-aware chunking because PDF structure is visual, not semantic. The PDF format specifies where glyphs appear on a page, not whether they are a header, body text, or caption. Tools like unstructured.io, PyMuPDF, pdfplumber, and marker attempt to reconstruct document structure from layout analysis: detecting headers by font size, identifying tables by ruled lines or alignment, and separating columns. The quality varies enormously by PDF type. Born-digital PDFs (exported from Word or LaTeX) preserve structure better than scanned PDFs (which require OCR first).

Advantages

  • Preserves authored structure: Chunks align with the document's actual sections, tables, and code blocks. The writer's intent is respected.
  • Metadata-rich: Each chunk can carry metadata like "section: Installation > Docker" or "type: table" or "language: Python." This enables metadata-filtered retrieval (only search chunks from the Installation section).
  • No broken tables or code: Tables stay together. Code blocks stay together. These are the elements most damaged by naive splitters.
  • Better LLM context: When a chunk includes its section header, the LLM knows what it is reading. "Docker Installation: Run the following command..." is far more useful than "Run the following command..." with no context.

Disadvantages

  • Format-specific: A Markdown splitter does not work on HTML. An HTML splitter does not work on PDFs. You need format-aware splitters for each document type in your corpus.
  • Parsing complexity: HTML parsers must handle malformed HTML. PDF structure extraction is an active research area and results are imperfect. Code parsers must handle every language and syntax variant.
  • Variable chunk sizes: A section may be one paragraph or twenty pages. You need a secondary size-based splitter to enforce bounds, and that secondary splitter may re-introduce some of the problems document-aware chunking solved.
  • More engineering: More code, more dependencies, more edge cases than any character-based splitter. The engineering investment pays off for structured corpora but is overkill for a pile of plain-text emails.

When to Use It

Document-aware chunking is the right choice for technical documentation, code repositories, API documentation, wikis, manuals, and any corpus where documents have meaningful structure. If your users will ask questions like "How do I configure the Docker installation?" and the document has a section literally titled "Docker Installation," document-aware chunking ensures that section is a single retrievable chunk -- not scattered across arbitrary 512-token windows. For on-premise deployments over internal documentation (API docs, runbooks, SOPs), this is often the highest-ROI chunking upgrade available.

Agentic Chunking

Agentic chunking is the most expensive and most intelligent approach: an LLM reads the document and decides the optimal chunk boundaries itself. Instead of applying a fixed algorithm, the LLM is prompted to analyze the document's content, identify natural topical and structural boundaries, and propose chunks that maximize each chunk's internal coherence and retrievability. This is the chunking strategy that most closely mimics what a human expert would do if asked to split a document for a Q&A system.

How It Works

The document is fed to the LLM with a prompt like: "You are a RAG chunking expert. Read the following document and split it into chunks optimized for retrieval. Each chunk should be topically coherent, self-contained enough to understand without surrounding context, and between 200 and 800 tokens. Output the chunks as a JSON array of strings." The LLM reads the document, identifies natural boundaries, and returns the chunks.

# Agentic chunking via LLM (conceptual) prompt = """ You are a document chunking specialist for a RAG system. Read the document below and split it into chunks that optimize retrieval quality. Each chunk must: 1. Be topically coherent (discuss one topic or subtopic) 2. Be self-contained (understandable without surrounding context) 3. Be between 200 and 800 tokens 4. Preserve critical context (section titles, table headers, variable definitions) within each chunk 5. Never cut a sentence, table, or code block in half Return the chunks as a JSON array of strings. DOCUMENT: {document_text} """ response = llm.generate(prompt.format(document_text=document_text)) chunks = json.loads(response) # The LLM reads the entire document and decides boundaries itself. # It can recognize that a table belongs with its preceding paragraph, # that a code example belongs with its explanation, and that a topic # shift happens mid-paragraph -- none of which rule-based splitters detect.

Variations

  • Single-pass: Feed the whole document, get all chunks back in one LLM call. Works for documents that fit in the context window. Cheapest agentic variant.
  • Sliding-window: For long documents, slide a window through the text and ask the LLM to mark boundaries within each window. Stitch boundaries across windows. Handles arbitrarily long documents.
  • Iterative refinement: First pass produces candidate chunks. Second pass asks the LLM to review and merge or split any chunks that are too small, too large, or not coherent. Higher quality, higher cost.
  • Proposition-based: Instead of asking the LLM to split, ask it to decompose the document into atomic propositions (self-contained factual statements). Each proposition becomes a chunk. Extremely precise retrieval, very high cost, very many chunks. This is the approach behind "Dense X Retrieval" (propositional chunking).

Advantages

  • Highest quality boundaries: The LLM understands what the text means and splits at semantically optimal points. It recognizes that a table belongs with its caption, that a definition belongs with the term it defines, and that an example belongs with the concept it illustrates.
  • Context-aware: The LLM can add context to each chunk, like prepending "From the section on Installation:" to a chunk that would otherwise lack context. This is similar to Anthropic's "Contextual Retrieval" technique.
  • Handles any format: The LLM does not care whether the input is Markdown, HTML, PDF-extracted text, or a messy OCR dump. It reads and understands the content regardless of format quality.
  • Self-contained chunks: The LLM can ensure each chunk is understandable on its own, which is exactly what the retriever needs (the retrieved chunk is presented to the LLM without its surrounding context).

Disadvantages

  • Very expensive: Every document requires one or more LLM calls during ingestion. For a 1000-document corpus with 128K-token documents, this is 1000 LLM calls at maximum context length. On a self-hosted 70B model, that is hours of GPU time. On an API, that is hundreds to thousands of dollars.
  • Slow: LLM inference is orders of magnitude slower than string operations or embedding-based splitting. Ingestion pipelines go from minutes to hours or days.
  • Non-deterministic: The LLM may produce different chunk boundaries on different runs. For systems that require reproducible ingestion (auditing, compliance), this is a problem. Fixing the temperature at 0 helps but does not guarantee identical output across model versions.
  • Schema reliability: The LLM may fail to produce valid JSON, may produce chunks outside the requested size range, or may hallucinate content not in the original document. You need validation and retry logic.
  • Requires a capable model: A small or weak LLM produces worse chunk boundaries than a good rule-based splitter. Agentic chunking only pays off with a strong model (70B+ parameters or a frontier API model).

When to Use It

Agentic chunking is reserved for high-value documents where retrieval accuracy is critical and the cost of a wrong answer (or a missed answer) is high: legal contracts in a law firm RAG system, clinical guidelines in a medical RAG system, regulatory documents in a compliance system, and executive briefings. It is also appropriate for small, high-value corpora (a few hundred critical documents) where the one-time ingestion cost is acceptable and the corpus does not change often. For large, frequently-updated corpora (millions of documents, daily ingestion), agentic chunking is usually prohibitively expensive.

On-Premise Cost Warning: Running agentic chunking with a 70B model on-premise requires significant GPU resources during ingestion. A single H100 can chunk roughly 100-200 documents per hour (depending on document length and model speed). Plan your ingestion throughput against your available GPU hours. If you need to re-chunk after a strategy change, the cost repeats. Consider caching chunk boundaries so documents are only chunked once.

Overlap & Sliding Windows

Overlap is the practice of repeating a small portion of each chunk at the beginning of the next chunk. If chunk K ends with the last 50 tokens of a paragraph, chunk K+1 begins with those same 50 tokens before continuing. This creates a sliding window where content near a boundary appears in both chunks, ensuring that information at the edge of a chunk is not lost from either side's context.

Why Overlap Matters

Consider a sentence that straddles a chunk boundary: "The effective date of this agreement is January 15, 2026, and it shall remain in force for..." -- chunk K ends at "January 15, 2026," and chunk K+1 starts at "and it shall remain in force for..." Without overlap, the embedding for chunk K captures "the effective date is January 15, 2026" but not the duration. The embedding for chunk K+1 captures "shall remain in force for [duration]" but not the date. A query about "When does the agreement start and how long does it last?" matches neither chunk strongly.

With 50-token overlap, chunk K+1 begins with the last 50 tokens of chunk K, including "The effective date of this agreement is January 15, 2026," followed by "and it shall remain in force for..." Now chunk K+1's embedding captures both the date and the duration. The query matches strongly. Overlap ensures that information near a boundary is represented in at least one complete chunk.

Typical Overlap Sizes

The standard recommendation is 10-20% of the chunk size. For a 512-token chunk, that is 50-100 tokens of overlap. For a 256-token chunk, 25-50 tokens. For a 1024-token chunk, 100-200 tokens. The trade-off:

  • More overlap: Better context preservation at boundaries. Fewer "lost in the gap" retrieval failures. But more storage (the same text is stored in two chunks), more embedding compute (overlapping text is embedded twice), and more redundant retrieval (the same passage may be returned twice in search results, requiring deduplication).
  • Less overlap: Less storage, less compute, less redundancy. But higher risk of boundary-spanning information being split across chunks and poorly represented in either.
  • Zero overlap: Maximum storage efficiency. Acceptable for sentence-aware and semantic chunking where boundaries are already natural (a sentence is a complete unit, so overlap is less necessary). Risky for fixed-size and recursive chunking where boundaries are arbitrary.
# Overlap visualization (512-token chunks, 64-token overlap) Document: [------- Chunk 1 (512) -------] [------- Chunk 2 (512) -------] [------- Chunk 3 (512) -------] # The last 64 tokens of Chunk 1 are the first 64 tokens of Chunk 2. # A sentence straddling the Chunk 1/2 boundary appears in both. # Storage cost: ~12.5% overhead (64/512). Retrieval benefit: high.
Overlap Is Less Critical with Good Boundaries: If your chunking strategy already splits at natural boundaries (sentence-aware, paragraph-aware, semantic, document-aware), overlap adds less value because chunks are already complete units. Overlap is most important for fixed-size and recursive chunking where boundaries are arbitrary. For semantic and agentic chunking, overlap can be reduced or eliminated with minimal quality loss.

Chunk Size Guidance

Chunk size is the single most important parameter in any chunking strategy, and it is not one-size-fits-all. The right size depends on what you are optimizing for: retrieval precision (small chunks), generation context (large chunks), or a balance of both. Here is the guidance, from small to large.

256 Tokens -- Precise Retrieval

Small chunks produce focused embeddings. A 256-token chunk covers roughly one paragraph or a few sentences. The embedding captures a specific, narrow meaning. When the user asks a specific question ("What is the penalty for late payment under Section 4?"), the retriever can pull the exact paragraph that answers it, without surrounding noise. The LLM gets a precise, relevant context.

The trade-off: small chunks may lack the context the LLM needs to understand the retrieved passage. A chunk that says "The penalty is 1.5% per month" is only useful if the LLM knows it is about late payments under Section 4 -- which may require context from a chunk that was not retrieved. Small chunks work best when each chunk is self-contained (agentic or document-aware chunking with section headers prepended) and when you retrieve multiple chunks (top-k=5 or more) to provide surrounding context.

512 Tokens -- Balanced

512 tokens is the most commonly recommended default and for good reason. It is roughly 350-400 words, about one to two paragraphs. Large enough to contain a complete idea with its surrounding context (the topic sentence, the explanation, and the conclusion). Small enough that the embedding is focused and the retriever can pinpoint relevant passages without too much noise. For most RAG systems handling prose documents, 512 tokens with 50-100 tokens of overlap is the sweet spot.

1024 Tokens -- More Context Per Chunk

Larger chunks carry more context per retrieval. A 1024-token chunk might contain an entire section of a document, including background, main point, and examples. When retrieved, the LLM gets enough context to understand the passage without needing surrounding chunks. This reduces the need for high top-k retrieval (you can retrieve fewer chunks and still have enough context).

The trade-off: larger chunks have more diffuse embeddings. A 1024-token chunk covering two subtopics will match queries about either subtopic weakly, rather than one subtopic strongly. Retrieval precision drops. Larger chunks also mean fewer chunks per document, which means coarser retrieval granularity -- you cannot retrieve just the relevant paragraph, you retrieve the whole section it sits in.

2048+ Tokens -- Long Context Models

With modern LLMs supporting 128K+ token context windows, there is a temptation to use very large chunks (2048, 4096, or even 8192 tokens). The argument: if the LLM can handle long context, give it long context. This works when the document is highly topically focused (a single long technical section that should not be split) and when the embedding model can represent long passages meaningfully (some embedding models degrade on very long inputs -- check your model's optimal input length).

The trade-off is severe: very large chunks have very diffuse embeddings, retrieval precision drops significantly, and you are feeding the LLM a lot of potentially-irrelevant context (which hurts accuracy via attention dilution and increases cost). Use 2048+ token chunks only when the document's structure demands it (a single long section that cannot be meaningfully split) and combine with a re-ranking step to ensure the retrieved large chunk is actually relevant.

Chunk Size ~Word Count Retrieval Precision Context Per Chunk Best For
256 tokens ~180 words Low Factoid Q&A, specific clause lookup, self-contained chunks
512 tokens ~375 words Balanced General-purpose RAG, most prose documents
1024 tokens ~750 words Moderate High Technical docs, multi-paragraph reasoning, low top-k retrieval
2048+ tokens ~1500+ words Very High Long-context models, highly focused sections, with re-ranking
🎯 The Size-Quality Relationship: Chunk size controls the trade-off between retrieval precision and generation context. Smaller chunks = more precise retrieval, less context per retrieval. Larger chunks = less precise retrieval, more context per retrieval. You can compensate for small chunks by retrieving more of them (higher top-k). You can compensate for large chunks by adding a re-ranker. You cannot compensate for a bad chunk size by changing anything else -- it is a foundational parameter that affects every downstream stage. Benchmark 256, 512, and 1024 on your specific corpus and queries before committing.

Multi-Modal Chunking

RAG systems are increasingly multi-modal: users ask questions about audio recordings, video footage, and images, not just text. Chunking strategies for these modalities differ fundamentally from text chunking because the "natural boundaries" are different. You cannot split an audio file at "sentence boundaries" the same way you split text -- you split at silence, speaker changes, or topic shifts detected from a transcript. This section covers the major multi-modal chunking approaches. For a deeper treatment, see our multi-modal RAG page.

Audio Chunking

Audio (meeting recordings, interviews, podcasts, lectures) is chunked by temporal boundaries that align with natural pauses or speaker changes:

  • Silence-based chunking: Detect silence (segments where audio energy drops below a threshold for more than N milliseconds) and split there. This produces chunks aligned with natural conversational pauses. Tools: pydub, librosa, webrtcvad (Voice Activity Detection).
  • Speaker diarization: Identify speaker changes and split at each speaker turn. Each chunk is one speaker's continuous speech. This is critical for interviews and meetings where "who said what" matters. Tools: pyannote.audio, whisper-x with diarization, resemblyzer.
  • Fixed-duration chunking: Split every N seconds (e.g., 30 seconds). Simple, predictable, and works when silence detection is unreliable (noisy recordings). The trade-off is that chunks may cut mid-sentence or mid-word.
  • Transcript-then-chunk: Transcribe the audio first (using Whisper or another ASR model), then apply text chunking strategies to the transcript. Each audio chunk is the audio segment corresponding to a text chunk. This is the most common approach in production because it lets you reuse all the text chunking infrastructure.
# Audio chunking via transcript-then-chunk (conceptual) # 1. Transcribe audio with word-level timestamps transcript = whisper_model.transcribe("meeting.wav", word_timestamps=True) # 2. Build text from transcript with timestamp anchors text_with_times = [(w.text, w.start, w.end) for w in transcript.words] full_text = " ".join(w[0] for w in text_with_times) # 3. Apply text chunking (e.g., recursive, 512 tokens, 64 overlap) text_chunks = recursive_split(full_text, chunk_size=512, overlap=64) # 4. For each text chunk, find the corresponding audio segment for chunk in text_chunks: start_time = find_timestamp_for_char_offset(chunk.start_index) end_time = find_timestamp_for_char_offset(chunk.end_index) audio_segment = extract_audio("meeting.wav", start_time, end_time) # Store: text chunk (embedded for retrieval) + audio segment (returned to user)

Video Chunking

Video combines visual and audio tracks. Chunking must consider both:

  • Scene-based chunking: Detect scene changes (significant visual changes between frames) using shot boundary detection. Each chunk is one scene. Tools: PySceneDetect, OpenCV with histogram differencing.
  • Frame + audio chunking: Split the video at scene boundaries, then within each scene, split the audio track using silence or speaker diarization. Each final chunk is a scene-segment with aligned audio.
  • Fixed-duration with keyframe extraction: Split every N seconds, extract one keyframe per chunk, caption the keyframe with a vision model, and embed the caption + transcript. This is the simplest multi-modal video chunking approach.
  • Caption-based chunking: If the video has captions or a transcript, apply text chunking to the transcript and align each text chunk with its video segment (same as audio transcript-then-chunk, extended to video).

Image Chunking

Images do not have natural sequential boundaries like text or audio. Chunking an image means deciding what constitutes a retrievable unit:

  • Whole-image embedding: Embed the entire image as one chunk. Simple, works for standalone images (photos, diagrams). Tools: CLIP, SigLIP, OpenCLIP (all run on-premise).
  • Region-based chunking: Split the image into a grid (e.g., 3x3 or 4x4) and embed each region. Useful for large images with distinct regions (a dashboard screenshot, an annotated diagram). Each region is a retrievable chunk.
  • Caption-based chunking: Generate a text caption with a vision-language model (LLaVA, Qwen-VL,CogVLM), then embed the caption as a text chunk. The image is retrieved via its caption. This bridges vision and text retrieval.
  • Object-detection-based chunking: Run object detection (YOLO, DETR), crop each detected object, and embed each crop as a chunk. Useful for images with multiple distinct objects (a product catalog, a medical scan with multiple findings).
  • Document-image chunking: For scanned documents, run OCR and layout analysis (e.g., LayoutLM, Donut, Nougat), then chunk based on detected layout regions (headers, paragraphs, tables, figures). Each region becomes a chunk with both text and image representation.
Multi-Modal Chunking Connects to the Multi-Modal RAG Page: Multi-modal chunking is a deep topic with its own trade-offs, tools, and production patterns. This section provides the overview. For the full treatment -- including embedding strategies for multi-modal chunks, cross-modal retrieval, and on-premise model selection -- see our multi-modal RAG page (coming soon).

Practical Recommendations by Document Type

Different document types have different structures, and the best chunking strategy depends on the document. Here are concrete recommendations for common document types in enterprise RAG systems.

Legal Contracts

Recommended: Document-aware chunking by section/clause, with 512-token chunks and 64-token overlap. Legal contracts are highly structured: numbered sections, lettered subsections, defined terms. Split at section boundaries. Prepend the section number and title to each chunk ("Section 4.2: Termination for Convenience"). Do not split a clause across chunks -- a single clause should be one chunk, even if it exceeds the target size. For high-value contracts (M&A agreements, IP licenses), upgrade to agentic chunking so the LLM can recognize that a defined term in Section 12 refers back to the definition in Section 1 and ensure that context is preserved.

Medical Records (EHR)

Recommended: Document-aware chunking by encounter/section, with 256-512 token chunks and 50-token overlap. Medical records are structured by encounter (visit, lab result, imaging report, medication change). Split at encounter boundaries. Each chunk should carry metadata: patient ID (or de-identified ID), encounter type, date, provider. Never split a single encounter across chunks. For clinical notes with narrative sections (HPI, Assessment, Plan), use paragraph-aware chunking within the encounter. For on-premise deployments in HIPAA-regulated environments, all chunking must happen on-premise with no API calls -- use local spaCy + document-aware splitters, not cloud-based agentic chunking.

Source Code

Recommended: Code-aware AST-based chunking by function/class/method, with 512-1024 token chunks and zero overlap (code units do not benefit from overlap the way prose does). Each chunk should include the function signature, docstring, and full body. Never split inside a function. For files with multiple small functions, group related functions (same class, same module) into one chunk if they fit. Carry metadata: file path, language, class name, function name. This enables queries like "show me the authentication logic" to retrieve the relevant function, not a random 512-token slice that might contain half a function.

Technical Manuals & Documentation

Recommended: Document-aware (Markdown/HTML) chunking by section, with 512-token chunks and 64-token overlap. Manuals are structured by chapters and sections. Split at header boundaries. Prepend the section breadcrumb ("Installation > Docker > Configuration") to each chunk. Keep code examples together with their explanation -- do not split the "Run this command:" sentence from the command itself. For API documentation, keep the endpoint definition, parameters, and example response as one chunk.

Emails

Recommended: One email per chunk, or paragraph-aware chunking for long emails, with 256-512 token chunks. Most emails are short enough to be a single chunk. The email metadata (sender, recipient, date, subject) should be prepended to the chunk text so the embedding and the LLM both have context. For long email threads, split at message boundaries (each email in the thread is a chunk) and carry the thread subject as metadata. Overlap is usually unnecessary since each email is a natural unit.

Audio Recordings (Meetings, Interviews)

Recommended: Transcript-then-chunk approach. Transcribe with a local Whisper model (on-premise for confidentiality), then apply semantic or sentence-aware chunking to the transcript. Each chunk is linked to its audio segment via timestamps. For meetings, split at speaker/turn boundaries and carry speaker labels as metadata. For interviews, keep each question-answer pair as one chunk if it fits the size limit. Chunk size: 512 tokens of transcript, 64-token overlap. Store both the text embedding (for retrieval) and the audio segment (for playback to the user).

Video (Training Videos, Webinars, Surveillance)

Recommended: Scene-based chunking with transcript alignment. Detect scene boundaries, extract keyframes, caption keyframes with a local vision-language model (LLaVA, Qwen-VL), transcribe audio with Whisper, and apply semantic chunking to the transcript. Each chunk has: text (transcript segment), image (keyframe), and timestamps. Embed the text for retrieval. For training videos, chunk at topic boundaries (often aligns with slide changes). For surveillance, chunk by minute or by event detection (motion, person appearance).

Document Type Recommended Strategy Chunk Size Overlap Key Metadata
Legal Contracts Document-aware (by clause/section) 512 tokens 64 tokens Section number, clause type, document title
Medical Records Document-aware (by encounter) 256-512 tokens 50 tokens Patient ID, encounter type, date, provider
Source Code Code-aware (AST, by function) 512-1024 tokens 0 File path, language, class, function name
Technical Manuals Document-aware (Markdown/HTML) 512 tokens 64 tokens Section breadcrumb, document title
Emails One-per-chunk / paragraph-aware 256-512 tokens 0-32 tokens Sender, recipient, date, subject, thread ID
Audio Recordings Transcript-then-chunk + semantic 512 tokens (transcript) 64 tokens Speaker, timestamp, audio file offset
Video Scene-based + transcript-then-chunk 512 tokens (transcript) 64 tokens Scene ID, keyframe, timestamp, duration
🎯 The Universal Starting Point: If you are unsure, start with recursive chunking at 512 tokens with 64-token overlap. This handles 80% of document types reasonably well. Measure retrieval quality (recall@k and answer faithfulness) on a test set of 50-100 queries. Then upgrade to document-aware chunking if your documents have exploitable structure, or semantic chunking if they are long prose. Re-benchmark after each change. Never deploy a chunking strategy change without measuring its effect on retrieval quality -- the improvement (or regression) is often counterintuitive.

Comparison Table

The following table summarizes every chunking strategy covered in this guide, ranked roughly from simplest to most sophisticated. Use it as a quick reference when deciding which strategy to evaluate for your use case.

Strategy Complexity Context Preservation Retrieval Precision Best For Tool / Library
Fixed-Size Very Low Low Uniform logs, prototyping, baseline Native Python, LangChain CharacterTextSplitter
Sentence-Aware Low Fair (complete sentences) Fair Articles, reports, narrative prose spaCy, NLTK, pySBD, Stanza
Paragraph-Aware Low Good (paragraph boundaries) Good Structured prose, manuals, reports Native Python, LlamaIndex SentenceSplitter
Recursive Low-Moderate Good (best available boundary) Good Mixed-format corpora, general default LangChain RecursiveCharacterTextSplitter
Semantic Moderate Long technical docs, research papers LangChain SemanticChunker, LlamaIndex SemanticSplitter, custom
Document-Aware Moderate-High High Technical docs, code, Markdown, HTML LangChain MarkdownHeaderTextSplitter, HTMLSplitter, unstructured.io
Agentic High-value docs, critical retrieval accuracy Custom (LLM + prompt engineering), proposition-based systems

How to Read This Table

  • Complexity includes implementation effort, dependencies, and compute cost. Fixed-size is a few lines of Python with no dependencies. Agentic requires an LLM in the loop and prompt engineering.
  • Context preservation measures how well the strategy maintains the meaning and surrounding context of the original text. Fixed-size cuts sentences in half (poor). Agentic keeps every chunk self-contained (excellent).
  • Retrieval precision measures how well chunks support accurate retrieval -- do the chunks match the right queries? Semantic and agentic chunking produce topically focused embeddings that match precise queries strongly.
  • Best for is a guideline, not a rule. Many document types benefit from multiple strategies (e.g., document-aware to identify sections, then semantic within long sections).
  • Tool / Library lists production-ready implementations. For on-premise sovereign deployments, verify that the tool runs locally without API calls.
Combining Strategies: These strategies are not mutually exclusive. The best production systems often combine them: document-aware chunking to split a Markdown file by sections, then recursive chunking to split any oversized section, then semantic chunking within long sections to identify topic shifts. Or: semantic chunking to find topic boundaries, then a size-based splitter to enforce bounds. Or: agentic chunking for the 100 most critical documents, recursive chunking for the other 10,000. Treat the strategies as building blocks, not alternatives.

References & Further Reading

Source Attribution

Source: Avondale.AI Technical Documentation Library
Category: RAG Engineering
Type: Technical Reference Guide
License: CC BY 4.0

Foundational Papers & Articles

  • Lewis et al. (2020). "Retrieval-Augmented Generation for Knowledge-Intensive NLP Tasks." arXiv:2005.11401 -- The original RAG paper.
  • Karpukhin et al. (2020). "Dense Passage Retrieval for Open-Domain Question Answering." arXiv:2004.04906 -- DPR, showing that passage-level retrieval outperforms document-level.
  • Anthropic (2024). "Contextual Retrieval." anthropic.com/news/contextual-retrieval -- Adding LLM-generated context to chunks before embedding to improve retrieval by up to 49%.
  • Xu et al. (2024). "Dense X Retrieval: What Retrieval Granularity Should We Use?" arXiv:2402.01669 -- Proposition-level chunking, decomposing documents into atomic factual statements.
  • Sarma et al. (2024). "Simple Semantic Search: Semantic Chunking for RAG." -- Using embedding-based similarity for chunk boundary detection.
  • Liu et al. (2024). "Lost in the Middle: How Language Models Use Long Contexts." arXiv:2307.03172 -- Why long-context retrieval degrades and chunking plus targeted retrieval remains superior.
  • Gao et al. (2024). "Retrieval-Augmented Generation for Large Language Models: A Survey." arXiv:2312.10997 -- Comprehensive RAG survey covering chunking and retrieval stages.

Tools & Libraries

Further Reading

Suggested Citation:
Chunking Strategies for RAG: Complete Guide to Splitting Documents for Retrieval. Avondale.AI Technical Documentation Library. Accessed June 2026. https://avondale.ai/technical/rag/chunking-strategies.html