Skip to content
Part 4 41 min read

RAG and Knowledge Systems

RAG lets a model answer from your own documents. Learn how it works in production: chunking, vector databases, hybrid search, better queries, reranking, filters, document parsing, GraphRAG, and multimodal retrieval.

Go to quiz
On this page

Section 1 of 10

Chunking strategies: fixed, semantic, structural, late chunking

Retrieval works on pieces of documents, not whole documents, so how you cut the text decides what the retriever can ever return. A chunk that is too coarse buries the answer in noise; one that is too fine strips away the context that made the sentence meaningful. This section covers four families: fixed-size (with overlap and recursive separators), structural (headers and sentences), semantic (embedding-driven breakpoints) and late chunking (split after the transformer), plus Anthropic's contextual retrieval, which repairs lost context by prepending an explanation to every chunk.[1][2]

Fixed-size and recursive splitting

The baseline is a window of chunk_size units with chunk_overlap units shared between neighbours so a fact straddling a boundary still lands whole in at least one chunk. In LangChain's RecursiveCharacterTextSplitter the size is measured by length_function, which defaults to Python len(), i.e. characters. The recursive variant tries separators in order ("\n\n", "\n", " ", "": paragraph, line, word, character) so coherent units stay together for as long as possible.[3]

Fixed-size window with overlap document text, left to right chunk 1 (chunk_size) chunk 2 chunk 3 shared text (chunk_overlap) survives in both overlap with chunk 3 Recursive splitter: fall back to a finer separator only when a piece is still too big 1 · paragraph "\n\n" 2 · line "\n" 3 · word " " 4 · character "" too big too big too big Separator order is the LangChain default list; CJK/Thai text needs custom punctuation separators.
Top: overlapping windows keep boundary text in two chunks. Bottom: the recursive splitter prefers paragraph breaks and only descends to words or characters when it must.
উপরে ওভারল্যাপ সহ স্লাইডিং উইন্ডো দেখানো হয়েছে, যাতে দুই চাঙ্কের সীমানার লেখা দুই জায়গাতেই থাকে। নিচে রিকার্সিভ স্প্লিটার আগে প্যারাগ্রাফ, তারপর লাইন, ওয়ার্ড এবং শেষে ক্যারেক্টার দিয়ে কাটে, যতক্ষণ না টুকরা ছোট হয়।

Structural and sentence-aware splitting

Structure-aware splitters respect boundaries the author already drew. LlamaIndex's SentenceSplitter breaks at sentence boundaries (the docs example uses chunk_size=1024 plus a chunk_overlap), and MarkdownNodeParser parses raw markdown into nodes.[4] LangChain's MarkdownHeaderTextSplitter groups text by header level and attaches the headers as metadata (for example {'Header 1': ..., 'Header 2': ...}); headers are stripped from the text by default (strip_headers=False keeps them). Chain it with a recursive splitter so overlap applies inside a section and never crosses a header boundary.[5]

from langchain_text_splitters import (
    MarkdownHeaderTextSplitter, RecursiveCharacterTextSplitter)

md = MarkdownHeaderTextSplitter(
    headers_to_split_on=[("#", "Header 1"), ("##", "Header 2")],
    strip_headers=False)          # keep headings inside the chunk text
sections = md.split_text(markdown_text)  # each Document carries header metadata

rc = RecursiveCharacterTextSplitter(chunk_size=1000, chunk_overlap=100)  # illustrative values
chunks = rc.split_documents(sections)   # overlap stays inside each section

Units matter: the LangChain default measures characters, not tokens, so a "1000" chunk is not 1000 tokens. The values above are illustrative; none of the sources I checked states a recommended chunk size or overlap percentage, so tune them against your own retrieval evals.

Semantic chunking

Instead of a fixed size, semantic chunking embeds sentences and cuts where adjacent-sentence similarity drops. LlamaIndex's SemanticSplitterNodeParser chooses breakpoints from embedding similarity; its docs example uses breakpoint_percentile_threshold=95 and buffer_size=1, and credits Greg Kamradt's "5 levels of embedding chunking".[4]

