Skip to content
Back to skills

Chunking Strategies

ASecurity

Document chunking techniques for RAG. Fixed-size, recursive, semantic, token-based, document-aware, proposition, parent-child, sliding window, and Anthropic contextual retrieval. Tradeoff tables, LangChain and LlamaIndex code. USE WHEN: user mentions "chunking", "text splitter", "split documents", "semantic chunking", "contextual retrieval", "parent-child chunks", "proposition chunking" DO NOT USE FOR: retrieval after chunking - use `advanced-retrieval`; query-side transforms - use `query-t...

  • 31 stars
  • 0 votes
  • 0 copies
  • 3 views
  • Added September 8, 2026
ai-agentspythonnodeexpress

Works with

  • cli

Security analysis

A100/100

Scanned September 8, 2026

npx -y skills add claude-dev-suite/claude-dev-suite --skill chunking-strategies --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Chunking Strategies?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Chunking Strategies
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/claude-dev-suite-chunking-strategies/badge)](https://www.skillsdirectory.com/skills/claude-dev-suite-chunking-strategies)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: chunking-strategies
description: |
  Document chunking techniques for RAG. Fixed-size, recursive, semantic, token-based,
  document-aware, proposition, parent-child, sliding window, and Anthropic contextual
  retrieval. Tradeoff tables, LangChain and LlamaIndex code.

  USE WHEN: user mentions "chunking", "text splitter", "split documents", "semantic
  chunking", "contextual retrieval", "parent-child chunks", "proposition chunking"

  DO NOT USE FOR: retrieval after chunking - use `advanced-retrieval`;
  query-side transforms - use `query-transformations`; overall design - use `rag-architecture`
allowed-tools: Read, Grep, Glob, Write, Edit
---
# Chunking Strategies

## Strategy Tradeoff Matrix

| Strategy | Preserves Semantics | Cost | Best For | Chunk Unit |
|---|---|---|---|---|
| Fixed-size | No | Free | Homogeneous prose | Chars |
| Recursive character | Partial | Free | General text | Chars with hierarchy |
| Token-based | No | Free | Exact token budgeting | Tokens |
| Document-aware (Markdown/HTML) | Yes | Free | Technical docs, wikis | Headers/sections |
| Code-aware | Yes | Free | Source code | Functions/classes |
| Semantic (embedding breakpoints) | Yes | $$ | Long narrative, research papers | Meaning shifts |
| Proposition-based | Yes | $$$ | High-precision Q&A, legal | Atomic facts |
| Parent-child | Yes | Free | Need small match + big context | Hierarchy |
| Sliding window | Partial | Free | Dialogues, timelines | Overlap + stride |
| Contextual retrieval (Anthropic) | Yes | $$ | Production RAG > 5k chunks | Chunk + LLM context |

## Fixed-Size

```python
def fixed_chunks(text: str, size: int = 800, overlap: int = 200) -> list[str]:
    return [text[i:i + size] for i in range(0, len(text), size - overlap)]
```

Use only as a baseline. Breaks mid-sentence and mid-token.

## Recursive Character (default for most projects)

```python
from langchain_text_splitters import RecursiveCharacterTextSplitter

splitter = RecursiveCharacterTextSplitter(
    chunk_size=800,
    chunk_overlap=200,
    separators=["\n\n", "\n", ". ", "? ", "! ", " ", ""],
    length_function=len,
    is_separator_regex=False,
)
chunks = splitter.split_documents(docs)
```

Tries separators in order so paragraph boundaries are preferred over arbitrary cuts.

## Token-Based (exact LLM budgeting)

```python
from langchain_text_splitters import TokenTextSplitter

splitter = TokenTextSplitter(
    encoding_name="cl100k_base",  # GPT-4, text-embedding-3
    chunk_size=512,
    chunk_overlap=64,
)
chunks = splitter.split_text(text)
```

For Claude, use `anthropic.Anthropic().messages.count_tokens` to measure; approximate ratio is ~3.5 chars per token for English.

## Document-Aware: Markdown

```python
from langchain_text_splitters import MarkdownHeaderTextSplitter, RecursiveCharacterTextSplitter

