Module 4: Evaluation and Chunking Strategies

Advanced Chunking Patterns

Quick overview

Beyond basic chunking: recursive strategies (LangChain-style), metadata enrichment (headers, source), hierarchical chunks (parent/child), and context preservation. These patterns improve retrieval quality significantly.

You'll learn production-ready advanced patterns.


Recursive Chunking (LangChain-style)

Concept:

# Try to split hierarchically:
# 1. By paragraphs (\n\n)
# 2. If a paragraph is too large → by sentences (\n)
# 3. If a sentence is too large → by spaces
# 4. If a word is too large → by characters

# Guarantees: Never exceed max_chunk_size

Implementation:

from langchain.text_splitter import RecursiveCharacterTextSplitter
import tiktoken

def tiktoken_len(text):
    encoding = tiktoken.encoding_for_model("gpt-4")
    return len(encoding.encode(text))

# Recursive splitter with tokens
splitter = RecursiveCharacterTextSplitter(
    chunk_size=500,
    chunk_overlap=50,
    length_function=tiktoken_len,
    separators=["\n\n", "\n", ". ", " ", ""]  # Hierarchy
)

text = """
# Python Guide

Python is a programming language.

## Installation
Download from python.org.
Run the installer.

## First Program
print("Hello World")
"""

chunks = splitter.split_text(text)
print(f"Chunks: {len(chunks)}")
for i, chunk in enumerate(chunks):
    print(f"\nChunk {i+1} ({tiktoken_len(chunk)} tokens):")
    print(chunk[:100] + "...")

Metadata Enrichment

Add metadata to chunks:

class MetadataChunker:
    """Chunker with metadata"""
    
    def chunk_with_metadata(self, text, source="doc1.md"):
        """Add source, headers, etc."""
        # Detect headers (# Markdown)
        lines = text.split('\n')
        current_header = None
        chunks = []
        
        for line in lines:
            if line.startswith('#'):
                current_header = line.strip('# ')
            # ... chunking logic ...
            
            chunk = {
                'text': chunk_text,
                'metadata': {
                    'source': source,
                    'header': current_header,
                    'tokens': len(tokens)
                }
            }
            chunks.append(chunk)
        
        return chunks

Advantage: Retrieval can filter by source, header, etc.


Hierarchical Chunks (Parent/Child)

Concept:

# Embed small chunks (high precision)
# But retrieve large chunks (more context)

# Parent chunk (800 tokens):
parent = "Full section about Python installation..."

# Child chunks (200 tokens each):
children = [
    "Download Python from python.org...",
    "Run installer...",
    "Verify installation..."
]

# Retrieval:
# 1. Search in children (precision)
# 2. If match → Return parent (full context)

Context Preservation

Windowed chunking:

# Preserve previous/following context:

def windowed_chunking(text, chunk_size=500, window_before=100, window_after=100):
    """Chunks with previous/following context"""
    base_chunks = fixed_chunking(text, chunk_size)
    
    windowed = []
    for i, chunk in enumerate(base_chunks):
        # Add context from the previous chunk
        context_before = base_chunks[i-1][-window_before:] if i > 0 else ""
        
        # Add context from the following chunk
        context_after = base_chunks[i+1][:window_after] if i < len(base_chunks)-1 else ""
        
        windowed_chunk = context_before + " [...] " + chunk + " [...] " + context_after
        windowed.append(windowed_chunk)
    
    return windowed

Summary

What you learned:

  • Recursive: Balance fixed/semantic
  • Metadata: Enriches chunks (source, headers)
  • Hierarchical: Parent/child for precision+context
  • Context preservation: Windowed chunking

Key pattern: Hierarchical chunks = best for RAG.


In the next capsule

Capsule 08: Mini-Project - RAG System with Intelligent Chunking

You'll build a complete RAG system with end-to-end evaluation.


Module 4 - Embeddings Deep Dive Guide Advanced patterns: production-ready chunking