Naar de inhoud
NLEN
Illustratie: Chunking-strategieën voor RAG: documenten opknippen

Chunking-strategieën: documenten slim opknippen voor RAG

Door Ivo Donker — samengesteld met AI-ondersteuning (Claude & Gemini)

Wat je hiervoor moet weten

Dit artikel valt onder Module 3: Werken met je eigen data binnen het leertraject. Voordat we de diepte ingaan met opkniptechnieken, is het raadzaam vertrouwd te zijn met de fundamenten:

Lees eerst het basisartikel over RAG voor beginners om de volledige cyclus van ophalen en genereren te begrijpen. Daarnaast helpt het basisartikel over hoe tokenisatie werkt om te zien hoe ruwe tekst door taalmodellen in discrete eenheden wordt verdeeld. Voor inzicht in de wiskundige representatie van tekstfragmenten kun je de uitleg over embeddings raadplegen.

Bij het bouwen van een Retrieval-Augmented Generation (RAG) systeem ligt de focus vaak op de keuze voor het taalmodel of de gebruikte vectordatabase. In de praktijk blijkt echter dat de kwaliteit van de antwoorden voor een overweldigend deel wordt bepaald door een veel eerdere stap in de pijplijn: het opdelen van bronbestanden in behapbare stukken tekst, ook wel chunking genoemd.

Wanneer een document verkeerd wordt opgeknipt, kan het meest geavanceerde taalmodel het juiste antwoord niet formuleren. Knip je te grof, dan verdrinkt de specifieke informatie in een zee van irrelevante context en overschrijd je onnodig het contextbudget. Knip je te fijn, dan raakt de kernboodschap versnipperd en verliest het zoekalgoritme de samenhang die nodig is om de vraag semantisch te koppelen. In dit artikel behandelen we systematisch de belangrijkste chunking-methoden, hun sterke en zwakke punten, en hoe je de optimale strategie selecteert voor uiteenlopende documenttypen.

De spagaat van chunking: contextbehoud versus zoekprecisie

Elke chunking-strategie probeert een fundamentele paradox op te lossen. Aan de ene kant vereist vectorzoek functionaliteit (dense retrieval) compacte en gefocuste tekstsegmenten. Een embeddingmodel probeert de algehele betekenis van een passage samen te persen in één enkele vector. Bevat zo'n segment tien verschillende onderwerpen, dan verwatert de resulterende vector en daalt de trefzekerheid bij gerichte zoekvragen.

Aan de andere kant heeft het uiteindelijke taalmodel (de generator) juist voldoende omliggende context nodig om nuances, voorwaarden en definities correct te interpreteren. Een zin zoals "Deze uitzondering geldt uitsluitend voor medewerkers in schaal 8 of lager" is feitelijk waardeloos voor een LLM als de voorafgaande alinea—waarin de daadwerkelijke regeling wordt gedefinieerd—in een ander tekstfragment is beland.

Bij het ontwerpen van de opknipstrategie balanceren we voortdurend tussen twee risico's: contextfragmentatie (waarbij cruciale betekenis verloren gaat op de snijlijn) en retrieval-vervuiling (waarbij te veel ruis wordt geïnjecteerd in de prompt). Een dieper inzicht in de praktische vectorberekening vind je in het artikel over de praktische toepassing van vector-embeddings, waarin de wiskundige gevolgen van verwaterde vectoren worden gedemonstreerd.

Vaste vensters met overlap (Fixed-size chunking)

De meest basale en wijdverbreide aanpak is het opknippen van tekst op basis van een vast aantal tokens of karakters, gecombineerd met een constante overlap. Een gangbare configuratie is bijvoorbeeld een venstergrootte van 512 tokens met een overlap van 50 tokens (ongeveer 10%).

