AI Engineering Academy · Les

De RAG-architectuur: indexeren en ophalen

U brengt de twee fasen van RAG in kaart: de offline-indexeringsfase waarin documenten worden opgeknipt, ge-embed en opgeslagen, en de online-retrievalfase waarin voor elke query relevante context wordt gevonden.

Les 2 van 413 stappen

De RAG-architectuur: indexeren en ophalen 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.

RAG heeft twee afzonderlijke fasen

Een RAG-systeem werkt in twee fundamenteel verschillende fasen die op verschillende momenten worden uitgevoerd. De offline-indexeringsfase verwerkt uw documenten één keer (of wanneer ze veranderen) en maakt een doorzoekbare index. De online-ophaalfase wordt in realtime uitgevoerd voor elke query van een gebruiker. Als u deze scheiding begrijpt, kunt u systemen ontwerpen die zowel snel zijn tijdens queries als op lange termijn onderhoudbaar blijven.

De indexeringsfase: stap één — laden

Indexering begint met documenten laden: onbewerkte bestanden uit uw bronsystemen lezen. Documenten kunnen pdf's, Word-bestanden, HTML-pagina's, Markdown-bestanden, databaseregels of andere tekstbronnen zijn. Elk document wordt als platte tekst in het geheugen geladen, waarbij de structuur waar mogelijk behouden blijft. Bibliotheken zoals pypdf, python-docx en unstructured nemen het meeste werk van het parseren van specifieke indelingen voor hun rekening.

from pypdf import PdfReader

def load_pdf(path):
    reader = PdfReader(path)
    pages = []
    for i, page in enumerate(reader.pages):
        text = page.extract_text()
        pages.append({'text': text, 'page': i + 1, 'source': path})
    return pages

docs = load_pdf('company_policy.pdf')
print(f'Loaded {len(docs)} pages')

De indexeringsfase: stap twee — opdelen in fragmenten

LLM's hebben eindige contextvensters en het ophalen van volledige documenten is inefficiënt. De geladen tekst wordt opgesplitst in kleinere fragmenten van ongeveer 200-1000 tokens. Goed opdelen behoudt de semantische samenhang: een fragment moet een volledig idee uitdrukken. Een veelgebruikte strategie maakt gebruik van overlappende vensters, zodat zinnen bij de grenzen van fragmenten in twee fragmenten voorkomen en informatieverlies op de splitsingspunten wordt voorkomen.

from langchain_text_splitters import RecursiveCharacterTextSplitter

splitter = RecursiveCharacterTextSplitter(
    chunk_size=500,      # characters per chunk
    chunk_overlap=50,    # overlap between chunks
    separators=['\n\n', '\n', '. ', ' ']
)

for page in docs:
    chunks = splitter.split_text(page['text'])
    for chunk in chunks:
        # Each chunk carries metadata from its source page
        print(f'Chunk ({len(chunk)} chars): {chunk[:80]}...')

De indexeringsfase: stap drie — embedden

Elk tekstfragment wordt omgezet in een dichte vector-embedding die de semantische betekenis ervan numeriek vastlegt. U roept voor elk fragment een embeddingmodel aan — zoals OpenAI's text-embedding-3-small — en ontvangt een hoogdimensionale array met floats. Fragmenten met een vergelijkbare betekenis leveren vectoren op die in deze hoogdimensionale ruimte dicht bij elkaar liggen. Dat maakt zoeken op semantische overeenkomst mogelijk.

from openai import OpenAI

client = OpenAI()

def embed_chunks(chunks):
    texts = [c['text'] for c in chunks]
    response = client.embeddings.create(
        model='text-embedding-3-small',
        input=texts
    )
    for i, chunk in enumerate(chunks):
        chunk['embedding'] = response.data[i].embedding
    return chunks

# Batch up to 2048 texts per API call
embedded = embed_chunks(all_chunks)

De indexeringsfase: stap vier — opslaan

