Chunking Strategies for RAG

Chunking strategies for Retrieval-Augmented Generation (RAG) are the methods by which a corpus of documents is partitioned into smaller, semantically coherent units before being embedded and stored in a vector database, so that the most relevant units can be retrieved at query time. The strategy chosen profoundly affects retrieval precision, context utilization, and answer quality — making it one of the highest-leverage engineering decisions in any RAG pipeline. A counterintuitive fact: smaller chunks are not always better; chunk size interacts with the embedding model's training distribution in ways that can cause retrieval to degrade when chunks become too granular, even as semantic coherence appears to improve.

The core insight

Chunking is fundamentally about creating retrieval units whose granularity matches the semantic density of queries: a chunk must be large enough to contain a complete, self-sufficient thought, yet small enough that irrelevant surrounding text does not dilute the embedding vector away from the query's intent. The "aha moment" is recognizing that chunking is not a preprocessing detail — it defines the unit of meaning in your retrieval index, and every downstream metric (faithfulness, answer relevance, hallucination rate) is directly constrained by how well those units align with what users actually ask. Choosing a chunking strategy is therefore equivalent to choosing the resolution of a microscope: the wrong resolution makes the target invisible.

How It Works

Formal Problem Setup

Let a corpus D\mathcal{D} consist of NN documents. A chunking function χ\chi maps each document did_i to an ordered sequence of text spans:

1
χ(di)=⟨ci,1, ci,2, …, ci,Ki⟩,ci,k⊆di\quad \chi(d_i) = \langle c_{i,1},\, c_{i,2},\, \ldots,\, c_{i,K_i} \rangle, \quad c_{i,k} \subseteq d_i

Explanation: Each document is decomposed into an ordered list of non-overlapping (or partially overlapping) text chunks, where the total number of chunks KiK_i depends on both the document length and the chosen strategy. Each chunk is then encoded by an embedding model ϕ:T→Rd\phi : \mathcal{T} \to \mathbb{R}^d mapping text to a dd-dimensional dense vector:

2
vi,k=ϕ(ci,k)∈Rd\quad \mathbf{v}_{i,k} = \phi(c_{i,k}) \in \mathbb{R}^d

Explanation: The embedding function projects the discrete token sequence of a chunk into a continuous vector space where geometric proximity approximates semantic similarity. At query time, a user query qq is embedded and the top-rr chunks are retrieved by approximate nearest neighbor search using cosine similarity:

3
sim(q,ci,k)=ϕ(q)⊤vi,k∥ϕ(q)∥ ∥vi,k∥\quad \text{sim}(q, c_{i,k}) = \frac{\phi(q)^\top \mathbf{v}_{i,k}}{\|\phi(q)\|\,\|\mathbf{v}_{i,k}\|}

Explanation: Cosine similarity measures the angle between two unit-normalized vectors; chunks whose embedding direction aligns most closely with the query embedding are considered most relevant. The core trade-off can be framed as a bias–variance decomposition of retrieval quality. Let ℓ\ell denote average chunk length in tokens. Retrieval precision P(ℓ)P(\ell) and recall R(ℓ)R(\ell) trade off with chunk size:

4
Lretrieval(ℓ)=α⋅(1−P(ℓ))⏟noise from large chunks+(1−α)⋅(1−R(ℓ))⏟fragmentation from small chunks\quad \mathcal{L}_{\text{retrieval}}(\ell) = \alpha \cdot \underbrace{(1 - P(\ell))}_{\text{noise from large chunks}} + (1-\alpha) \cdot \underbrace{(1 - R(\ell))}_{\text{fragmentation from small chunks}}

Explanation: This loss captures the fundamental tension — larger chunks reduce recall loss (the answer is more likely present) but increase precision loss (irrelevant context pollutes the embedding); α\alpha is a task-specific weight. For overlapping chunks with stride ss and window ww (both in tokens), the overlap ratio ρ\rho determines context continuity:

5
ρ=w−sw=1−sw\quad \rho = \frac{w - s}{w} = 1 - \frac{s}{w}

Explanation: When ρ=0\rho = 0 there is no overlap (pure partitioning); as ρ→1\rho \to 1 the stride approaches zero and adjacent chunks are nearly identical, maximizing context continuity at the cost of index size. ---

Strategy Taxonomy

#### 1. Fixed-Size Chunking The simplest strategy: split every document into windows of exactly ww tokens with stride ss. ```python from transformers import AutoTokenizer tokenizer = AutoTokenizer.from_pretrained("sentence-transformers/all-MiniLM-L6-v2") def fixed_size_chunks(text: str, window: int = 256, stride: int = 32) -> list[str]: """ Tokenize text and produce overlapping fixed-size chunks. window: chunk size in tokens stride: number of tokens to advance between chunks """ token_ids = tokenizer.encode(text, add_special_tokens=False) chunks = [] for start in range(0, len(token_ids), stride): end = start + window chunk_ids = token_ids[start:end] if len(chunk_ids) < window // 4: # discard tail slivers break chunks.append(tokenizer.decode(chunk_ids)) if end >= len(token_ids): break return chunks ```