Het mechanisme is eenvoudig: een teller loopt door de tekstreeks en start na elke 462 nieuwe tokens een nieuw blok dat de laatste 50 tokens van het vorige blok meeneemt. De overlap fungeert als vangnet om te voorkomen dat een zin precies doormidden wordt gesneden en zijn betekenis verliest.

# Voorbeeld van vaste vensterverdeling in Python
def fixed_size_chunk(tokens: list[str], chunk_size: int = 512, overlap: int = 50):
    chunks = []
    step = chunk_size - overlap
    for i in range(0, len(tokens), step):
        chunk = tokens[i:i + chunk_size]
        chunks.append(chunk)
        if i + chunk_size >= len(tokens):
            break
    return chunks

Hoewel deze methode computationeel zeer goedkoop en eenvoudig te implementeren is, kent zij ernstige nadelen. Het algoritme is volledig blind voor syntaxis en semantiek. Het snijdt dwars door alinea's, opsommingen, tabellen en soms zelfs zinsconstructies heen. Voor ongestructureerde platte tekst zonder strikte hiërarchie kan het volstaan, maar voor contracten, handleidingen of beleidsstukken levert het structureel kwaliteitsverlies op.

Recursieve karakter- en zinsplitsing

Om te voorkomen dat natuurlijke teksteenheden bruut worden doorgesneden, maakt men veelvuldig gebruik van recursieve splitters (zoals de RecursiveCharacterTextSplitter). Deze methode probeert de tekst stapsgewijs op te splitsen aan de hand van een geordende lijst met scheidingstekens, van groot naar klein.

Het algoritme probeert eerst te splitsen op dubbele regeleindes (alineagrenzen). Zijn de resulterende alinea's nog steeds groter dan de ingestelde doellengte, dan valt het terug op enkele regeleindes. Is een regel alsnog te lang, dan splitst het op zinsbeëindigende leestekens (punten, uitroeptekens, vraagtekens), daarna op spaties tussen woorden, en pas in het uiterste geval op individuele karakters.

# Schematische werking van recursieve scheidingstekens
separators = [
    "\n\n",  # 1. Alineagrens (behoudt thematische eenheid)
    "\n",    # 2. Regelgrens (behoudt lijsten en strofen)
    ". ",    # 3. Zinsgrens (behoudt grammaticale eenheid)
    " ",     # 4. Woordgrens (voorkomt halve woorden)
    ""       # 5. Noodgreep (karakterniveau)
]

Door deze hiërarchie blijven logische alinea's en zinnen zoveel mogelijk intact. De resulterende tekstbrokken voelen natuurlijk aan en bevatten zelden gebroken zinsdelen. Desondanks blijft de methode fundamenteel regelgebaseerd: het algoritme "weet" niet of twee opeenvolgende alinea's inhoudelijk bij elkaar horen of juist een compleet nieuw onderwerp inleiden.

Semantische chunking: splitsen op basis van embedding-afstanden

In plaats van te vertrouwen op visuele witruimte of statische karakterlimieten, meet semantische chunking de daadwerkelijke inhoudelijke verschuiving in een tekst. Hierbij wordt het document eerst opgeknipt in afzonderlijke zinnen. Vervolgens wordt voor elke zin (of een klein glijdend venster van zinnen) een embedding berekend.

Het algoritme berekent de cosinus-afstand tussen de vectoren van opeenvolgende zinnen. Zolang de opeenvolgende zinnen thematisch nauw verwant zijn, blijft de afstand klein. Zodra de auteur overschakelt naar een ander onderwerp, ontstaat er een plotselinge sprong in de vectorafstand. Wanneer deze afstand een vooraf bepaalde drempelwaarde (bijvoorbeeld het 95e percentiel van alle gemeten afstanden) overschrijdt, plaatst het systeem een splitsing.

# Conceptuele detectie van semantische grenzen
cosine_distance = 1.0 - cosine_similarity(embedding_a, embedding_b)

if cosine_distance > dynamic_threshold:
    # Betekenisverschuiving gedetecteerd: begin een nieuw tekstblok
    create_new_chunk()
