Text Splitters (Chunking)
A 200-page PDF cannot go into one prompt, and even if it could, searching it would be noisy. So we cut documents into small chunks first. The two numbers that matter are chunk_size (how big each piece is) and chunk_overlap (how much text repeats between pieces so a sentence is not cut in half). Play with both in the live playground below: it uses the same algorithm as LangChain.
In this session
- Why split: limits, accuracy and cost
- chunk_size and chunk_overlap
- RecursiveCharacterTextSplitter: the default
- Token, Markdown, HTML, code and JSON splitters
- split_documents keeps metadata
- Chunk size tips by data type
01The big idea: cutting a long train into coaches
Think of a long-distance train. You cannot lift the whole train, but each coach is easy to move, and each coach has a number (metadata) so you know where it belongs. Splitting cuts a document into coaches. The overlap is like the vestibule between coaches: a little shared space so nothing falls through the gap.
Set chunk_overlap to 10, then try chunk_size 50, 100, 200 and 500: you get 12, 6, 4 and 1 chunks, exactly the numbers in the notebook. Paste your own text too.
02Which splitter?
RecursiveCharacterTextSplitter tries separators in order: paragraph \n\n → line \n → space → character. It only cuts smaller when a piece is still too big, so paragraphs and sentences stay together as much as possible.
| Data | Splitter | Typical size |
|---|---|---|
| normal text / PDF | RecursiveCharacterTextSplitter | 500–1000 chars, 10–20% overlap |
| strict token limit | TokenTextSplitter (tiktoken) | by tokens |
| Markdown docs | MarkdownHeaderTextSplitter (+ recursive) | by section |
| HTML pages | HTMLHeaderTextSplitter | by heading |
| code | RecursiveCharacterTextSplitter.from_language(Language.PYTHON) | by function / class |
| JSON | RecursiveJsonSplitter(max_chunk_size=…) | by key |
03Hands-on: run it here
Same text and settings as the notebook, plus the NovaBank PDF from Lab 06. See how split_documents keeps the page metadata on every chunk, and how the code splitter keeps each function together.
from langchain_text_splitters import (CharacterTextSplitter, RecursiveCharacterTextSplitter, MarkdownHeaderTextSplitter, Language)
import warnings; warnings.filterwarnings("ignore")
from langchain_community.document_loaders import PyPDFLoader
text = """LangChain is a framework for building applications with large language models.
It provides tools for prompts, chains, memory, retrieval and agents.
RAG stands for Retrieval Augmented Generation. It lets the LLM answer from your own documents.
First we load documents, then split them into chunks, then create embeddings and store them in a vector database.
Agents use an LLM to decide which tool to call. Tools can be search engines, calculators or APIs."""
print("Character (\\n\\n, 200):", [len(c) for c in CharacterTextSplitter(separator="\n\n", chunk_size=200, chunk_overlap=20).split_text(text)])
chunks = RecursiveCharacterTextSplitter(chunk_size=120, chunk_overlap=20).split_text(text)
print(f"Recursive (120/20): {len(chunks)} chunks")
for i, c in enumerate(chunks[:4]): print(f" {i} ({len(c)}) {c}")
for size in [50, 100, 200, 500]:
print(f" chunk_size={size:>3} -> {len(RecursiveCharacterTextSplitter(chunk_size=size, chunk_overlap=10).split_text(text))} chunks")
pages = PyPDFLoader("novabank_handbook.pdf").load()
docs = RecursiveCharacterTextSplitter(chunk_size=150, chunk_overlap=20).split_documents(pages)
print(f"\nPDF: {len(pages)} pages -> {len(docs)} chunks; chunk 4 keeps metadata page={docs[4].metadata['page']}:", docs[4].page_content[:70])
md = "# Python Course\n## Module 1: Basics\nVariables, data types and loops.\n## Module 2: OOP\nClasses, objects and inheritance.\n# GenAI Course\n## Module 1: LangChain\nPrompts, chains and agents."
for d in MarkdownHeaderTextSplitter(headers_to_split_on=[("#", "course"), ("##", "module")]).split_text(md):
print(" md:", d.metadata, "->", d.page_content)
code = "class Calculator:\n def add(self, a, b):\n return a + b\n\n def sub(self, a, b):\n return a - b\n\ndef greet(name):\n print(f\"Hello {name}\")\n"
for c in RecursiveCharacterTextSplitter.from_language(Language.PYTHON, chunk_size=80, chunk_overlap=0).split_text(code):
print(" code chunk:", c.splitlines()[0], f"... ({len(c.splitlines())} lines)")TokenTextSplitter is in the notebook but not here: it downloads the tiktoken vocabulary the first time, which needs Colab’s internet.
04Hands-on: Ajeetkumar’s Colab notebook
groq_key with your free key from console.groq.com, switch on Notebook access, then run the cells from the top.05Practice tasks
- Split the PDF with chunk_size 300 and 1500: compare the number of chunks.
- Split a markdown README by headers.
- Split a JavaScript file using
Language.JS.
Try these in the same Colab notebook after you finish the lab. Change one thing at a time and look at the output again.
06Summary
- Split because of model limits, search accuracy and cost.
chunk_size= max size;chunk_overlap= repeated text between chunks.RecursiveCharacterTextSplitteris the default: paragraph → line → space → character.- Use header splitters for Markdown/HTML,
from_languagefor code. split_documentskeeps metadata for citations.
07Quick quiz
1. Why use chunk_overlap?
2. Which separator does the recursive splitter try first?
3. Good starting size for PDF text?
4. Which splitter keeps Python functions together?
5. What does split_documents keep that split_text does not?
08References
- LangChain: Text splitters
- Chunk visualiser (ChunkViz)
- Ajeetkumar’s notebook: LC_07_Text_Splitters.ipynb (download)