Semantic Chunking for RAG: Why Fixed-Size Chunks Fail and What to Use Instead
The Hook
You chunk your docs at 512 tokens with 100 overlap. Retrieval precision: 62%. You switch to semantic chunking. Precision: 89%. Fixed-size chunks break semantic boundaries. Semantic chunking respects document structure. The difference is the difference between “RAG works” and “RAG hallucinates.”
The Thesis
Chunking strategy is the single highest-leverage RAG optimization. Fixed-size chunks (512 tokens, 100 overlap) cut sentences, tables, code blocks mid-thought. Semantic chunking (semantic splitter, proposition splitting, LLM-based) preserves semantic units. The precision delta: 25-40% improvement.
The Science
Why Fixed-Size Fails
| Problem | Example |
|---|---|
| Mid-sentence cuts | “The algorithm works by…” [CUT] “…iterating until convergence.” |
| Table fragmentation | Header in chunk 1, rows in chunk 2 |
| Code fragmentation | Function signature in chunk 1, body in chunk 2 |
| Context loss | “As mentioned above” → context in previous chunk |
Research: Zhang et al. (2024, Semantic Chunking for RAG) — 12 datasets, 4 embedding models:
- Fixed 512: 62.3% recall@5
- Semantic: 87.1% recall@5 (+40%)
- Proposition: 89.3% recall@5 (+43%)
Semantic Chunking Methods
| Method | Mechanism | Precision Gain | Complexity |
|---|---|---|---|
| Semantic Splitter | Embedding similarity between sentences | +25% | Low |
| Proposition Splitting | LLM extracts atomic facts | +35% | Medium |
| LLM-based | LLM segments by topic | +40% | High |
| Markdown/Structure | Header-aware splitting | +30% | Low |
Semantic Splitter (LangChain/llama-index)
Algorithm:
- Split into sentences
- Embed each sentence
- Cluster adjacent sentences by cosine similarity
- Merge until similarity < threshold
from langchain.text_splitter import SemanticChunker
from sentence_transformers import SentenceTransformer
embedder = SentenceTransformer("BAAI/bge-m3")
chunker = SemanticChunker(
embedder,
breakpoint_threshold_type="percentile",
breakpoint_threshold_amount=95 # top 5% dissimilarity = boundary
)
chunks = chunker.split_text(document)
Proposition Splitting (Microsoft, 2024)
Idea: LLM extracts atomic propositions from text. Each proposition = one chunk.
PROMPT = """
Extract atomic propositions from this text. Each proposition should be:
- Self-contained (understandable alone)
- Single fact/claim
- Include context needed to understand
Text: {text}
Output: JSON list of propositions with context.
"""
Result: Each chunk = one verifiable fact. Retrieval precision ↑, hallucination ↓.
LLM-Based Chunking
PROMPT = """
Segment this document into semantically coherent chunks.
Each chunk should:
- Cover one topic/subtopic
- Be 200-800 tokens
- Preserve complete thoughts
- Include necessary context
Document: {text}
Output: JSON with chunk_text, topic, token_count.
"""
The Comparison
| Method | Recall@5 | Latency | Cost/1M tokens | Best For |
|---|---|---|---|---|
| Fixed 512/100 | 62.3% | 0ms | $0 | Baseline |
| Markdown splitter | 78.2% | 10ms | $0 | Structured docs |
| Semantic Splitter | 87.1% | 50ms | $0.50 | General |
| Proposition (GPT-3.5) | 89.3% | 200ms | $2.00 | High-stakes |
| LLM chunking (GPT-4) | 91.2% | 500ms | $10.00 | Critical apps |
The Protocol: Choose Your Chunker
Decision Tree
Is document structured (Markdown, HTML, code)?
YES → Markdown/Header splitter (fast, 80%+ recall)
NO → Is precision critical? (legal, medical, legal)
YES → Proposition splitting (GPT-3.5) or LLM chunking
NO → Semantic Splitter (BGE-M3, 95th percentile)
Implementation Priority
- Week 1: Replace fixed-size with Markdown/Semantic splitter (5 min change, +25% recall)
- Week 2: Add proposition splitting for critical collections (legal, medical, technical)
- Week 3: A/B test chunking strategies on your eval set
- Week 4: Implement hybrid: semantic for docs, proposition for critical sections
The Counterpoint
- Cost: Proposition/LLM chunking adds $2-10/M tokens
- Latency: LLM chunking adds 200-500ms/doc
- Over-chunking: Too many tiny chunks = retrieval noise
- Evaluation needed: Chunking quality must be measured on YOUR eval set
Practical Takeaway
This week: Replace RecursiveCharacterTextSplitter(512, 100) with SemanticChunker(BGE-M3, 95th percentile). Run your eval set. Expect 25%+ recall improvement in 5 minutes of code change.
Sources: Zhang et al. (2024) Semantic Chunking for RAG; LangChain SemanticChunker docs; LlamaIndex SemanticSplitterNodeParser; Microsoft “Proposition-based Retrieval” (2024); RAG evaluation benchmarks (RAGAS, TruLens, 2024).