Chunking for RAG
RAG retrieves chunks — but where do chunks come from? You have to split documents first, and how you split is one of the highest-leverage decisions in a RAG system. Too big dilutes relevance, too small loses context. The strategies, the size-and-overlap trade-off, and why chunking is the unglamorous lever that quietly makes or breaks retrieval.
What you'll learn
- Why chunking exists and why it's a high-leverage RAG decision
- The chunk-size trade-off — dilution vs lost context
- Chunking strategies — fixed, overlap, sentence/semantic, structure-aware
- Parent-document retrieval and matching chunks to your queries
Before you start
RAG retrieves “chunks” — but a document isn’t born as chunks. Before anything can be embedded and indexed, you have to split documents into pieces, and how you split is one of the highest-leverage, most underrated decisions in a RAG system. Get it wrong and even a great embedding model retrieves the wrong thing.
How chunking changes what gets retrieved
The document below is split four ways. Pick a strategy to see the chunk boundaries; the highlighted chunk is what retrieval returns for the sample query. Notice when fixed-size splitting breaks the answer across two chunks.
Split every 256 tokens regardless of sentence or paragraph boundaries.
Northwind Cloud is a managed data platform that runs entirely on your existing cloud provider account. You bring the infrastructure; we bring the orchestration layer.
To get started, install the northwind CLI and run northwind init. The wizard will ask for your AWS or GCP credentials, create the necessary IAM roles, and provision a small control-plane cluster.
The platform currently supports three compute engines: Spark for batch workloads, Flink for streaming, and DuckDB for ad-hoc OLAP queries under 100 GB. All three share the same catalog and storage layer.
Data security is handled at two levels. At rest, all tables are encrypted using AES-256-GCM with keys you manage in AWS KMS or GCP Cloud KMS — Northwind never sees your encryption keys.
retrievedIn transit, all connections use mutual TLS 1.3. You can optionally enable private link so that traffic never leaves your cloud provider's backbone network.
Pricing is based on compute hours consumed, not data volume. Spark and Flink jobs are billed at $0.08 per vCPU-hour; DuckDB queries are metered at $0.004 per GB scanned, with the first 100 GB per month free.
Support tiers: Community (forum only), Developer ($299/mo, 8-hour response), and Enterprise (24/7, one-hour SLA for P1 incidents, dedicated Slack channel).
Northwind is open-core: the control plane and catalog are source-available under the Business Source License. The compute adapters and storage connectors are fully open-source under Apache 2.0.
The chunk-size trade-off
Everything hinges on one tension:
- Chunks too big — the one relevant sentence is diluted by surrounding irrelevant text, so its embedding is a muddy average and retrieval precision drops (and you waste context-window space feeding the model padding).
- Chunks too small — a chunk loses the context needed to understand or answer (a pronoun whose antecedent is in the previous sentence, a number whose units are in the heading), so even a correct retrieval is useless.
The basic mechanics are a window size and an overlap that carries context across boundaries:
doc = "RAG retrieves relevant chunks then the LLM generates a grounded answer from them".split()
def chunk(words, size, overlap):
step = size - overlap
return [" ".join(words[i:i+size]) for i in range(0, len(words), step) if words[i:i+size]]
for size, overlap in [(4, 0), (6, 2)]:
cs = chunk(doc, size, overlap)
print(f"size={size} overlap={overlap}: {len(cs)} chunks")
for c in cs:
print(f" | {c}")
size=4 overlap=0: 4 chunks
| RAG retrieves relevant chunks
| then the LLM generates
| a grounded answer from
| them
size=6 overlap=2: 4 chunks
| RAG retrieves relevant chunks then the
| then the LLM generates a grounded
| a grounded answer from them
| them
With no overlap, “generates a grounded answer” is split across two chunks — neither alone says what’s generated. The overlap=2 version repeats “then the” / “a grounded” across boundaries, so a sentence straddling a cut still lands intact in some chunk. Overlap is cheap insurance against splitting a thought in half.
Strategies, from naive to smart
- Fixed-size — split every N tokens. Simple and fast, but blindly cuts mid-sentence; almost always paired with overlap.
- Sentence / paragraph-aware — split on natural boundaries so chunks are coherent units.
- Structure-aware — respect the document’s own structure: Markdown headings, sections, code blocks, table rows. Keeps semantically-whole units together.
- Semantic chunking — split where the topic shifts (detect drops in sentence-to-sentence similarity), so each chunk is about one thing.
- Parent-document retrieval — index small chunks for precise matching, but return the larger parent (section/page) to the LLM, getting precision and context.
In one breath
- Chunking splits documents before embedding/indexing, and how you split is a top RAG-quality lever — a great embedder can’t fix bad chunks.
- The size trade-off: too big dilutes the relevant sentence (muddy embedding, wasted context); too small strips the context a chunk needs to be understood.
- Overlap carries context across boundaries so a thought split by a cut still lands whole in some
chunk (the demo: overlap repeats
then theacross the seam). - Strategies: fixed-size + overlap, sentence/paragraph, structure-aware (headings/code), semantic (split on topic shift), and parent-document (index small, return the parent).
- There’s no universal best — match chunking to your documents and queries, attach metadata, and measure it with RAG evals rather than guessing.
Quick check
Quick check
Next
Chunks become vectors via sentence embeddings stored in a vector database; the retrieval pipeline is RAG basics and advanced RAG, and you tune chunking by measuring with RAG evals.