else:
    # Onderwerp loopt door: voeg zin toe aan huidig blok
    append_to_current_chunk()

De kracht van semantische chunking ligt in de adaptieve lengte: homogene passages blijven één samenhangend geheel, terwijl compacte alinea's met veel inhoudelijke wisselingen snel worden opgedeeld. De zwakke plek is de computationele overhead. Voor een document van 100 pagina's moeten honderden individuele API-aanroepen of lokale embedding-berekeningen worden uitgevoerd, enkel en alleen om de snijpunten te bepalen. Wie lokale modellen inzet voor deze taak kan de gids over lokaal documenten doorzoeken met AI raadplegen voor praktische overwegingen rondom rekenkracht en doorvoersnelheid.

Documentbewuste en structurele chunking

Veel zakelijke documenten—zoals jaarverslagen, software-specificaties, juridische wetsteksten en Markdown-bestanden—bezitten al een expliciete formele structuur. Documentbewuste (of layout-aware) chunking benut deze opmaak in plaats van platte tekst te forceren.

Bij documentbewust opknippen worden koppen (H1, H2, H3) geanalyseerd om een boomstructuur van het document op te bouwen. Elke chunk erft de metadata van zijn bovenliggende secties. Een paragraaf onder "3.1 Uitzonderingen op het retourbeleid" krijgt die volledige padstructuur als contextuele metadata meegeleverd in de vectorindex.

Omgaan met tabellen en lijsten

Tabellen vormen een berucht knelpunt in RAG-pijplijnen. Wanneer een vaste-groottensplitter midden in een HTML- of Markdown-tabel snijdt, verdwijnen de kolomkoppen en worden getallen volledig losgezongen van hun labels. Een cel met de waarde "€ 45.000" zegt niets zonder de corresponderende rij- ("Senior Developer") en kolomnaam ("Maximum salaris").

Structurele splitters isoleren tabellen daarom als ondeelbare eenheden. Als een tabel te groot is voor het embedding-venster, wordt deze getransformeerd: elke rij wordt omgezet in een zelfstandige, leesbare zin (bijvoorbeeld: "Functie: Senior Developer | Maximum salaris: € 45.000 | Schaal: 11"). Hierdoor blijft de relatie tussen velden intact, ongeacht waar de splitsing valt.

Geavanceerde patronen: Parent-Document en Hiërarchische Retrieval

Om het dilemma tussen kleine zoekvectoren en rijke generatiecontext definitief op te lossen, hanteren moderne architecturen een ontkoppelde aanpak: het Parent-Document of Small-to-Big patroon.

In dit scenario wordt het bronbestand op twee niveaus tegelijk verwerkt:

Tijdens de retrievalfase zoekt het systeem naar de best passende child-vector. Maar in plaats van dat kleine fragment direct naar het taalmodel te sturen, haalt de applicatie via een referentie-ID het volledige bovenliggende parent-document op. Hierdoor profiteert de zoekstap van de scherpe precisie van kleine vectoren, terwijl het genererende model beschikt over de volledige thematische context.

Hoe deze opzet schaalt binnen grotere databases en indexstructuren lees je in het artikel over vector-indexering met HNSW en IVF, waarin indexeringspatronen voor grote vectorcollecties worden behandeld.

Late Chunking en Contextual Retrieval

Een recente innovatie binnen het veld is Late Chunking (geïntroduceerd rond 2024–2025). Traditioneel knip je eerst de tekst op en voer je daarna elk fragment afzonderlijk door het embeddingmodel. Hierdoor ontbreekt bij de embeddingstap elke vorm van interactie tussen opeenvolgende alinea's.

Bij Late Chunking wordt het volledige document (of een zeer grote sectie tot 8192 tokens) in één keer door de transformer-encoder gestuurd. Pas in de laatste laag van het netwerk, nadat alle token-representaties via het self-attention-mechanisme informatie hebben uitgewisseld over het hele document, worden de token-embeddings gegroepeerd en gepoold tot afzonderlijke chunk-vectoren.

