← Blog

RAG

RAG chunking-strategieën: negen getest op 92k documenten

Negen chunking-strategieën. Eén archief van 92.000 documenten van een Nederlandse gemeente. Echte queries van werkende archivarissen. Dit verschoof recall, en de techniek die hybrid BM25 versloeg.

Jacob Molkenboer· Oprichter · A Brand New Company· 6 jun 2026· 9 min
Open eiken kaartenbak op ivoren bureau, één tab omhoog met groen vlaggetje, koperen scheider, stapel crème papier.

Het is een dinsdagochtend in een gemeentearchief ergens in de Randstad. Een archivaris typt een query in de beta-zoektool die we hebben gebouwd: welke moties zijn er aangenomen over windenergie tussen 2018 en 2022? Het systeem moet 92.000 documenten teruglezen. Raadsbesluiten, moties, collegebrieven, bestemmingsplannen, de meeste in PDF, sommige gescand met slechte OCR, een handvol nog in DOCX uit 2009. De verkeerde chunk-grens betekent dat de juiste motie twee alinea's verderop staat dan de vector die de zoekactie als eerste teruggeeft. Recall is het hele spel.

We hebben het voorjaar van 2026 besteed aan het benchmarken van negen chunking-strategieën op dat corpus. Hetzelfde embedding-model, dezelfde reranker, dezelfde queryset. Alleen de chunker veranderde. Dit is de veldgids die we op dag één hadden willen hebben.

Het corpus en de queries

Het archief bevat 92.184 documenten van één Nederlandse gemeente, teruglopend tot 2002. De mediane lengte ligt rond de 1.400 woorden; de staart loopt door tot voorbij 80.000 woorden voor omgevingsvergunningen met bijlagen. Ongeveer 71% is born-digital PDF, 22% gescande PDF (we hebben alles opnieuw door Surya gehaald voor het indexeren), en de rest is een mix van DOCX, ODT en HTML uit de oude Joomla-site.

Onze evaluatieset bestaat uit 612 queries, geschreven door drie werkende archivarissen, elk gekoppeld aan één tot twaalf ground-truth documenten die ze al met de hand hadden gevonden. De metriek is recall@10 op documentniveau: kwam er minstens één chunk uit een doeldocument in de top tien?

Het embedding-model is constant gehouden op jina-embeddings-v3, een meertalig model met een contextvenster van 8.192 tokens. De retriever is een platte HNSW-index. De reranker is BGE-reranker-v2-m3 over de top 50. Niets in die stack verandert tussen runs. Alleen de chunker beweegt.

De baseline die je moet verslaan

De meeste productie-RAG-systemen die we bij gemeenten en advocatenkantoren langs zien komen draaien op een hybride retriever: BM25 over chunks plus dense embedding-retrieval, samengevoegd met reciprocal rank fusion. Dat is de lat. Op dit corpus haalt hybrid BM25 plus dense (chunks op vaste 512 tokens) recall@10 = 0,78. Alles daaronder is een regressie; alles daarboven moet de operationele kosten rechtvaardigen.

Negen chunkers, naast elkaar

Elke chunker is minstens een volle dag afgesteld en daarna vastgezet. De onderstaande cijfers zijn recall@10 op documentniveau tegen de 612 gelabelde queries. Dezelfde dense-only retrieval, geen hybrid, geen reranker, zodat je ziet wat de chunker zelf doet.

#ChunkerToelichtingRecall@10
1Vaste 512 tokens, geen overlapDe stropop.0,58
2Vaste 512 tokens, 64 tokens overlapOude standaard.0,63
3Recursieve character-splitterLangChain-stijl, 1000 / 200.0,66
4Zin-bewust (Nederlandse spaCy)4 tot 6 zinnen per chunk.0,61
5Alinea-bewustNatuurlijke alinea-breaks, max 800 tokens.0,65
6Sliding window 1024 / 256Breder venster, meer redundantie.0,69
7Semantisch (embedding-drop)Cosinus-breekpunt tussen zinnen.0,71
8Structuur-bewust (kopjes)Gebruikt besluit / motivering / bijlage als kapstok.0,74
9Late chunkingEerst embedden, dan chunken.0,81

Hybrid BM25 plus dense over strategie #1 komt op 0,78. Late chunking alleen, zonder BM25, verslaat dat. Met BM25 erbij komt late chunking op 0,86. Dat is de kop boven dit verhaal.

