Skip to content

    Forward Deployed Engineer

    Chunking Trade-offs Measured on a Fixed Question Set

    Change chunk size and overlap one setting at a time, score each on the same 23 golden questions, and learn when chunk size is not the problem.

    By Published 9 min read

    Share

    This lesson is part of Forward Deployed Engineer: RAG, Search and Data. Enroll for free, then sign in, to read it in full.

    What you'll learn: what chunk size and overlap do in the capstone's heading-aware chunker, how to measure them on a fixed question set, and why on this corpus size barely matters while removing headings does.

    At a glance

    Figure 1. A chunking decision, in the order to ask the questions

    flowchart TD accTitle: Deciding how to chunk a corpus accDescr: Start with the corpus and a question set. If documents have no headings, add structure first. If every section already fits in one chunk, size is a no-op, so keep the defaults and move on. If some sections are longer than the chunk size, sweep size and overlap on the question set and choose by recall first, context cost second. S["Corpus and a question set"] --> H{"Do documents have headings?"} H -- no --> A["Add structure first: split at paragraphs, add headings"] H -- yes --> L{"Is any section longer than the chunk size?"} A --> L L -- no --> K["Size is a no-op: keep defaults, measure, move on"] L -- yes --> M["Sweep size and overlap on the question set"] M --> R{"Is recall flat while context grows?"} R -- yes --> T["Take the smaller chunk size"] R -- no --> U["Take the best recall, then check context cost"]

    How to read this: the first two questions are about the documents, not about numbers, and they settle most cases before any sweep is run.

    • A chunk is the unit that gets scored, shown to the model and cited.
    • Chunk size is a no-op on this corpus from 120 words up: no section is longer than 99 words, and the chunker never merges across headings.
    • Overlap costs storage; on this corpus it did not change recall.
    • Measure on a fixed question set; do not tune by one good answer.

    Scenario

    Composite scenario, not a real company. Lumen Industrial Supply, a fictional distributor, has an assistant that answers from 30 short product documents. One colleague says "bigger chunks give the model more context"; another says "smaller chunks are more precise". Both argue from habit. The team has a 36-question golden set and a retriever it can rebuild in a second, so a table can settle it. The numbers come from running it, except the cost example.

    What you will be able to do

    • Explain how chunk_document forms windows from whole lines and where overlap enters.
    • Run a size and overlap sweep and read recall, chunk count and context words together.
    • Predict when changing chunk size is a no-op.
    • Choose a chunking approach for a new document type and name what to measure.

    Prerequisites

    Core ideas

    How the capstone forms a chunk

    The chunker splits a document at headings (a chunk never crosses one). Inside a section it adds whole lines to a window until the next would exceed the size, then starts a new window that repeats trailing lines worth at most overlap words.

    Verified: capstone/fde-product-discovery-copilot/app/retrieval/chunking.py

    def _windows(lines: list[str], size: int, overlap: int) -> list[list[str]]:
        """Group whole lines into windows of about `size` words; the next window repeats trailing
        lines worth at most `overlap` words."""
        windows: list[list[str]] = []
        current: list[str] = []
        count = 0
        for line in lines:
            n = len(line.split())
            if current and count + n > size:
                windows.append(current)
                current, count = [], 0
                for prev in reversed(windows[-1]):  # carry the overlap
                    pn = len(prev.split())
                    if count + pn > overlap:
                        break
                    current.insert(0, prev)
                    count += pn
            current.append(line)
            count += n
        if current:
            windows.append(current)
        return windows

    Because it groups whole lines, a single 300-word paragraph is never split: at size 120 it stays one 300-word chunk. A test in the lesson folder pins this.

    Figure 2. How size and overlap change the windows

    Setting Windows over six 5-word lines (L1 to L6) Chunks Words stored
    size 10, overlap 0 L1 L2 / L3 L4 / L5 L6 3 30
    size 10, overlap 5 L1 L2 / L2 L3 / L3 L4 / L4 L5 / L5 L6 5 50

    How to read this: with overlap 5 each window repeats the last line of the one before, so you store 50 words for 30 words of text.

    Overlap buys one thing: an answer that straddles a boundary appears whole in one chunk. It costs storage and duplicate hits.

    Predict first

    Predict first. The corpus has 174 sections. The capstone default is size 120 and overlap 20. You raise the size to 480 and rerun the 23 golden questions that expect documents. Does document recall at 3 go up, down, or stay the same?

    Answer

    It stays the same, to the last decimal. The longest section is 99 words, so at size 120 every section is already one chunk, and sections are never merged across headings. The sweep below shows 174 chunks at both settings.

    Estimating cost with a model, then checking it

    The widget below models fixed windows over a document with no headings. Take the longest document, compat-opcua, at about 236 words, with windows of 60 words overlapping by 10, and count the top 3 chunks.

    Windows over the 236-word OPC UA compatibility note (no headings) (interactive when JavaScript is on)
    Chunks5
    Words stored (overlap counted each time)276
    Storage overhead from overlap1.17x the document
    Words sent to the model for the top 3 chunks180

    The starting output is 5 chunks, 276 words stored (1.17 times the document) and 180 words sent to the model for the top 3 chunks. The widget is a model: the real chunker splits compat-opcua at its headings into 6 chunks. Use the widget to feel overlap, the measurement below for facts.

    Worked example

    The sweep runs each setting against the same 23 questions, one row per setting. Rows marked "removed" delete the heading lines first, so a document is one flat section. Read chunks, document recall at 3 and context words together.

    Findings from the real run (pasted below):

    • Heading-aware chunks: recall is 94.9 percent (21 of 23 questions fully found) at every size from 30 to 480 words, with or without overlap.
    • Context words for the 6 retrieved chunks rise from 181 at size 30 to 210 at size 120 and stay there.
    • Without headings, size 120 finds 19 of 23 questions and sends 574.5 words; one chunk per document sends 1,120.9 words.

    The cost of the extra context, with placeholder numbers: 574.5 minus 209.8 is about 365 more words per question. At 1.3 tokens per word (the capstone's estimate) that is about 474 tokens. For 1,000 questions a day, about 474,000 input tokens, or roughly 0.47 euros at a placeholder price of 1 euro per million input tokens. Fill in your provider's published price.

    Assumptions: the 23 public golden questions that expect documents; the query is the raw question; retrieval is the 6 best chunks, at most 3 per document. The capstone index is keyword-only (BM25) with no embeddings, so nothing here measures vector search. The full pipeline also searches by SKU, so these figures do not equal the 94.2 percent in the evaluation report.

    WARNING

    The author who tuned the pipeline also wrote the golden set, so these are regression numbers, not an accuracy estimate. With 23 questions, one question is 4.3 points of the fully-found rate. Do not read a trend into a one-question gap, such as 19 against 20 questions fully found (88.4 against 89.1 percent recall).

    At the customer: "Should we use bigger chunks?" A good answer asks what the documents look like, shows the table for their own corpus with a question set they helped write, and says what result would change the setting.

    Build it

    Work in capstone/fde-product-discovery-copilot. The measurement lives in lessons/M3-L2/ and uses the capstone's own EvidenceStore.

    Verified: capstone/fde-product-discovery-copilot/lessons/M3-L2/m3_l2_chunk_sweep.py

    def measure(size: int, overlap: int, documents=None, items=None, headings: str = "kept") -> Row:
        documents = documents if documents is not None else load_documents(ROOT / "data" / "docs")
        items = items if items is not None else questions_with_documents()
        store = EvidenceStore(documents, size, overlap)
        recalls: list[float] = []
        context_words: list[int] = []
        for q in items:
            hits = store.search(q["question"], PUBLIC, TOP_K, exclude_superseded=True, max_per_doc=MAX_PER_DOC)
            ranked = distinct_docs([h.chunk.ref for h in hits])
            recalls.append(recall_at_k(q["expected_doc_ids"], ranked, 3) or 0.0)
            context_words.append(sum(len(h.chunk.text.split()) for h in hits))
        words = [len(c.text.split()) for c in store.chunks.values()]

    Figure 3. The measurement harness: rebuild the index per setting, replay the same questions

    Chunk size sweep harnessDocument store to Worker (documents); Worker to Search index (chunks); Eval service to Retriever (question); Retriever to Search index (query)Document store: 30 documentsDocument store30 documentsWorker: chunk at size, overlapWorkerchunk at size,overlapSearch index: rebuilt per settingSearch indexrebuilt persettingEval service: 23 questionsEval service23 questionsRetriever: 6 chunksRetriever6 chunksdocumentschunksquestionquery

    How to read this: only the Worker's settings change between runs; the questions, the filter and the scoring stay fixed, so any change in the table comes from chunking.

    Run the sweep and the tests:

    PYTHONPATH=. ./.venv/Scripts/python lessons/M3-L2/m3_l2_chunk_sweep.py
    ./.venv/Scripts/python -m pytest lessons/M3-L2 -q

    This is the real output of the first command (scores are deterministic; nothing here depends on a model or the network):

    headings size overlap chunks mean_words doc_recall@3 fully_found ctx_words
        kept   30       0    201       27.8        94.9%     21/23      181.3
        kept   30      20    204       29.4        94.9%     21/23      184.5
        kept   60       0    178       31.4        94.9%     21/23      201.5
        kept   60      20    179       31.7        94.9%     21/23      205.2
        kept  120       0    174       32.2        94.9%     21/23      209.8
        kept  120      20    174       32.2        94.9%     21/23      209.8
        kept  480       0    174       32.2        94.9%     21/23      209.8
        kept  480      20    174       32.2        94.9%     21/23      209.8
     removed   60       0    124       45.1        93.5%     21/23      288.0
     removed  120       0     62       90.3        88.4%     19/23      574.5
     removed 1000       0     30      186.5        89.1%     20/23     1120.9

    The two misses at every heading-aware setting are q01 and q07. No chunk size or overlap fixes them, so they are not chunking problems: the capstone's evaluation report attributes them to one or two neighbouring documents filling the top results. Removing headings also removes the heading words from the index, so that row measures boundaries and heading text together.

    Exercise

    Decision exercise. Lumen adds 40-page installation manuals. Sections run to about 600 words, written as numbered steps of 20 to 60 words with one step per line, and customers ask questions whose answers span two or three consecutive steps. Options: (A) keep size 120 and overlap 20; (B) size 400 and overlap 0; (C) size 250 and overlap 50, then rebuild the golden set with 15 manual questions. Choose and justify.

    Model answer

    Choose C, with a caveat. A makes windows of about three steps, and an overlap of 20 words carries a step across a boundary only when that step is 20 words or shorter, so most multi-step answers are cut. B has no overlap, so a straddling answer can appear whole in neither chunk, and each 400-word chunk sends far more text per hit. C holds five or six steps per chunk and its overlap of 50 repeats about one step. But 250 and 50 are a starting guess: add manual questions to the golden set and run the sweep before trusting it. If the manuals were long paragraphs, none of the three would help, because the chunker never splits a line; a sentence-level splitting rule would come first.

    Task: add a top_k argument to measure (default 6) in a copy of the file, run sizes 30 and 120 at top_k 3, and report what changed. Acceptance criteria: the existing tests still pass on the original file; your copy has a test that the default behaviour is unchanged; your report states the numbers and whether the gap is bigger than one question. Done when the report and the test are in your lesson folder.

    Hint 1: what to predict first

    Fewer retrieved chunks means each matters more. Write down which size you expect to lose recall before you run it.

    Hint 2: how to read the result

    Count whole questions, not percentages. One question is 4.3 points, so a one-question gap means add questions, not change a setting.

    Common mistakes

    • Tuning chunk size by one good answer instead of a fixed question set.
    • Comparing recall without the context column, which hides the cost of bigger chunks.
    • Assuming a size setting applies when sections are already smaller than it.
    • Treating a one-question difference on a small set as a result.

    Knowledge check

    1. Why did raising the chunk size from 120 to 480 change nothing in the sweep?

    Every section is shorter than 120 words and the chunker never merges across headings, so each section was already one chunk. The sweep shows 174 chunks at both sizes and identical recall.

    2. What does overlap cost, and what does it buy?

    It stores some text twice, so the index is larger and a sentence can appear in two hits. It protects an answer that straddles a boundary, which then appears whole in one chunk. On this corpus it did not change recall.

    3. Why report words in context next to recall?

    Bigger chunks can keep recall level while sending far more text to the model. Without headings, size 120 sent 574.5 words against 209.8, raising cost for every question. Recall alone hides that.

    4. How should you treat the 94.9 percent against 93.5 percent difference between two rows?

    As noise. With 23 questions one question is 4.3 points, and both rows find 21 of 23 fully. Collect more questions before picking a setting.

    5. A document has one 800-word paragraph. What does this chunker do, and what would you do?

    It keeps the paragraph as one 800-word chunk because it only groups whole lines. You would add a rule that splits long lines at sentence boundaries before windowing, then rerun the sweep on questions about that document.

    You can now

    • Describe how heading-aware windows and overlap are formed.
    • Run and read a size and overlap sweep.
    • Predict when a size change is a no-op.
    • Choose a chunking approach for a new document type.

    Further reading

    Last verified: 2026-11-02

    Next

    Search: keyword, vector, hybrid, and choosing a store

    Enroll to keep reading

    Lesson 2 of 10 · Forward Deployed Engineer: RAG, Search and Data

    Change chunk size and overlap one setting at a time, score each on the same 23 golden questions, and learn when chunk size is not the problem.

    In the rest of this lesson

    • Worked example
    • Build it
    • Exercise
    • Common mistakes
    • Knowledge check
    • You can now

    Enrolling is free. It unlocks every lesson in Forward Deployed Engineer: RAG, Search and Data, tracks your progress across devices and keeps your quiz scores. The newsletter is optional.

    Ask about this article

    Get answers grounded in this post. AI-generated — based on this article, and may be imperfect.

    Scaled AI Weekly

    Enjoyed this? Get more like it every Monday.

    Real architecture decisions, LLMOps patterns that survive production, and engineering leadership advice — from 12+ years of building at enterprise scale. Free. No spam. Unsubscribe anytime.

    Get new articles by email

    Free. We email a confirmation link first, and you can unsubscribe from any email.

    Join engineers building production AI systems

    Comments