Document Chunking Strategies & Parsing
Garbage in, garbage out: Learn how intelligent text chunking and hierarchical structure-aware parsing maximize retrieval recall and eliminate vector dilution.
1. The Vector Dilution Dilemma
Why can't we simply embed an entire 30-page PDF document into a single vector? Because transformer embedding models (like text-embedding-3-small) compress all tokens into a fixed coordinate space (e.g., 1,536 floats).
The Problem: Semantic Averaging (Vector Dilution)
When a document spans multiple topics (e.g., quarterly revenue, legal liability, technical roadmap), its embedding becomes an averaged centroid of all those disparate topics. When a user asks a specific question ("What was the net loss in Q2?"), the distance to this diluted document vector is large, and retrieval fails completely.Chunking isolates atomic concepts into distinct searchable vectors.
2. Chunking Methodologies Compared
1. Recursive Character Splitting
The industry default. Recursively splits text using a hierarchy of separators: paragraphs (\n\n), then newlines (\n), spaces, and finally characters.
2. Markdown & Structure-Aware Splitting
Splits documents along structural headings (#, ##, ###). Crucially, heading titles are injected into every chunk's metadata for rich context.
3. Semantic Boundary Splitting
Embeds individual sentences and calculates the cosine distance between consecutive sentences. A split occurs only when semantic distance spikes above a percentile threshold.
4. Parent Document (Small-to-Big)
Splits text into small child chunks (200 tokens) linked to large parent chunks (1,500 tokens). Child chunks are searched; parent chunks are supplied to the LLM.
3. Tuning Chunk Size & Overlap
Two primary hyperparameters govern text splitting:
chunk_size: The maximum number of characters or tokens per chunk. Too small (e.g., 50 tokens) loses vital surrounding context; too large (e.g., 2,000 tokens) dilutes semantic precision.chunk_overlap: The number of overlapping characters/tokens between adjacent chunks (typically 10% to 20% ofchunk_size). Overlap ensures that a sentence split across a boundary does not lose its meaning.
| Target Content | Recommended Chunk Size | Overlap | Rationale |
|---|---|---|---|
| FAQ & Customer Support | 150 - 300 tokens | 30 tokens (10%) | Questions are short and atomic; tightly matched to single answers. |
| General Technical Documentation | 500 - 800 tokens | 100 tokens (15%) | Balances full code snippets and conceptual explanations. |
| Legal & Regulatory Contracts | 800 - 1,500 tokens | 200 tokens (20%) | Clauses depend heavily on preceding definitions and cross-references. |
4. Hands-On Python: Implementing Splitting Strategies
Here is a practical Python script demonstrating both recursive character splitting and markdown header parsing:
from langchain_text_splitters import (
RecursiveCharacterTextSplitter,
MarkdownHeaderTextSplitter
)
raw_markdown = """# Enterprise AI Architecture
## 1. Vector Database Selection
Vector databases provide approximate nearest neighbor search over dense embeddings.
Pinecone offers managed cloud scalability.
ChromaDB is ideal for embedded local workflows.
## 2. Ingestion Pipeline
Always sanitize and normalize text before computing embeddings.
Ensure chunk overlap is between 10% and 20% to avoid boundary context loss.
"""
# Strategy A: Structure-Aware Markdown Header Splitting
headers_to_split_on = [
("#", "Header_1"),
("##", "Header_2"),
]
markdown_splitter = MarkdownHeaderTextSplitter(
headers_to_split_on=headers_to_split_on,
strip_headers=False
)
header_chunks = markdown_splitter.split_text(raw_markdown)
print(f"=== Markdown Header Splits ({len(header_chunks)} chunks) ===")
for i, chunk in enumerate(header_chunks, 1):
print(f"Chunk {i} Metadata:", chunk.metadata)
print(f"Chunk {i} Text Preview:", chunk.page_content[:60], "...\n")
# Strategy B: Recursive Character Text Splitting (Secondary refinement)
recursive_splitter = RecursiveCharacterTextSplitter(
chunk_size=150,
chunk_overlap=20,
separators=["\n\n", "\n", " ", ""]
)
sub_chunks = recursive_splitter.split_documents(header_chunks)
print(f"Total refined sub-chunks: {len(sub_chunks)}")5. Parent Document Retriever (Small-to-Big Retrieval)
In production RAG, you often encounter a painful conflict:
The Parent Document Retriever solves this elegantly:
- A large document is split into 1,500-token Parent Chunks and stored in a Key-Value Docstore (e.g. Redis, MongoDB, or disk).
- Each parent is sub-split into small 200-token Child Chunks and indexed in the Vector Database with a
parent_idpointer. - When a user query arrives, the Vector Database matches the child chunk, but the retriever fetches and injects the parent chunk into the LLM prompt.
Hybrid Search (Dense + Sparse)
Continue to the next advanced retrieval and vector search engineering module.
Explore Related Tools
Hand-picked utilities and calculators related to this tool.