headers = [("#", "h1"), ("##", "h2"), ("###", "h3")]
md_splitter = MarkdownHeaderTextSplitter(headers_to_split_on=headers, strip_headers=False)
header_chunks = md_splitter.split_text(markdown_text)

# Secondary splitter for oversized sections
char_splitter = RecursiveCharacterTextSplitter(chunk_size=800, chunk_overlap=150)
chunks = char_splitter.split_documents(header_chunks)  # metadata carries h1/h2/h3
```

Header metadata enables section-level filtering at query time.

## Document-Aware: Code

```python
from langchain_text_splitters import RecursiveCharacterTextSplitter, Language

py_splitter = RecursiveCharacterTextSplitter.from_language(
    language=Language.PYTHON, chunk_size=1500, chunk_overlap=200
)
ts_splitter = RecursiveCharacterTextSplitter.from_language(
    language=Language.TS, chunk_size=1500, chunk_overlap=200
)
```

Splits on `class`, `def`, `function` boundaries. Larger chunks because code lines are shorter than prose.

## Semantic Chunking (embedding breakpoints)

Splits where adjacent sentences diverge in meaning. Expensive (embeds every sentence) but yields coherent chunks on long-form content.

```python
from langchain_experimental.text_splitter import SemanticChunker
from langchain_openai import OpenAIEmbeddings

splitter = SemanticChunker(
    OpenAIEmbeddings(model="text-embedding-3-small"),
    breakpoint_threshold_type="percentile",   # or "standard_deviation", "interquartile"
    breakpoint_threshold_amount=95,
    buffer_size=1,
)
chunks = splitter.create_documents([long_text])
```

Manual variant for fine control (break where cosine distance between adjacent sentence embeddings exceeds the 95th percentile):

```python
import numpy as np
from sentence_transformers import SentenceTransformer

def semantic_chunks(text: str, pct: float = 95) -> list[str]:
    sents = [s.strip() for s in text.split(". ") if s.strip()]
    embs = SentenceTransformer("all-MiniLM-L6-v2").encode(sents)
    sims = [np.dot(embs[i], embs[i+1]) / (np.linalg.norm(embs[i]) * np.linalg.norm(embs[i+1]))
            for i in range(len(sents) - 1)]
    dists = 1 - np.array(sims)
    breaks = [i + 1 for i, d in enumerate(dists) if d > np.percentile(dists, pct)]
    out, start = [], 0
    for b in breaks + [len(sents)]:
        out.append(". ".join(sents[start:b])); start = b
    return out
```

## Proposition-Based Chunking

Decompose text into atomic factual propositions using an LLM. Each proposition becomes one chunk.

```python
from anthropic import Anthropic
import json

client = Anthropic()

PROMPT = """Decompose the passage into atomic propositions. Each proposition:
- Expresses exactly one fact
- Is self-contained (no pronouns without antecedents)
- Resolves coreferences inline

Return JSON array of strings. Passage:
{passage}"""

def propositionize(passage: str) -> list[str]:
    msg = client.messages.create(
        model="claude-sonnet-4-5-20250929",
        max_tokens=2048,
        messages=[{"role": "user", "content": PROMPT.format(passage=passage)}],
    )
    return json.loads(msg.content[0].text)
```

Highest retrieval precision; highest ingestion cost. Use for legal, medical, compliance.

## Parent-Child Chunking

Index small chunks (for precise retrieval) but return parent chunks (for context).

```python
from langchain.retrievers import ParentDocumentRetriever
from langchain.storage import InMemoryStore
from langchain_chroma import Chroma
from langchain_text_splitters import RecursiveCharacterTextSplitter
from langchain_openai import OpenAIEmbeddings

parent_splitter = RecursiveCharacterTextSplitter(chunk_size=2000)
child_splitter = RecursiveCharacterTextSplitter(chunk_size=400)
vectorstore = Chroma(collection_name="children", embedding_function=OpenAIEmbeddings())
docstore = InMemoryStore()

retriever = ParentDocumentRetriever(
    vectorstore=vectorstore,
    docstore=docstore,
    child_splitter=child_splitter,
    parent_splitter=parent_splitter,
)
retriever.add_documents(docs)
```

See also `advanced-retrieval` for multi-vector and sentence-window patterns that generalize this idea.

## Sliding Window with Stride

```python
def sliding_window(tokens: list[str], window: int = 512, stride: int = 256) -> list[list[str]]:
    return [tokens[i:i + window] for i in range(0, len(tokens), stride) if i + window <= len(tokens)]
