Naar de inhoud
NLEN
Illustratie: Chunking-strategieën kwantitatief vergelijken voor RAG

Chunking-strategieën kwantitatief vergelijken voor RAG

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

De keuze voor een chunking-strategie bepaalt in hoge mate het succes of falen van een Retrieval-Augmented Generation (RAG) pijplijn. Wanneer brondocumenten in te grote fragmenten worden opgedeeld, overspoelt irrelevante tekst het contextvenster van het taalmodel. Maak je fragmenten daarentegen te klein, dan verdwijnt de samenhang en ontbreken noodzakelijke randvoorwaarden om een vraag feitelijk te beantwoorden. Deze beslissing wordt in de praktijk te vaak genomen op basis van vuistregels of willekeurige standaarden zoals 500 tokens met 10% overlap.

Een doordachte architectuur vereist een kwantitatieve evaluatie: hoe presteert een specifieke splitsingsmethode op jouw corpus in termen van trefzekerheid, signaal-ruisverhouding en uiteindelijke antwoordkwaliteit? In dit artikel formuleren we een reproduceerbare meetopzet waarmee verschillende splitsingsstrategieën objectief met elkaar worden vergeleken. Voor een breder overzicht van de interactie tussen opzoek- en generatiestappen verwijzen we naar het fundament in het artikel over RAG-evaluatie, waarin de complete keten van zoektrap tot modelrespons wordt ontleed.

De afwegingen tussen vaste, semantische en structurele splitsing

Voordat we meetwaarden berekenen, is het noodzakelijk om de fundamentele splitsingsmechanismen te classificeren. Er bestaan grofweg vier dominante chunking-benaderingen, elk met specifieke aannames over tekststructuur en semantische dichtheid:

Wanneer documenten extreem lang en narratief van aard zijn, kan een chunking-strategie zelfs concurreren met direct inlezen via enorme contextvensters. Wie overweegt documenten in hun geheel aan te bieden zonder tussenliggende vectorindex, doet er goed aan het overzicht van modellen voor het samenvatten van lange documenten te raadplegen om de kosten en verwerkingslimieten van ongesplitste contexten te doorgronden.

Isolatie van de zoekfase versus de generatiefase

Een veelgemaakte methodologische fout is het beoordelen van chunking op basis van de uiteindelijke modelantwoorden alleen. Als een model faalt, kan dat liggen aan een slechte chunk (retrieval-fout), maar evengoed aan een redeneerfout van de generator of hallucinatie ondanks perfecte context. Om een chunking-strategie zuiver te kwantificeren, splitsen we de evaluatie op in twee geïsoleerde fasen:

1. Retrieval-kwaliteit (vóór het LLM): Worden de juiste passages teruggevonden uit de database? Bevatten de opgehaalde vectoren de brondata die nodig is om de vraag te beantwoorden?

2. Generatie-efficiëntie (door het LLM): Hoeveel ruis bevatten de opgehaalde brokken, en leidt de gefragmenteerde context tot coherentieverlies of onnodige tokenkosten?

Omdat de trefzekerheid van de retrieval-laag direct samenhangt met de gekozen vectorrepresentatie, moet de invloed van de chunkgrootte altijd worden geëvalueerd in combinatie met het gebruikte embeddingmodel. Zie ook de gids voor het evalueren van embedding-modellen om te verifiëren hoe de effectieve dimensies en contextcapaciteiten van embeddingsvectoren zich verhouden tot je chunklengte.

Kwantitatieve retrieval-statistieken: Recall, MRR en precisie

Om retrieval objectief te meten, is een gecureerde evaluatieset met paren van vragen en ground-truth documentpassages vereist. We definiëren een query $q$, een verzameling relevante document-ID's $R_q$, en een geordende lijst van $k$ opgehaalde chunks $K_q = [c_1, c_2, \dots, c_k]$.

Metriek Formule Wat het meet bij chunking
Recall@k $$\frac{|R_q \cap K_q|}{|R_q|}$$ Of de vereiste informatie überhaupt aanwezig is in de top-$k$ resultaten.
Mean Reciprocal Rank (MRR) $$\frac{1}{|Q|} \sum_{i=1}^{|Q|} \frac{1}{\text{rank}_i}$$ Hoe hoog de eerste relevante chunk in de ranking verschijnt.
Context Precision $$\frac{\sum_{r=1}^k P@r \cdot \mathbb{I}(c_r \in R_q)}{|R_q|}$$ Of relevante chunks bovenaan staan en irrelevante chunks worden vermeden.
Context Token Efficiency $$\frac{\text{Aantal relevante tokens in top-}k}{\text{Totaal aantal opgehaalde tokens}}$$ De signaal-ruisverhouding binnen het contextvenster.

Kleine chunks (bijv. 128 tokens) scoren vaak een hoge Context Precision en Context Token Efficiency, maar kunnen een lagere Recall@k opleveren als een argument over meerdere zinnen verspreid staat. Grote chunks (bijv. 1024 tokens) halen gemakkelijk een hoge Recall@k, maar drukken de Context Token Efficiency aanzienlijk omlaag doordat er honderden irrelevante tokens worden geïnjecteerd.

Illustratief voorbeeld: De onderstaande cijfers tonen een hypothetische vergelijking tussen drie splitsingsvarianten op een technisch testcorpus van 500 documenten.