Similarity of neighbouring sentences (schematic, not measured data) hi lo breakpoint breakpoint chunk 1 chunk 2 chunk 3 A cut is made where the distance jumps past a percentile threshold (docs example: 95).
Semantic chunking cuts at the biggest topic shifts between adjacent sentences. The curve is illustrative only.
পাশাপাশি বাক্যের এমবেডিং সিমিলারিটি হঠাৎ কমে গেলে সেখানে ব্রেকপয়েন্ট বসে এবং নতুন চাঙ্ক শুরু হয়। বক্ররেখাটি শুধু ধারণা বোঝানোর জন্য আঁকা, বাস্তব ডেটা নয়।

Late chunking

Conventional pipelines chunk first and embed each chunk alone, so a chunk saying "the company" cannot know which company. Late chunking (Günther et al., 2024) runs a long-context embedding model over the whole text and splits "after the transformer model and just before mean pooling", so every chunk vector is pooled from token embeddings that already attended to the surrounding document. The authors state it needs no extra training, and fine-tuning can improve it further.[1] The limit is practical: it requires an embedding model whose context window can hold the document.

Naive: chunk, then embed 1 · Split first chunk A | B | C 2 · Encode chunks alone no view of other chunks 3 · Mean pooling per chunk's tokens Chunk vectors context-blind Late chunking: embed, then chunk 1 · Encode whole doc long-context model 2 · Token embeddings every token saw the doc 3 · Split, then pool mean pool per span Chunk vectors carry surrounding context The split moves to "after the transformer, just before mean pooling". No extra training required; fine-tuning can improve further (Günther et al., 2024).
Only the position of the split changes: late chunking pools chunk vectors from token embeddings that were computed over the whole document.
নেইভ পদ্ধতিতে আগে কাটা হয়, তারপর প্রতিটি চাঙ্ক আলাদাভাবে এনকোড হয়। লেট চাঙ্কিংয়ে পুরো ডকুমেন্ট একবারে এনকোড করে, তারপর মিন পুলিংয়ের ঠিক আগে কাটা হয়, তাই প্রতিটি চাঙ্ক ভেক্টর আশপাশের কনটেক্সট বহন করে।

Contextual retrieval (Anthropic)

A different repair: have an LLM write a short, chunk-specific explanation of where the chunk sits in its document, prepend it to the chunk, and use the result for both the embedding and the BM25 index. Anthropic reports the context is roughly 50 to 100 tokens per chunk and that one-time generation costs $1.02 per million document tokens. For knowledge bases under about 200,000 tokens (around 500 pages) the post says simpler approaches, such as putting everything in the prompt, are enough.[2]

Prepend generated context, then index twice Chunk raw slice LLM-written context ~50-100 tokens each Contextualized chunk context + original Embeddings BM25 index Fuse + rerank top results Reported retrieval failure rate (lower is better; bar length = rate) baseline 5.7% contextual embeddings 3.7% (-35%) + contextual BM25 2.9% (-49%) + reranking 1.9% (-67%) Figures as reported by Anthropic on its own evaluation sets; percentages are relative reductions vs the 5.7% baseline.
Contextual retrieval: generated context is prepended before both embedding and BM25 indexing; reported failure rate falls from 5.7% to 1.9% once reranking is added.
প্রতিটি চাঙ্কের আগে LLM-এর লেখা ছোট কনটেক্সট জুড়ে দিয়ে এমবেডিং ও BM25 দুটোই বানানো হয়। Anthropic-এর রিপোর্ট অনুযায়ী রিট্রিভাল ফেইলিউর রেট ৫.৭% থেকে কমে রিরেংকিং-সহ ১.৯% হয়।

Choosing a strategy

The comparison below is my own editorial synthesis from how each method works, not a benchmark result.

StrategyCuts atStrength (editorial)Watch out for (editorial)
Fixed + overlapA size limitTrivial, predictable, cheapCan slice through sentences and facts
RecursiveParagraph, line, word, character, in that orderKeeps coherent units whenever they fitSize is in characters by default
Structural (headers, sentences)Author-drawn boundariesHeader metadata for filtering and citationsNeeds clean, consistent markup
SemanticEmbedding-similarity dropsTopic-coherent chunksExtra embedding pass; threshold to tune
Late chunkingAfter the transformer, before poolingChunk vectors carry document contextNeeds a long-context embedding model
Contextual retrievalAny chunker, plus LLM contextRepairs lost context for dense and BM25One-time LLM cost per chunk
Baseline failure rate
5.7%
Contextual embeddings
-35% (3.7%)
+ contextual BM25
-49% (2.9%)
+ reranking
-67% (1.9%)
Context per chunk
~50-100 tokens
Generation cost (one-time)
$1.02 / M doc tokens

