RAG and search engineeringMiddleLesson 2098 min read

Chunking documents well

Search finds chunks, not whole documents. How you cut the text decides what can be found. Learn size, overlap, splitting by headings and useful metadata.

Lesson in motion

In 60 seconds

Chunking documents well

Search finds chunks, not whole documents. How you cut the text decides what can be found. Learn size, overlap, splitting by headings and useful metadata.

1/5
In simple words
If you cut a newspaper into single words, no piece makes sense. If you keep the whole newspaper as one piece, you cannot hand someone just the cricket score. Good chunking cuts along the natural lines, like cutting out one full article at a time, with its headline still on it.
A chunk is a piece of a document that you embed, index and retrieve on its own. When a question comes in, the model sees only the chunks you retrieve, not the whole document. So each chunk should make sense on its own and contain a complete idea.

Size: the main trade-off

Chunks that are…GoodBad
Too small (a sentence or two)Precise matchesMissing context: "This is allowed only once a year". What is "this"?
Too big (many pages)Plenty of contextOne vector must cover many topics, so it matches nothing well; costs more tokens
About right (often a few paragraphs, one idea)Makes sense alone and matches clearlyNeeds testing on your data
There is no single correct size. It depends on your documents, your embedding model's input limit and your questions. Start with a middle size, such as a few hundred words, and let your test set decide.

Overlap

Overlap means the end of one chunk is repeated at the start of the next. If an important sentence falls on a boundary, it still appears whole in at least one chunk. A small overlap is common. Too much overlap fills your index and your prompts with repeated text.

Split by structure, not only by length

Documents have structure: headings, sections, numbered clauses, list items, table rows, FAQ pairs. Splitting along that structure keeps ideas together. Split by length only inside a section that is too long.
  1. 1

    Find the structure

    Headings, clause numbers, FAQ questions, slide titles.
  2. 2

    Split by section

    Each section becomes a candidate chunk.
  3. 3

    Split long sections

    Cut by paragraph, then by length with a small overlap.
  4. 4

    Merge tiny pieces

    Join very short sections with their neighbour so no chunk is just a heading.
  5. 5

    Add context and metadata

    Put the document title and section heading on every chunk.
Python: split by headings, then by length with overlappython
def split_sections(text):
    """Split text into (heading, body) pairs at lines starting with '#'."""
    sections, heading, lines = [], "Untitled", []
    for line in text.splitlines():
        if line.startswith("#"):
            if lines:
                sections.append((heading, " ".join(lines)))
            heading, lines = line.lstrip("#").strip(), []
        elif line.strip():
            lines.append(line.strip())
    if lines:
        sections.append((heading, " ".join(lines)))
    return sections

def chunk(text, doc_id, title, max_words=80, overlap=15):
    chunks = []
    for heading, body in split_sections(text):
        words = body.split()
        start = 0
        while True:
            piece = " ".join(words[start:start + max_words])
            chunks.append({
                "id": f"{doc_id}#{len(chunks)}",
                "doc_id": doc_id,
                "section": heading,
                # Title and heading go into the text that gets embedded.
                "text": f"{title} > {heading}: {piece}",
            })
            if start + max_words >= len(words):
                break
            start += max_words - overlap
    return chunks

policy = """# Leave policy
Staff get 12 days of casual leave each year.
# Carry forward
Unused casual leave cannot be carried to the next year."""
for c in chunk(policy, "hr-leave-v3", "HR Leave Policy 2025"):
    print(c["id"], "|", c["text"])
Notice the chunk text: HR Leave Policy 2025 > Carry forward: Unused casual leave…. Without the title and heading, the chunk "Unused casual leave cannot be carried…" would not say which policy or which organisation it belongs to.

Metadata: the labels on each chunk

FieldWhy it helps
Document id and chunk idCitations, updates and deletions
Title, section, page numberShow the source; add context to the chunk
Date and versionPrefer the newest; filter out old versions
LanguageRoute queries; pick the right model or re-ranker
Type (policy, FAQ, manual, email)Filter searches; show different answer styles
Access groupsPermission filtering at search time (see Securing RAG)
Weak chunking
  • Fixed 500 characters, cutting words and tables in half
  • No title or heading on the chunk
  • No page number, so citations cannot point anywhere
  • The same chunk size for FAQs and 200-page manuals
Better chunking
  • Split by headings or clauses, then by length
  • Title and heading added to each chunk's text
  • Page, section and version stored as metadata
  • Settings chosen by testing on real questions
Note
Some teams search small chunks for precision, then send a larger surrounding piece (the whole section) to the model for context. This is sometimes called small-to-big or parent-child retrieval. It is worth trying when answers need more context than a small chunk holds.
Try this. Take one page of any rules document you know, such as a hostel or society rulebook. Where would you cut it? Which chunks would be confusing without the heading?

Watch and read more

Lab

A chunker that splits by headings, adds context, and prints chunks you have checked by eye.

~30 min

The problem

Use the code from the lesson (Python 3, standard library only).

  1. Write a made-up rules document of 6–10 sections with # headings, for example a housing society's rules. Make one section long (150+ words) and one section only one line.
  2. Run the chunker with max_words=80, overlap=15. Read every chunk. Does each make sense alone?
  3. Add a step that merges any section shorter than 15 words into the next section.
  4. Add a page or version field to each chunk's metadata.
  5. Write three questions a resident might ask, and for each, which chunk id should answer it.

You are done when

Hard questions

Try to answer before you reveal. If you can answer these, you understood the lesson.

Q1Why put the title and heading into the text that gets embedded, and not only into metadata?Reveal

The embedding model only sees the text you give it. If "Carry forward" and "HR Leave Policy" appear only in metadata, the vector for the chunk does not include that meaning, so a question like "can I carry forward casual leave?" may match it less well. Metadata is good for filters and citations; context that helps matching must be in the embedded text.

Q2An FAQ page has 40 short question-and-answer pairs. How would you chunk it, and why?Reveal

Make each question with its answer one chunk. The pair is a complete idea, and the question text often matches how users ask. Splitting by a fixed length could cut a question away from its answer or join two unrelated answers. Add the page title and a category as context, and very short pairs can stay short because they are complete.

Please sign in to continue.

Questions people ask

Should I count chunk size in words, characters or tokens?

Tokens are what the embedding model and the language model actually count, so they are the most accurate. Words are a simple approximation to start with. Note that the same text in many Indian scripts often uses more tokens than in English, so a "word" budget may not match across languages.

What about chunks that are only a heading?

Merge them with the next section. A chunk that says only "Annexure B" matches many questions weakly and answers none.

Can a model do the chunking for me?

Yes, some teams ask a model to find topic boundaries or write a one-line summary for each chunk. This can help with messy text, but it costs more and can make mistakes. Compare it with simple structure-based splitting on your test set.

How do I know if my chunking is good?

Measure retrieval on a set of real questions with known right answers, then change one setting at a time. The last lesson in this track shows how.

Lesson test

5 questions. Get 3 right (60%) to pass and complete this lesson.

Sign in to take the test and save your progress