RAG Chunking Strategy: Fix Retrieval by Splitting on Sentences

Fixed-width character slicing is the most common silent killer of RAG quality. When chunks cut mid-sentence, every indexed passage is a meaningless fragment - retrieval looks fine but returns garbage. Splitting on sentence boundaries fixes it.

AI Engineerairagchunking

Why chunking determines retrieval quality

RAG retrieval is only as good as the chunks it searches. If a document is sliced into 25-character windows, the index stores fragments like "re retried three t" - no embedding model can make that semantically meaningful. Cosine similarity will find something, but the returned passages won't answer the user's question.

The retrieval and ranking code is irrelevant; garbage in, garbage out.

The broken pattern: fixed-width character slices

def chunk(text):
    # BAD: cuts sentences and words apart at arbitrary positions
    size = 25
    return [text[i:i+size] for i in range(0, len(text), size)]

A document like "User authentication is handled by issuing a signed JWT on login." becomes:

"User authentication is h"
"andled by issuing a sign"
"ed JWT on login."

Each chunk is syntactically broken. TF-IDF and embedding models both suffer.

The fix: split on sentence boundaries

import re

def chunk(text):
    # Split after sentence-ending punctuation followed by whitespace
    parts = re.split(r"(?<=[.!?])\s+", text.strip())
    return [p.strip() for p in parts if p.strip()] or [text.strip()]

This keeps each chunk a complete, self-contained thought. The same document now becomes:

"User authentication is handled by issuing a signed JWT on login."
"The token expires after 30 minutes and must be refreshed."

Both chunks are retrievable on their own terms - one answers authentication questions, the other answers expiry/refresh questions.

Plugging it into the pipeline

The index() and retrieve() functions don't change - only chunk() does:

def index(corpus):
    chunks = [c for doc in corpus for c in chunk(doc)]
    vectorizer = TfidfVectorizer().fit(chunks)
    matrix = vectorizer.transform(chunks)
    return vectorizer, matrix, chunks

def retrieve(question, vectorizer, matrix, chunks, k=2):
    qv = vectorizer.transform([question])
    sims = cosine_similarity(qv, matrix)[0]
    idxs = np.argsort(sims)[::-1][:k]
    return [chunks[i] for i in idxs]

Production chunking patterns

Sentence splitting is the right starting point. Real production RAG systems go further:

The failure mode - tiny character slices silently tanking retrieval - is one of the most common RAG bugs in production. It never throws an error; you just get bad answers.

Want to try it hands-on? HeyDevJob gives you this exact setup in a live cloud workspace in your browser - edit it, run it, and see it work. Free, nothing to install.

Try it in a workspace →

What you'll practice

FAQ

Why is my RAG retrieval returning irrelevant or garbled results?

The most common cause is bad chunking upstream of the index. If documents are split into fixed-width character slices, every chunk is a meaningless fragment and no retrieval algorithm can rescue it. Switch to sentence or paragraph splits.

What is the best chunking strategy for RAG?

Start with sentence-boundary splitting (split on .!? followed by whitespace). For production, add a token-budget cap (e.g. 256 tokens) with 10-20% overlap so context isn't lost at chunk edges. Use layout-aware splitting for PDFs.

How does chunking affect embedding quality in RAG?

Embedding models encode meaning at the phrase and sentence level. A chunk that cuts mid-word or mid-sentence produces a vector that represents noise, not a concept - so cosine similarity returns random-looking results regardless of the model quality.

Keep learning

Restore a Broken LLM API IntegrationAI/ML projectHarden an LLM Pipeline Against API FailuresAI/ML projectParse JSON From an LLM (Strip Markdown Fences)AI/ML projectAI/ML roadmapStep by step to hiredAI/ML interview questionsSTAR answersAll AI/ML projectsProjects hub

Learn it by doing. Open this in a live cloud workspace, make the change yourself, and keep a record of the work you can share.

Open the workspace →