Research frontier: Late chunking (Günther et al., 2024) moves the split to after the transformer and before mean pooling so chunk embeddings carry whole-document context, with no extra training required.[1] Anthropic's contextual retrieval reports cutting failed retrievals by 35% (contextual embeddings), 49% (adding contextual BM25) and 67% (adding reranking) from a 5.7% baseline.[2] Limits: the Anthropic figures are the vendor's own evaluation, and late chunking depends on a long-context embedding model. I did not verify head-to-head comparisons between the two.
গবেষণার সারকথা: Late chunking পুরো document-এর context রেখে chunk embedding বানায়; contextual retrieval প্রতিটি chunk-এ ব্যাখ্যা জুড়ে failure rate 5.7% থেকে 1.9% করে।

Practice · 1 task

Open any free AI chat: ChatGPT · Claude · Gemini

How practice works
  • A new chat is a fresh conversation with no history. In ChatGPT, Claude, or Gemini, click New chat. It is usually at the top left.
  • Same chat means: keep typing in the chat that is already open. The AI remembers what you sent before.
  • To use a prompt: click Copy, click in the message box of the chat, paste it (Ctrl+V, or press and hold on a phone), and press Enter.
  • AI answers are a little different every time. Your answer does not need to match the example word for word. Look for the same idea.
  • Use made-up data only. Do not paste private information into a chat.

Task 1

See how a bad cut breaks an answer

Why this matters

When you build a support chatbot, your documents are cut into small pieces called chunks. If a cut lands in the middle of a fact, the bot can give a wrong answer, or no answer.

  1. Step 1
    New chat

    Copy this prompt, paste it, and press Enter. It gives the AI a chunk that was cut in the middle of a sentence.

    Prompt
    You are a support bot. Answer the question using ONLY the chunk below. If the chunk does not contain the answer, say "Not enough information".
    
    Chunk (cut every 90 characters, this is chunk 2 of 2):
    "days of purchase. Pro plan customers may request a refund within 30 days. Refunds are paid to the original payment method in 5 business days."
    
    Question: How many days do Basic plan customers have to ask for a refund?

    You should see: Most chats reply Not enough information, because the number for the Basic plan is missing. A few wrongly say 30.

    Example answer
    Not enough information

    Your answer will be worded differently.

  2. Step 2
    New chat

    Copy this prompt, paste it, and press Enter. This chunk was cut at the end of a sentence.

    Prompt
    You are a support bot. Answer the question using ONLY the chunk below. If the chunk does not contain the answer, say "Not enough information".
    
    Chunk (cut at the end of a sentence):
    "Zentro Cloud Refund Policy. Basic plan customers may request a full refund within 14 days of purchase. Pro plan customers may request a refund within 30 days."
    
    Question: How many days do Basic plan customers have to ask for a refund?

    You should see: 14 days. The whole fact is inside one chunk.

    Example answer
    Basic plan customers have 14 days to request a refund.

    Your answer will be worded differently.

  3. Step 3
    Same chat

    Look at the chunk in step 1 again. Write down the words that are missing there.

    You should see: The chunk starts with days of purchase. The number 14 and the words Basic plan are gone.

What just happened

A chatbot does not read your whole document. It searches small pieces called chunks and sends only the best ones to the AI. A fixed-size cutter cuts after a set number of characters, even in the middle of a fact. Here the cut removed 14, so the AI could not answer. A cutter that stops at sentence ends keeps the fact whole.

You are done when You have two answers: one without the number 14, and one with 14 days.

Check yourselfWhy did step 1 fail, when the full policy exists in the document?Show answer

The cutter split the sentence, so the number and the plan name went into different chunks. The search sent only one chunk to the AI. A cutter that respects sentence ends keeps the fact in one piece.

Try a harder version

In the chunk from step 2, replace the first sentence with The company raised prices. and ask Which company is this about? Then add the line From the Zentro Cloud refund policy. at the top and ask again. What changed?

Watch · 3 videos