Het gevolg is opmerkelijk: de vector van een individuele alinea bevat nu subtiele sporen van informatie uit de introductie en eerdere hoofdstukken. Verwijzingen zoals "dit systeem" of "zoals hierboven vermeld" dragen de betekenis van hun antecedenten met zich mee in de vectorruimte, zonder dat de chunk fysiek groter hoeft te zijn.

Voor wie werkt met Nederlandstalige documenten is de keuze voor het onderliggende model hierbij bepalend; zie hiervoor het overzicht over het juiste embeddingmodel voor Nederlandse documenten kiezen om te zien welke modellen lange sequenties en cross-token interactie ondersteunen.

Vergelijking van chunking-strategieën

De onderstaande tabel zet de vijf belangrijkste benaderingen tegenover elkaar op basis van rekencomplexiteit, contextbehoud en ideale toepassingsgebieden.

Strategie Complexiteit Contextbehoud Beste toepassing Grootste nadeel
Vast venster Zeer laag Matig tot slecht Eenvoudige platte tekst, snelle prototypes Knipt willekeurig door zinnen en tabellen
Recursief Laag Redelijk tot goed Artikelen, algemene documentatie Houdt geen rekening met thematische overgangen
Semantisch Hoog Zeer goed Verhalende teksten, wisselende betogen Traag en duur door vele embedding-aanroepen
Parent-Document Gemiddeld Uitstekend Complexe handleidingen, rapporten Vereist dubbele opslag (vectoren + document store)
Late Chunking Hoog Uitzonderlijk Lange juridische en technische dossiers Vereist toegang tot ruwe model-hidden-states

Kwaliteitsmeting en evaluatie van chunks

Het optimaliseren van chunk-parameters (zoals grootte en overlap) gebeurt te vaak op basis van buikgevoel. Om de effectiviteit wetenschappelijk vast te stellen, meten we twee specifieke retrieval-metrieken:

Daarnaast speelt contextbeheer in de prompt een directe rol bij de prestaties van het generatiemodel. Hoe je de opgehaalde chunks optimaal rangschikt en structureert binnen het beschikbare venster wordt uitgebreid behandeld in de context-engineering gids.

Valkuilen in de praktijk: Nederlandse voorbeelden

In de Nederlandse taalpraktijk treden specifieke complicaties op die bij standaard Engelstalige tutorials vaak over het hoofd worden gezien:

Samengestelde woorden en afkortingen: Het Nederlands staat vol met lange samenstellingen (zoals arbeidsongeschiktheidsverzekeringsvoorwaarden) en specifieke afkortingen met punten (zoals m.b.t., t.a.v., d.w.z., art.). Een naïeve zinsplitter die louter op . splitst, breekt een zin middenin een wetsartikel zoals "Ingevolge art. 7:610 BW is..." genadeloos af.

Opsommingen in beleidsstukken: Nederlandse overheids- en CAO-documenten maken intensief gebruik van gelaagde opsommingen (artikel 4, lid 2, sub a). Wanneer een splitter lid 2 loskoppelt van de hoofdtitel van artikel 4, ontbreekt elk referentiekader. Hier is structurele metadata-injectie noodzakelijk: plak altijd het pad Document > Hoofdstuk > Artikel als prefix voor de tekst van de chunk.

Hierna verder met

Nu de theorie en praktijk van chunking helder zijn, kun je de volgende stap zetten in de verwerkingsketen:

Onderzoek hoe je de resulterende vectoren optimaal structureert en doorzoekt in vector-indexering met HNSW en IVF. Wil je vervolgens zien hoe je deze opgehaalde tekstblokken zonder kwaliteitsverlies integreert in geavanceerde prompts, vervolg dan met de context-engineering gids.