Pros: O(N)O(N) complexity, deterministic, easy to reason about index size. Cons: Splits sentences and paragraphs mid-thought; embedding quality degrades when semantic boundaries are violated. --- #### 2. Recursive Character / Structure-Aware Chunking Splits on a priority hierarchy of delimiters: `["\n\n", "\n", ". ", " ", ""]`. At each level, if a piece exceeds `max_size`, it is recursively split on the next delimiter. ```python from langchain.text_splitter import RecursiveCharacterTextSplitter splitter = RecursiveCharacterTextSplitter( chunk_size=512, # characters, not tokens chunk_overlap=64, separators=["\n\n", "\n", ". ", " ", ""], length_function=len, ) chunks = splitter.split_text(document_text) ``` This is the most widely used baseline in production RAG systems because it respects natural language boundaries without requiring document-format parsing. --- #### 3. Semantic Chunking Groups sentences together as long as their consecutive embedding similarity exceeds a threshold θ\theta, creating variable-length chunks whose boundaries coincide with genuine topic shifts. ```python import numpy as np from sentence_transformers import SentenceTransformer model = SentenceTransformer("all-MiniLM-L6-v2") def semantic_chunks(sentences: list[str], threshold: float = 0.85) -> list[str]: embeddings = model.encode(sentences, normalize_embeddings=True)

# cosine similarity between consecutive sentences sims = (embeddings[:-1] * embeddings[1:]).sum(axis=1) chunks, current = [], [sentences[0]] for i, sim in enumerate(sims): if sim >= threshold: current.append(sentences[i + 1]) else: chunks.append(" ".join(current)) current = [sentences[i + 1]] if current: chunks.append(" ".join(current)) return chunks ```

Pros: Chunk boundaries correspond to semantic shifts — much better embedding quality. Cons: Requires a full encoding pass over all sentences before chunking; O(N)O(N) embedding calls. --- #### 4. Document-Structure Chunking (Markdown / HTML-aware) Uses document hierarchy (headers, sections, list items) as natural chunk boundaries. Particularly effective for technical documentation, wikis, and legal documents with explicit section structure. ```python from langchain.text_splitter import MarkdownHeaderTextSplitter headers_to_split_on = [ ("#", "H1"), ("##", "H2"), ("###", "H3"), ] md_splitter = MarkdownHeaderTextSplitter( headers_to_split_on=headers_to_split_on, strip_headers=False, ) md_chunks = md_splitter.split_text(markdown_document)

# Each chunk carries metadata: {"H1": "Introduction", "H2": "Background"} ``` Metadata retention is a key advantage: each chunk knows its structural provenance, enabling metadata filtering at retrieval time. --- #### 5. Agentic / Proposition-Level Chunking Decomposes documents into atomic propositions — indivisible factual statements — using an LLM. Introduced by Chen et al. (2023) in the Dense X Retrieval paper, this produces the finest-grained retrieval units. ```python from openai import OpenAI client = OpenAI() def proposition_chunk(passage: str) -> list[str]: prompt = ( "Decompose the following passage into a list of atomic, " "self-contained factual propositions. Each proposition must be " "independently understandable. Return one proposition per line.\n\n" f"Passage: {passage}" ) response = client.chat.completions.create( model="gpt-4o", messages=[{"role": "user", "content": prompt}], temperature=0.0, ) return [line.strip() for line in response.choices[0].message.content.strip().split("\n") if line.strip()] ```

Pros: Maximum retrieval precision; no irrelevant co-text dilutes the embedding. Cons: LLM inference cost at index time; proposition count can explode for long corpora. --- #### 6. Hierarchical / Parent-Child Chunking Maintains two index levels: small child chunks for high-precision retrieval, large parent chunks (or full documents) for context-rich generation. After retrieval, child chunks are "expanded" to their parent before being passed to the LLM. ```python from langchain.retrievers import ParentDocumentRetriever from langchain.storage import InMemoryStore from langchain.vectorstores import Chroma from langchain.text_splitter import RecursiveCharacterTextSplitter from langchain.embeddings import HuggingFaceEmbeddings child_splitter = RecursiveCharacterTextSplitter(chunk_size=200) parent_splitter = RecursiveCharacterTextSplitter(chunk_size=2000) vectorstore = Chroma( embedding_function=HuggingFaceEmbeddings( model_name="sentence-transformers/all-MiniLM-L6-v2" ) ) docstore = InMemoryStore() retriever = ParentDocumentRetriever( vectorstore=vectorstore, docstore=docstore, child_splitter=child_splitter, parent_splitter=parent_splitter, ) ``` This strategy decouples retrieval granularity from generation context, directly addressing the precision–recall trade-off in Equation [4]. ---

researcher · Peter J. Liu | b. 1982 | Summarization and Long-Document NLP Foundations

Peter Liu, a research scientist at Google Brain, co-authored Text Summarization with Pretrained Encoders (2019) and was a key contributor to the T5 framework, both of which established the importance of document segmentation and passage-level representation for downstream tasks — laying conceptual groundwork for how modern RAG systems think about text units. His work on summarization demonstrated that the granularity of input segments directly controls what information a transformer attends to, a principle central to chunking design. Surprisingly, Liu's early career was in computational biology before he pivoted entirely to NLP research.