Waarom fixed chunking verloor (en waarom iedereen het toch nog uitrolt)

Fixed-size chunking zit in elke quickstart om een goede reden: deterministisch, snel, triviaal parallel. Het gooit ook elk signaal weg dat het document je gaf. Een motie in een Nederlands raadsbesluit heeft een voorspelbare vorm: Beslispunt, Motivering, Stemverhouding. Hak dat in stukken van 512 tokens en het beslispunt staat in de helft van de gevallen los van de motivering. De embedding is dan een soep van twee onsamenhangende argumenten.

De goedkoopste oplossing is ook de meest onderschatte: loop eerst de documentstructuur af. Strategie #8 (kopje-bewust) was vier dagen werk, voornamelijk regex en een markdown-converter, en leverde 16 punten recall extra op boven de fixed baseline. Als je dit kwartaal niks anders kunt opleveren, lever dat dan op.

Late chunking, in één alinea

Late chunking draait de gebruikelijke volgorde om. In plaats van het document in chunks te knippen en elke chunk los te embedden, embed je het hele document één keer onder volledige attention en pool je daarna de token-embeddings tot chunks. De vector van elke chunk draagt nu context uit het volledige document. De Stemverhouding-chunk onderaan een motie weet dat hij bij die motie hoort. De techniek is geformaliseerd door Jina AI in Günther et al., 2024, en werkt alleen met long-context embedding-modellen. Onder de 8k context is het niet interessant.

from transformers import AutoModel
import torch

model = AutoModel.from_pretrained(
    "jinaai/jina-embeddings-v3", trust_remote_code=True
)
tok = model.tokenizer

# 1. Tokenise the whole document once.
doc = open("besluit-2022-431.txt").read()
inputs = tok(doc, return_tensors="pt", truncation=True, max_length=8192)

# 2. Token embeddings under full-document attention.
with torch.no_grad():
    hidden = model(**inputs).last_hidden_state[0]

# 3. Pick chunk boundaries however you like (headings work well).
boundaries = pick_boundaries_from_outline(doc, tok)  # [(start, end), ...]

# 4. Mean-pool token vectors inside each chunk.
chunk_vectors = [hidden[s:e].mean(dim=0) for s, e in boundaries]

Dat is de hele truc. De grenzen kunnen uit elk van de andere acht strategieën komen. Wij gebruikten kopje-bewuste grenzen binnen de late-chunking-pipeline, en daarom leest strategie #9 als een superset van strategie #8.

Let op

Late chunking is niet gratis. Een bestemmingsplan van 60.000 tokens in één keer embedden kost ongeveer 14x meer GPU-seconden dan datzelfde document als 120 losse chunks van 512 tokens embedden. Reserveer er budget voor, of batch je lange documenten 's nachts.

Waar de andere kandidaten stukgingen

Een paar resultaten verrasten ons genoeg om ze hier te benoemen.

Zin-bewust deed het slechter dan alinea-bewust

We verwachtten dat de Nederlandse spaCy-zinssplitter beter zou presteren dan alinea-splitting. Dat bleek niet zo. Raadsbesluiten halen gemiddeld 38 woorden per zin en zelden minder dan drie zinnen per alinea; zes zinnen in een chunk proppen leverde lelijke grenzen op waar twee derde van een motie in de ene chunk zat en de beslissende regel (draagt het college op) in de volgende.

Semantische chunking was wisselvallig

