Chunkingstrategieën: fixed, sentence en recursive
U implementeert en vergelijkt text splitters met vaste grootte, op zinsgrenzen en recursief, en begrijpt hoe de chunkgrootte en overlap de retrievalkwaliteit beïnvloeden.
Chunkingstrategieën: fixed, sentence en recursive is een gratis AI Engineering Academy-les op CoddyKit. Dit is les 2 van 4. Je kunt de volledige les hieronder gratis lezen en daarna in de browser praktisch oefenen met een ingebouwde code-editor en een AI-begeleider die 24/7 beschikbaar is. Deze les maakt deel uit van het leertraject AI Engineering Academy. Je voortgang wordt gesynchroniseerd op het web en in de CoddyKit-app. De cursus AI Engineering Academy bevat in totaal 4 lessen.
<span>Waarom de kwaliteit van tekstblokken belangrijk is</span>
Het opdelen in tekstblokken is het proces waarbij geladen documenten worden opgesplitst in kleinere stukken die binnen het contextvenster van het LLM passen en afzonderlijk kunnen worden geïndexeerd en opgehaald. De manier waarop je tekst opdeelt, bepaalt de kwaliteit van zoekresultaten sterker dan vrijwel elke andere factor. Als een tekstblok een antwoord over twee stukken verdeelt, is geen van beide stukken afzonderlijk voldoende om de vraag te beantwoorden. Een tekstblok waarin twee niet-gerelateerde onderwerpen worden vermengd, wordt opgehaald voor vragen over beide onderwerpen, maar is voor geen van beide nuttig.
<span>Tekstblokken met vaste grootte</span>
Tekst opdelen in blokken met vaste grootte splitst tekst op in blokken van precies N tekens of N tokens, ongeacht de grenzen van zinnen of alinea's. Dit is de eenvoudigste strategie en snel te implementeren. Het grootste nadeel is dat zinnen vaak middenin worden afgebroken, waardoor blokken midden in een gedachte beginnen of eindigen. Dit is acceptabel voor dichte, uniform opgemaakte tekst, zoals dumps van database-exporten, maar levert slechte zoekresultaten op bij verhalende tekst of technische documentatie.
def fixed_size_chunks(text, chunk_size=500, overlap=50):
'''Split text into fixed-size character chunks with overlap'''
chunks = []
start = 0
while start < len(text):
end = start + chunk_size
chunk = text[start:end]
chunks.append(chunk)
start += chunk_size - overlap # overlap keeps context at boundaries
return chunks
example = 'This is a long document. ' * 100
chunks = fixed_size_chunks(example, chunk_size=200, overlap=20)
print(f'Produced {len(chunks)} chunks, first: {chunks[0][:80]}...')<span>Overlap toevoegen aan blokken met vaste grootte</span>
De belangrijkste verbetering voor blokken met vaste grootte is overlap: elk blok deelt N tekens met het vorige blok. Zo staat informatie bij een blokgrens in beide aangrenzende blokken. Met een overlap van 50-100 tokens wordt een zin die een grens overschrijdt volledig opgenomen in ten minste één van de twee blokken. Meer overlap betekent een betere dekking, maar ook een grotere index en dubbele inhoud in de zoekresultaten.
# Overlap example with a sentence at the boundary
text = 'A B C D E F G H I J'
chunk_size = 6 # characters
overlap = 2
i = 0
while i < len(text):
print(repr(text[i:i+chunk_size]))
i += chunk_size - overlap
# Output shows overlapping content:
# 'A B C D'
# 'C D E F'
# 'E F G H'
# Each adjacent pair shares 2 chars<span>Tekst opdelen op zinsgrenzen</span>
Tekst opdelen op zinsgrenzen gebruikt NLP-bibliotheken zoals nltk of spacy om het einde van zinnen te detecteren voordat de tekst wordt opgesplitst. Zo wordt gegarandeerd dat geen enkele zin middenin wordt afgebroken. Je verzamelt zinnen totdat het toevoegen van de volgende zin de beoogde grootte zou overschrijden en begint dan een nieuw blok. Het resultaat bestaat uit blokken die altijd complete gedachten bevatten, wat een aanzienlijk betere kwaliteit van embeddings oplevert dan splitsen op een vast aantal tekens.
import nltk
nltk.download('punkt', quiet=True)
def sentence_chunks(text, max_tokens=300):
sentences = nltk.sent_tokenize(text)
chunks = []
current = []
current_len = 0
for sent in sentences:
sent_tokens = len(sent.split())
if current_len + sent_tokens > max_tokens and current:
chunks.append(' '.join(current))
current = []
current_len = 0
current.append(sent)
current_len += sent_tokens
if current:
chunks.append(' '.join(current))
return chunks<span>Recursief splitsen van tekst op tekens</span>
Recursief splitsen op tekens is de meest gebruikte strategie in RAG-systemen voor productie. De strategie probeert een hiërarchie van scheidingstekens in volgorde: eerst alinea-einden (\n\n), daarna regeleinden (\n), vervolgens punten aan het einde van zinnen, daarna spaties en ten slotte afzonderlijke tekens. De tekst wordt gesplitst op de grootste betekenisvolle grens waarbij het blok onder de beoogde grootte blijft. Zo respecteren de blokken de documentstructuur zo goed mogelijk.
from langchain_text_splitters import RecursiveCharacterTextSplitter
splitter = RecursiveCharacterTextSplitter(
chunk_size=1000, # target size in characters
chunk_overlap=200, # overlap between consecutive chunks
separators=['\n\n', '\n', '. ', '! ', '? ', ' ', ''],
length_function=len
)
with open('document.txt') as f:
text = f.read()
chunks = splitter.split_text(text)
print(f'Split into {len(chunks)} chunks')
for i, chunk in enumerate(chunks[:3]):
print(f'Chunk {i}: {len(chunk)} chars — {chunk[:60]}...')<span>Splitsen met kennis van tokens</span>
Het aantal tekens is een onnauwkeurige benadering van het aantal tokens. Een blok van 1000 tekens kan, afhankelijk van de woordlengte en taal, 200 of 400 tokens bevatten. Voor nauwkeurige controle splits je op basis van het aantal tokens met tiktoken. Dit is belangrijk om blokken binnen het contextvenster van het model te laten passen en de kosten nauwkeurig te schatten. De TokenTextSplitter van LangChain gebruikt tiktoken voor het opdelen met kennis van tokens.
from langchain_text_splitters import TokenTextSplitter
import tiktoken
# Split by actual token count, not characters
splitter = TokenTextSplitter(
encoding_name='cl100k_base', # GPT-4 tokenizer
chunk_size=256, # target tokens per chunk
chunk_overlap=32 # overlap in tokens
)
chunks = splitter.split_text(text)
# Verify token count
enc = tiktoken.get_encoding('cl100k_base')
for chunk in chunks[:3]:
tokens = len(enc.encode(chunk))
print(f'Chunk: {tokens} tokens')<span>De juiste blokgrootte kiezen</span>
De blokgrootte is een belangrijke hyperparameter. Kleine blokken (100-200 tokens) zijn nauwkeurig — ze bevatten sterk gerichte informatie die goed als embedding kan worden vastgelegd. Ze verliezen echter omliggende context, waardoor het LLM mogelijk niet genoeg informatie heeft om te antwoorden. Grote blokken (500-1000 tokens) bieden meer context, maar hun embeddings middelen over een breder onderwerp, waardoor ze moeilijker op specifieke vragen kunnen worden gevonden. De meeste productiesystemen gebruiken 256-512 tokens en testen empirisch met hun eigen gegevens.
<span>Context aan tekstblokken toevoegen</span>
Een krachtige verbetering bestaat uit contextuele kopteksten voor tekstblokken: voeg vóór de tekst van elk blok de documenttitel en sectiekop toe voordat je de embedding maakt. Zo legt de embedding niet alleen de inhoud van het blok vast, maar ook waar die in het document vandaan komt. Een blok met de tekst Voordelen en vakantiebeleid: werknemers bouwen jaarlijks 15 dagen op... wordt veel nauwkeuriger gevonden voor vragen over vakantiebeleid dan dezelfde tekst zonder koptekst.
def create_contextual_chunks(doc, splitter):
title = doc['metadata'].get('title', '')
section = doc['metadata'].get('section', '')
text = doc['text']
raw_chunks = splitter.split_text(text)
contextual_chunks = []
for chunk in raw_chunks:
# Prepend document context to each chunk
context_header = f'{title}\n{section}\n\n' if title else ''
contextual_chunks.append({
'text': context_header + chunk,
'metadata': doc['metadata']
})
return contextual_chunks<span>Strategieën vergelijken met je eigen gegevens</span>
Geen enkele strategie voor het opdelen in tekstblokken is universeel de beste. Bouw een snelle evaluatie: neem 20 vragen waarvan je de antwoorden kent, voer voor elke strategie een zoekopdracht uit en meet hoe vaak het juiste blok in de top 5 resultaten staat (trefferpercentage@5). Dit kost een uur om op te zetten en bespaart wekenlang giswerk. Vaak ontdek je dat jouw specifieke documentindeling (dichte juridische tekst tegenover gestructureerde technische documentatie) sterk de voorkeur geeft aan één strategie boven andere.
def evaluate_chunking_strategy(questions_and_answers, retriever):
hits = 0
for qa in questions_and_answers:
results = retriever.retrieve(qa['question'], top_k=5)
result_texts = [r['text'] for r in results]
# Check if answer text appears in any retrieved chunk
if any(qa['answer'] in text for text in result_texts):
hits += 1
hit_rate = hits / len(questions_and_answers)
print(f'Hit rate@5: {hit_rate:.1%} ({hits}/{len(questions_and_answers)})')
return hit_rate<span>Speciale documenttypen verwerken</span>
Sommige documenttypen hebben een gespecialiseerde strategie voor het opdelen nodig. Codebestanden moeten worden opgesplitst bij grenzen van functies of klassen, niet op basis van het aantal tekens. Markdown-bestanden moeten bij kopgrenzen worden opgesplitst, zodat elk blok overeenkomt met één sectie. Tabellen moeten intact blijven als één blok (splitsen midden in een tabel maakt de inhoud onbegrijpelijk). Plan je strategie op basis van de documenttypen in je corpus in plaats van overal dezelfde splitter toe te passen.
from langchain_text_splitters import MarkdownHeaderTextSplitter
# Split Markdown by header hierarchy
md_splitter = MarkdownHeaderTextSplitter(
headers_to_split_on=[
('#', 'h1'),
('##', 'h2'),
('###', 'h3')
]
)
with open('documentation.md') as f:
md_text = f.read()
# Each chunk gets metadata from its heading hierarchy
chunks = md_splitter.split_text(md_text)
for chunk in chunks[:3]:
print('Section:', chunk.metadata)
print('Text:', chunk.page_content[:80])
print()<span>De herkomst van tekstblokken bijhouden</span>
Elk tekstblok heeft een stabiele, unieke ID nodig die is afgeleid van het brondocument en de positie. Gebruik deze ID om specifieke blokken bij te werken wanneer documenten veranderen, zonder het volledige corpus opnieuw te indexeren. Een deterministische hash van het bronpad plus de index van het blok werkt goed. Leg ook de positie van het blok vast in de metagegevens (blok 3 van 12 uit document X) — dit helpt om volledige secties opnieuw samen te stellen wanneer meerdere aangrenzende blokken tegelijk worden opgehaald.
import hashlib
def assign_chunk_ids(chunks, source_path):
for i, chunk in enumerate(chunks):
key = f'{source_path}::chunk_{i}'
chunk_id = hashlib.sha256(key.encode()).hexdigest()[:16]
chunk['id'] = chunk_id
chunk['metadata']['chunk_index'] = i
chunk['metadata']['total_chunks'] = len(chunks)
return chunks
# IDs are stable across re-runs if source and position match
chunks = sentence_chunks(text)
chunks = [{'text': c, 'metadata': {}} for c in chunks]
assign_chunk_ids(chunks, 'docs/policy_v3.pdf')<span>Korte controle</span>
Test je begrip van de concepten uit AI Engineering in deze les.
<span>Samenvatting van de les</span>
In deze les heb je het volgende geleerd: blokken met vaste grootte zijn eenvoudig, maar breken zinnen middenin af, opdelen op zinsgrenzen behoudt complete gedachten, recursief splitsen op tekens respecteert de documentstructuur en is de meest gebruikelijke keuze voor productie, en geavanceerde technieken, waaronder splitsen met kennis van tokens, contextuele kopteksten, gespecialiseerde splitters voor Markdown en code en stabiele ID's voor incrementele updates. Hierna maken we embeddings van deze blokken en slaan we ze op in een vectordatabase.
Leer Python met een AI-tutor — gratis
Schrijf echte code en voer die uit in je browser, krijg direct hulp van een AI-tutor die 24/7 beschikbaar is en ga verder waar je gebleven bent op het web of in de app.
- Cursussen
- 30
- Lessen
- 120
Veelgestelde vragen
Is de les “Chunkingstrategieën: fixed, sentence en recursive” gratis?
Ja — de volledige tekst van “Chunkingstrategieën: fixed, sentence en recursive” kun je hier gratis op het web lezen. Als je interactief wilt oefenen met een ingebouwde code-editor en een AI-begeleider die 24/7 beschikbaar is, en de rest van de cursus AI Engineering Academy wilt ontgrendelen, kun je upgraden naar CoddyKit PRO. De cursus AI Engineering Academy bevat in totaal 4 lessen.
Wat leer ik in “Chunkingstrategieën: fixed, sentence en recursive”?
U implementeert en vergelijkt text splitters met vaste grootte, op zinsgrenzen en recursief, en begrijpt hoe de chunkgrootte en overlap de retrievalkwaliteit beïnvloeden. Je oefent met AI Engineering Academy door code rechtstreeks in de browser uit te voeren. Een AI-begeleider die 24/7 beschikbaar is beantwoordt je vragen terwijl je de les doorwerkt.
Heb ik ervaring nodig om met AI Engineering Academy te beginnen?
Ervaring vooraf is niet nodig. AI Engineering Academy op CoddyKit is opgebouwd voor beginners tot gevorderden, zodat je hier of bij het begin kunt starten en in je eigen tempo kunt leren. Dit is les 2 van 4.
Hoe lang duurt de les “Chunkingstrategieën: fixed, sentence en recursive”?
De meeste lessen van CoddyKit duren ongeveer 5–10 minuten. Elke les is kort en interactief, zodat je gestaag vooruitgaat en op het web en in de app precies verdergaat waar je was gebleven.
Kan ik code schrijven en uitvoeren in deze les over AI Engineering Academy?
Ja. Elke les over AI Engineering Academy bevat een ingebouwde code-editor, zodat je rechtstreeks in je browser echte code kunt schrijven en uitvoeren en direct feedback van AI krijgt — lokale installatie is niet nodig.
Alle lessen in deze cursus
- Documenten laden en tekst extraheren
- Chunkingstrategieën: fixed, sentence en recursive
- Indexeren: chunks embedden en opslaan
- Queryen, ophalen en genereren