```

Use for dialogue, legal contracts, timelines where context before and after each point matters.

## Anthropic Contextual Retrieval

Prepend LLM-generated context to each chunk before embedding. Reduces retrieval failure rate by ~35% on Anthropic's benchmark.

```python
from anthropic import Anthropic

client = Anthropic()

CONTEXT_PROMPT = """<document>
{whole_document}
</document>

Here is the chunk we want to situate within the whole document:
<chunk>
{chunk}
</chunk>

Give a short (1-2 sentence) context to situate this chunk within the overall
document for the purposes of improving search retrieval of the chunk.
Answer only with the succinct context and nothing else."""

def contextualize(whole_doc: str, chunk: str) -> str:
    msg = client.messages.create(
        model="claude-haiku-4-5-20250929",
        max_tokens=200,
        messages=[{"role": "user", "content": CONTEXT_PROMPT.format(
            whole_document=whole_doc, chunk=chunk)}],
        extra_headers={"anthropic-beta": "prompt-caching-2024-07-31"},
    )
    return msg.content[0].text

def contextual_chunks(doc: str, base_chunks: list[str]) -> list[str]:
    return [f"{contextualize(doc, c)}\n\n{c}" for c in base_chunks]
```

Use prompt caching on `whole_document` to drop cost by ~10x. Combine with BM25 + dense for best results.

## LlamaIndex Equivalents

```python
from llama_index.core.node_parser import (
    SentenceSplitter, SemanticSplitterNodeParser, MarkdownNodeParser,
    HierarchicalNodeParser,
)
from llama_index.embeddings.openai import OpenAIEmbedding

sentence = SentenceSplitter(chunk_size=512, chunk_overlap=64)
semantic = SemanticSplitterNodeParser(
    buffer_size=1, breakpoint_percentile_threshold=95, embed_model=OpenAIEmbedding()
)
markdown = MarkdownNodeParser()
hierarchical = HierarchicalNodeParser.from_defaults(chunk_sizes=[2048, 512, 128])
```

## Sizing Heuristics by Content Type

| Content | Chunk Size | Overlap | Splitter |
|---|---|---|---|
| Blog posts / articles | 800 chars | 150 | Recursive character |
| Technical docs (markdown) | 1000 chars | 200 | Markdown headers |
| Source code | 1500 chars | 200 | Language-aware |
| Legal / contracts | 400 chars | 100 | Proposition or sliding |
| Research papers | Variable | N/A | Semantic |
| Customer support tickets | 300 chars | 50 | Per-turn |
| Book / long narrative | 1500 chars | 300 | Semantic + parent-child |

## Anti-Patterns

| Anti-Pattern | Fix |
|---|---|
| One chunk size for all content types | Split pipeline per content type |
| Overlap = 0 | Always overlap 10-25% to preserve boundary context |
| Splitting code by character count | Use `Language.*` language-aware splitters |
| Stripping headers before chunking | Keep or re-prepend headers for retrieval signal |
| Chunks larger than embedding model context | Measure: `text-embedding-3-*` max 8191 tokens |
| No metadata on chunks | Attach `source`, `section`, `chunk_index`, `parent_id` |
| Re-embedding whole corpus on tweak | Incremental pipeline keyed by content hash |
| Semantic chunking on short docs | Not worth the cost below ~10 pages |

## Production Checklist

- [ ] Content-type detection routes to the right splitter
- [ ] Chunk size tuned with a retrieval eval set (`rag-evaluation`)
- [ ] Metadata stamped on every chunk (source, section, position, hash)
- [ ] Chunk hash stored for idempotent re-ingestion
- [ ] Token counts measured against embedding model limit
- [ ] Overlap configured (default 20%)
- [ ] Parent-child or multi-vector used when context window matters
- [ ] Contextual retrieval considered for corpora > 5k chunks
- [ ] Ingestion pipeline monitored (chunks/sec, embedding cost/doc)
- [ ] Re-chunking pathway documented (rolling re-index)

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.