---

Figure
RAG Chunking Pipeline — Strategy Comparison and Data Flow
RAG Chunking Strategies — Data Flow Raw Document Corpus D Chunking χ(d) → ⟨c₁…cₖ⟩ Fixed-Size Recursive Semantic Hierarchical Proposition Embed φ(c) v ∈ ℝᵈ per chunk Vector DB FAISS / Chroma / Weaviate Top-k Retrieval sim(φ(q), v) LLM Generation Prompt + Retrieved Chunks Semantic-aware strategies Structural / fixed strategies

---

Figure
Chunk Size vs. Retrieval Quality Trade-off
Chunk Size vs. Retrieval Quality Trade-off Chunk Size (tokens) → Score → 64 128 256 512 1024 2048 0 0.4 0.7 0.9 1.0 Precision Recall F1 Sweet Zone Precision Recall F1 (composite)

---

Evaluation Framework

A rigorous chunking evaluation measures three complementary metrics:

MetricFormulaWhat It Measures
Context PrecisionTP/(TP+FP)\text{TP} / (\text{TP} + \text{FP})Are retrieved chunks actually relevant?
Context RecallTP/(TP+FN)\text{TP} / (\text{TP} + \text{FN})Are all relevant facts covered?
Answer FaithfulnessFraction of claims grounded in retrieved chunksDoes the LLM hallucinate?

The RAGAS framework (Es et al., 2023) provides automated LLM-based scoring for all three. ```python from ragas import evaluate from ragas.metrics import context_precision, context_recall, faithfulness from datasets import Dataset eval_data = Dataset.from_dict({ "question": questions, "answer": generated_answers, "contexts": retrieved_chunks, # list of list of str "ground_truth": reference_answers, }) results = evaluate( dataset=eval_data, metrics=[context_precision, context_recall, faithfulness], ) print(results.to_pandas()) ```

researcher · Patrick Lewis | b. 1993 | Retrieval-Augmented Generation

Patrick Lewis, a researcher at Meta AI and University College London, is the lead author of the landmark 2020 paper Retrieval-Augmented Generation for Knowledge-Intensive NLP Tasks, which first formalized the RAG paradigm and demonstrated that retrieved passages must be carefully sized to function as effective conditioning context for a seq2seq generator. His work implicitly demonstrated that the granularity of retrieved units controls the information density presented to the generative model — a finding that directly motivates modern chunking research. Lewis began his PhD studying formal semantics before pivoting to large-scale neural retrieval systems.

Intuition

The Mental Model

Imagine you are a research assistant asked to answer questions from a massive archive of academic papers. If your colleague hands you entire papers at a time (huge chunks), you spend most of your reading time on irrelevant content — the embedding vector for "the paper about transformer attention mechanisms" is too diluted to reliably surface when someone asks specifically about the role of softmax temperature. Conversely, if your colleague hands you only individual sentences (tiny chunks), you lose the surrounding context that gives each sentence its meaning: "The method outperforms all baselines" is meaningless without knowing which method and which baselines. The ideal is to receive self-contained paragraphs — units that are complete enough to answer a question on their own, but focused enough that their topic is unambiguous. Semantic chunking automates exactly this intuition: it keeps sentences together as long as they talk about the same thing, and cuts when the topic shifts. Hierarchical chunking goes one step further, giving the retrieval system your ideal paragraph for finding the answer, while giving the LLM the full section for generating it.

Historical Development

Origins · 1 minhow this was discovered
History
2017
Robertson & Zaragoza's BM25 retrieval system (originating from the 1994 Okapi BM25) uses fixed document passages as retrieval units — an early practical forcing function for passage-level indexing.
2019
Karpukhin et al. (Dense Passage Retrieval, DPR) at Facebook AI partition Wikipedia into 100-word non-overlapping passages, establishing token-count chunking as the de facto standard for open-domain QA retrieval benchmarks.
2020
Patrick Lewis et al. publish Retrieval-Augmented Generation for Knowledge-Intensive NLP Tasks (NeurIPS 2020), formalizing the RAG pipeline and making chunking a first-class engineering concern rather than a preprocessing afterthought.
2022
LangChain (Harrison Chase) releases the `RecursiveCharacterTextSplitter`, democratizing structure-aware chunking and making delimiter-hierarchy splitting the community default.
2023
Chen et al. (Propositionizing / Dense X Retrieval, arXiv 2312.06648) demonstrate that LLM-generated atomic propositions outperform all fixed-size and recursive strategies on retrieval benchmarks, introducing proposition-level chunking.
2023
Es et al. release RAGAS (arXiv 2309.15217), providing the first systematic evaluation framework for RAG pipelines including chunk-level context precision/recall metrics.
2023
LlamaIndex introduces the `SentenceWindowNodeParser` and `HierarchicalNodeParser`, popularizing parent-child chunking as a practical pattern for production RAG systems.