De geëmbedde fragmenten worden samen met hun metagegevens opgeslagen in een vectordatabase (bronbestand, paginanummer, sectietitel). De vectoropslag bouwt een indexstructuur (meestal HNSW) waarmee snel naar geschatte naaste buren kan worden gezocht. Deze index wordt op schijf bewaard, zodat hij na het opnieuw starten behouden blijft. Indexering vindt meestal één keer plaats tijdens de configuratie en incrementeel wanneer nieuwe documenten worden toegevoegd.

import pinecone

pc = pinecone.Pinecone(api_key='YOUR_KEY')
index = pc.Index('rag-documents')

# Upsert vectors with metadata
vectors_to_upsert = [
    (
        chunk['id'],
        chunk['embedding'],
        {'text': chunk['text'], 'source': chunk['source'], 'page': chunk['page']}
    )
    for chunk in embedded_chunks
]

# Upsert in batches of 100
for i in range(0, len(vectors_to_upsert), 100):
    index.upsert(vectors=vectors_to_upsert[i:i+100])
print('Indexing complete')

De ophaalfase: query embedden

Wanneer een gebruiker een query indient, begint de online-ophaalfase. De eerste stap is de vraag van de gebruiker embedden met hetzelfde embeddingmodel dat tijdens het indexeren is gebruikt. Dit is cruciaal: als u hebt geïndexeerd met text-embedding-3-small, moet u ook query's uitvoeren met text-embedding-3-small. De query-embedding is een vector die de semantische betekenis van de vraag van de gebruiker vastlegt.

def embed_query(question):
    response = client.embeddings.create(
        model='text-embedding-3-small',  # MUST match indexing model
        input=[question]
    )
    return response.data[0].embedding

user_question = 'What is our parental leave policy?'
query_vector = embed_query(user_question)
print(f'Query embedded: {len(query_vector)}-dim vector')

De ophaalfase: ANN-zoekopdracht

De query-embedding wordt naar de vectoropslag gestuurd. Die voert een zoekopdracht naar geschatte naaste buren (ANN) uit om de K fragmenten te vinden waarvan de embeddings het meest lijken op de queryvector. Deze zoekopdracht is extreem snel (meestal minder dan 10 ms), omdat HNSW-indexen een kleine hoeveelheid volledigheid inruilen voor enorme snelheidswinst ten opzichte van zoeken met brute kracht. U haalt de bovenste K-fragmenten op — meestal K=5 tot K=20.

results = index.query(
    vector=query_vector,
    top_k=5,
    include_metadata=True
)

print(f'Retrieved {len(results.matches)} chunks:')
for match in results.matches:
    print(f'  Score: {match.score:.3f} | Source: {match.metadata["source"]}')
    print(f'  Text: {match.metadata["text"][:100]}...')
    print()

De twee fasen verbinden

Het belangrijkste inzicht is dat indexering en ophalen zijn ontworpen om samen te werken. Het embeddingmodel moet in beide fasen identiek zijn, omdat de wiskundige ruimte waarin vectoren zich bevinden modelspecifiek is. Als u van embeddingmodel wisselt, moet u alle documenten opnieuw indexeren. De vectoropslag vormt de brug: tijdens het indexeren accepteert deze vectoren en tijdens het ophalen geeft deze vectoren terug. Zo worden de twee fasen in de tijd van elkaar losgekoppeld, terwijl ze in de vectorruimte op elkaar afgestemd blijven.

Filteren op metagegevens tijdens het ophalen

Met zoeken in vectoren vindt u semantisch vergelijkbare fragmenten, maar soms moet u ook op metagegevens filteren. U kunt bijvoorbeeld alleen fragmenten ophalen uit documenten die in 2025 zijn geüpload, of alleen uit de map van de HR-afdeling. Vectoropslagen ondersteunen voorfiltering of nafiltering op metagegevensvelden. Voorfiltering (ondersteund door Pinecone en Qdrant) past het filter toe vóór de ANN-zoekopdracht. Dit is sneller en nauwkeuriger dan nafiltering van de bovenste K-resultaten.