Strategie Recall@3 MRR Context Token Efficiency Gem. latency retrieval
Vast venster (128 tokens, overlap 20) 0.68 0.54 78% 12 ms
Recursief (512 tokens, overlap 50) 0.89 0.76 52% 15 ms
Structureel / Markdown-secties 0.93 0.84 69% 18 ms

Contextfragmentatie en het meten van trouw

Wanneer een chunking-algoritme een document opsplitst, wordt de omringende context weggesneden. Dit introduceert het risico van contextfragmentatie: een chunk bevat bijvoorbeeld het antwoord "In dit geval bedraagt de opzegtermijn drie maanden", maar de voorwaarde "indien het contract vóór 2021 is getekend" staat in de voorafgaande paragraaf die niet is opgehaald.

Om te evalueren hoe gevoelig een chunking-strategie is voor fragmentatie, meten we de Faithfulness (Trouw) en Answer Relevance van het genererende model onder gecontroleerde omstandigheden. Een lage trouwscore wijst erop dat het generatieve model feiten moet verzinnen omdat randvoorwaarden ontbreken in de aangeleverde broktekst.

De beoordeling van trouw en feitelijke consistentie kan geautomatiseerd worden uitgevoerd met geijkte prompt-evaluatoren. Om de betrouwbaarheid van dergelijke beoordelingsmodellen te borgen, verwijzen we naar de systematiek in het artikel over LLM-as-a-Judge opzetten, waar methoden worden besproken om jury-bias en positievoorkeuren te corrigeren.

Valkuilen in het Nederlandse taalgebied

Bij het benchmarken van chunking op Nederlandstalige corpora treden specifieke taalkundige complicaties op die bij standaard Engelstalige evaluatieruns onzichtbaar blijven:

Tokenkosten, indexeertijd en opslagkwantificering

Een kwantitatieve evaluatie is niet compleet zonder de operationele kostenkant mee te wegen. Kleinere chunks met veel overlap leiden tot een explosie van het totale aantal vectoren in de database, wat de opslagkosten en de rekentijd voor het opbouwen van de index verhoogt. Bovendien beïnvloedt de totale chunkgrootte direct het aantal input-tokens per LLM-aanroep.

Voor een methodologische uitwerking van hoe tokenaantallen vertaald worden naar operationele budgetten, zie de methodiek voor het beheersen van evaluatiekosten. In onderstaande berekening leggen we vast hoe de datadichtheid en API-aanroepen schalen per splitsingsvariant.

# Voorbeeld: Formule voor vectorindex-omvang en API-overhead
# N = aantal brondocumenten
# W = gemiddeld aantal tokens per document
# C = chunk-grootte in tokens
# O = overlap in tokens

aantal_chunks = (W - O) / (C - O)
totale_index_vectoren = N * aantal_chunks
totale_invoer_tokens_per_rag_call = k * C

Als een corpus van 10.000 documenten van elk 2.000 tokens wordt gesplitst met $C=256$ en $O=50$, resulteert dat in circa 94.660 chunks. Kiest men voor $C=1024$ met $O=100$, dan daalt dit naar circa 20.550 chunks. Dit scheelt een factor 4,6 in database-grootte en initiële embedding-kosten.

Een reproduceerbaar experimenteel testprotocol

Om zelf een benchmark uit te voeren die bestand is tegen toevalsfluctuaties, hanteren we een gestandaardiseerd stappenplan. Hierbij worden alle variabelen buiten de chunking-methode strikt constant gehouden.

1. Bepaal de steekproefomvang

Zorg voor een representatieve set van minimaal 100 tot 250 testvragen met geverifieerde bronpassages. Om te berekenen of jouw steekproef statistisch voldoende onderscheidend vermogen biedt, kun je het statistische kader voor testgrootte raadplegen om betrouwbaarheidsintervallen rond je Recall-scores correct te interpreteren.

2. Zet alle overige parameters vast

Houd de volgende factoren identiek over alle testvarianten:

3. Voer het vergelijkend script uit

Hieronder staat een referentie-opzet in Python om retrieval-statistieken te loggen over verschillende chunking-configuraties:

from dataclasses import dataclass
from typing import List, Set

@dataclass
class RetrievalResult:
  query_id: str
  retrieved_chunk_ids: List[str]
  ground_truth_chunk_ids: Set[str]

def calculate_metrics(results: List[RetrievalResult], k: int = 3):
  total_recall = 0.0
  total_mrr = 0.0

  for res in results:
    top_k = res.retrieved_chunk_ids[:k]
    hits = [cid for cid in top_k if cid in res.ground_truth_chunk_ids]
    
    # Recall@k
    recall = len(hits) / max(1, len(res.ground_truth_chunk_ids))
    total_recall += recall
    
    # Reciprocal Rank
    rr = 0.0
    for rank, cid in enumerate(top_k, start=1):
      if cid in res.ground_truth_chunk_ids:
        rr = 1.0 / rank
        break
    total_mrr += rr

  count = max(1, len(results))
  return {
    f"Recall@{k}": round(total_recall / count, 4),
    "MRR": round(total_mrr / count, 4)
  }

Beslisboom voor chunking-selectie

Nadat de kwantitatieve meetresultaten zijn verzameld, kan de definitieve architectuurkeuze worden vastgesteld aan de hand van onderstaande beslisregels:

Door splitsingsparameters niet langer op intuïtie in te stellen maar structureel te meten via Recall@k, Context Token Efficiency en trouwscores, transformeer je de chunking-stap van een onzekere gok naar een geoptimaliseerde engineeringkeuze.