Embedding-drop chunking (#7) ziet er goed uit op een schone beleidsnota en valt uit elkaar op een gescande PDF uit 2009 met OCR-ruis. De cosinus-"grenzen" landden middenin zinnen, rond OCR-vuilnis. We probeerden het opnieuw met een schoonmaakronde en haalden het meeste verschil in, maar versloegen structuur-bewust nooit.

Sliding windows ruilden recall in voor latency

Het sliding window op 1024 / 256 hielp de recall (0,69) maar verdrievoudigde de index-grootte, en het reranken van de top-50 werd het nieuwe knelpunt. Op een vier jaar oud archief maakt dat minder uit; op een levend corpus dat elke maand met 1.200 documenten groeit, is het het verschil tussen een Postgres-rekening van €180 / mnd en €640 / mnd.

Wat je eerst probeert, op volgorde

Als je nu vaste chunks van 512 tokens zonder overlap uitrolt, dit is de volgorde die we een klein team zouden meegeven.

  1. Voeg overlap toe (64 tokens). Eén regel code. Levert ongeveer 5 punten op.
  2. Loop de documentstructuur af. Kopjes, genummerde secties, frontmatter. Ongeveer 10 tot 12 punten erbij.
  3. Voeg hybrid BM25 toe als je het nog niet hebt. Ongeveer 4 punten en het vangt de gevallen op die dense retrieval mist (eigennamen, dossiernummers).
  4. Pas daarna overweeg je late chunking. Het vraagt om een long-context embedding-model, GPU-budget en een venster om opnieuw te indexeren. De recall-winst is reëel, maar het zijn de laatste 5 punten, niet de eerste.
Onthou dit

Documentstructuur verslaat slimme embeddings negen van de tien keer. Gebruik de kopjes die de auteur al voor je heeft gemaakt, voordat je naar semantische trucs grijpt.

De kostenvraag die niemand ons stelde

Eén ding dat de benchmark niet vastlegde: tokenisatiekosten verschuiven op schaal. Het opnieuw embedden van het 92.000-documenten-archief met late chunking en jina-embeddings-v3 kostte ons ongeveer €240 aan GPU-tijd op een gehuurde A100, tegenover €18 voor de fixed-chunk baseline. Voor de gemeente is dat wisselgeld vergeleken met een archivaris-uurtje. Voor een SaaS-prijsmodel waarin je documenten van gebruikers on the fly ingest, is het een ander gesprek. Kleinere, gequantiseerde modellen zoals Google's Gemma 3 QAT-release beginnen on-laptop inference ook aan de embedding-kant aannemelijk te maken. Daar houden we de volgende benchmark scherp in de gaten.

Wat we hebben opgeleverd

Toen we dit voor de gemeente bouwden, was het probleem niet de chunker. Het was de OCR. Ongeveer 22% van het archief kwam binnen als scan, en het recall-verschil tussen Tesseract en Surya was groter dan het verschil tussen welke twee chunkers dan ook op deze lijst. We hebben Surya uiteindelijk twee keer gedraaid op alles wat onzeker leek en de outputs met elkaar verzoend. Dat is de onsexy helft van elk RAG-systeem dat we in productie zetten.

Als je vandaag maar één ding doet, open dan drie documenten uit je corpus en kijk waar de kopjes staan. Als de structuur er is, hoort je chunker hem te gebruiken voordat je nog een paper leest.

Kern

Documentstructuur verslaat slimme embeddings negen van de tien keer. Gebruik de kopjes die de auteur al voor je heeft gemaakt, voordat je naar semantische trucs grijpt.

FAQ

Waarom versloeg late chunking hybrid BM25 plus dense op dit corpus?

Hybride retrieval helpt wanneer chunks zelf zwak zijn. Late chunking maakt de chunks sterker, omdat elke vector volledige documentcontext meedraagt. Dense alleen vangt dan al af wat hybrid eerder lapte.

Heb ik specifiek jina-embeddings-v3 nodig, of werkt elk embedding-model?

Je hebt een long-context model met bidirectionele attention nodig. Onder de 8k tokens context heeft late chunking niets om uit te poolen. Jina v3, Nomic v2 en BGE-M3 voldoen allemaal; de meeste short-context modellen niet.

Is kopje-bewuste chunking genoeg als ik late chunking niet kan betalen?

Op gestructureerde corpora (juridisch, beleid, technische documenten) wel. Kopje-bewust bracht ons van 0,58 naar 0,74 recall@10 voor een minimale GPU-investering. Late chunking voegt daar nog 7 punten bovenop.

Hoe groot moet het corpus zijn voordat deze benchmark geldt?

We zagen dezelfde volgorde op 5.000 documenten en op 92.000. De recall-cijfers schuiven, maar kopje-bewust versloeg altijd fixed-size en late chunking stond altijd bovenaan de tabel.

En de OCR-kwaliteit?

OCR is belangrijker dan chunking-strategie op gescande corpora. We zagen grotere recall-verschillen tussen Tesseract en Surya dan tussen welke twee chunkers dan ook op deze lijst. Repareer eerst je OCR.

ragknowledge basearchitecturecase studytoolingai agents

Iets bouwen?

Start een project