# Retrieve only from HR department documents
results = index.query(
    vector=query_vector,
    top_k=5,
    filter={'department': {'$eq': 'HR'}},
    include_metadata=True
)

# Or filter by date range
results = index.query(
    vector=query_vector,
    top_k=5,
    filter={
        'upload_year': {'$gte': 2024},
        'doc_type': {'$eq': 'policy'}
    },
    include_metadata=True
)

Incrementeel indexeren voor updates

In productie verandert uw documentverzameling in de loop van de tijd. Een effectieve indexeringsarchitectuur ondersteunt incrementele updates: wanneer een document wordt bewerkt, verwijdert u de bestaande vectoren op ID en voegt u de nieuwe vectoren toe of werkt u ze bij. Wanneer documenten worden verwijderd, verwijdert u hun vectoren. Geef elk fragment een deterministische ID op basis van het pad naar het brondocument en de positie van het fragment, zodat u de juiste vectoren altijd kunt vinden en bijwerken zonder alles opnieuw te indexeren.

import hashlib

def make_chunk_id(source_path, chunk_index):
    # Deterministic, stable ID for each chunk
    key = f'{source_path}::chunk_{chunk_index}'
    return hashlib.md5(key.encode()).hexdigest()

def update_document(source_path, index):
    # Delete old vectors for this document
    index.delete(filter={'source': source_path})
    # Re-index the updated document
    new_chunks = load_and_chunk(source_path)
    new_embedded = embed_chunks(new_chunks)
    index.upsert(vectors=new_embedded)
    print(f'Updated {source_path}: {len(new_chunks)} chunks')

De volledige RAG-pijplijn in één oogopslag

De volledige RAG-architectuur ziet er als volgt uit: Offline: Documenten → Lader → Fragmentverdeler → Embeddingmodel → Vectoropslag. Online: Query van gebruiker → Embeddingmodel → Vectoropslag (ANN-zoekopdracht) → Bovenste K-fragmenten → Prompt samenstellen → LLM → Antwoord. De offline-pijplijn wordt één keer uitgevoerd per documentupdate. De online-pijplijn wordt voor elke query van een gebruiker in milliseconden uitgevoerd, waarbij de LLM alleen de relevante context ziet en niet de volledige documentverzameling.

Korte controle

Test uw begrip van de concepten uit AI Engineering in deze les.

Samenvatting van de les

In deze les hebt u geleerd: de offline-indexeringsfase, bestaande uit de stappen laden, opdelen in fragmenten, embedden en opslaan, die één keer per document worden uitgevoerd; de online-ophaalfase, waarin de query wordt geëmbed, een ANN-zoekopdracht wordt uitgevoerd en de bovenste K-fragmenten in milliseconden worden teruggegeven; en strategieën voor incrementeel indexeren om de vectoropslag actueel te houden wanneer documenten veranderen. Hierna bekijken we hoe u effectieve aangevulde prompts opstelt die opgehaalde context goed gebruiken.

Gratis beginnen

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 “De RAG-architectuur: indexeren en ophalen” gratis?

Ja — de volledige tekst van “De RAG-architectuur: indexeren en ophalen” 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 “De RAG-architectuur: indexeren en ophalen”?

U brengt de twee fasen van RAG in kaart: de offline-indexeringsfase waarin documenten worden opgeknipt, ge-embed en opgeslagen, en de online-retrievalfase waarin voor elke query relevante context wor… 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 “De RAG-architectuur: indexeren en ophalen”?

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

  1. Het probleem dat RAG oplost
  2. De RAG-architectuur: indexeren en ophalen
  3. De augmented prompt opstellen
  4. RAG versus fine-tuning: wanneer gebruikt u welke aanpak?
← Terug naar AI Engineering Academy