Engineering Blog

RAG Engineering

RAG chunking: preserve source positions and test retrieval units

A runnable offset-preserving splitter and a practical review of context, tables, document revisions, and token limits.

RAGO-XPublished Updated
#RAG#Chunking#Architecture
Concept diagram of document chunking for RAG

Splitting a document into equal lengths does not automatically produce usable evidence. If “disconnect power before opening the cover” is divided between its condition and action, retrieval may return only the action. The goal is to preserve answerable context together with its source position.

This article builds a character-based baseline with Python 3. Its size and overlap values are experimental choices, not RAGO-X production defaults.

Inspect extraction before splitting

Broken reading order in multi-column PDFs, repeated headers, and misplaced table cells cannot be repaired by adjusting chunk size. Check headings, paragraphs, tables, and footnotes against the original. Prefer sections and paragraphs as initial units and split long paragraphs further when necessary.

A table row often needs column names and units. For long tables, associate each piece with its caption and headers and retain page boundaries. If you add headers to a piece, store the original source span separately from context added for retrieval.

A baseline that preserves offsets

Save the following as chunk_demo.py and run python3 chunk_demo.py. Half-open intervals [start, end) ensure that text[start:end] reproduces the chunk.

python
def split_text(text, size=80, overlap=15):
    if size <= 0 or not 0 <= overlap < size:
        raise ValueError("require size > 0 and 0 <= overlap < size")
    start = 0
    while start < len(text):
        end = min(start + size, len(text))
        yield {"start": start, "end": end, "text": text[start:end]}
        if end == len(text):
            break
        start = end - overlap


text = "Pump X17: disconnect power. Wait 30 seconds. Check the inlet filter."
chunks = list(split_text(text, size=40, overlap=10))
print([(c["start"], c["end"]) for c in chunks])
assert [(c["start"], c["end"]) for c in chunks] == [(0, 40), (30, 68)]
assert all(text[c["start"]:c["end"]] == c["text"] for c in chunks)
assert set().union(*(set(range(c["start"], c["end"])) for c in chunks)) == set(range(len(text)))
assert list(split_text("")) == []

The output is [(0, 40), (30, 68)]: two spans overlap by 10 characters and cover the full input. Empty input yields no chunks; invalid overlap raises an error. The function does not strip or normalize the input, preserving offset comparisons.

Python string length is not byte length, a count of visual grapheme clusters, or a model token count. Check the actual model limit with its tokenizer. This baseline also ignores sentence and heading boundaries; it is not a complete production splitter.

Preserve enough metadata

Information Purpose
Document ID and revision Distinguish old and current content
Chunk ID and splitting version Trace reprocessing
Page, section, and offsets Let readers verify the original
Organization, cabinet, and access scope Retrieve only permitted material
Original span and added context Separate quotation from retrieval aids

Offsets must identify the version of extracted text they refer to. OCR or whitespace changes can move the same passage. When replacing a document, switch chunks and indexes consistently and verify that obsolete revisions are no longer retrieved. These are design concepts, not a public API schema.

Label evidence before tuning size

Mark the source passages that answer real questions, then compare two splitting configurations. Check whether top results contain the answer and its exceptions, and whether opening the source shows the matching revision. More chunks or longer answers are not evidence of improvement.

Symptom First experiment
Condition separated from conclusion Prefer sentence/paragraph boundaries and adjacent context
Several topics in one large chunk Split by heading and compare retrieval
Repeated evidence fills context Reduce overlap or deduplicate adjacent results
Table numbers misinterpreted Keep headers, units, and footnotes
Wrong citation position Inspect extraction version and source mapping

Overlap may reduce boundary loss but also increases storage and redundant candidates. Record the question set, input tokens, latency, and citation accuracy instead of assuming a universally optimal size. Continue with hybrid retrieval evaluation and the evidence-to-answer workflow.