Vector Databases: De Ruggengraat van AI-Applicaties

Gepubliceerd: 28 mei 2026
Leestijd: 12 minuten
Data Engineering

Ontdek hoe vector databases zoals Pinecone, Weaviate en pgvector AI-zoekfunctionaliteit mogelijk maken en hoe je ze integreert in je data stack.

Wat zijn Vector Databases en Waarom zijn ze in 2026 Onmisbaar?

De opkomst van Large Language Models (LLMs), generatieve AI en semantische zoektoepassingen heeft een fundamenteel nieuw type dataopslag noodzakelijk gemaakt: de vector database. Waar traditionele relationele databases uitblinken in het opslaan van gestructureerde data en het uitvoeren van exacte zoekopdrachten, schieten ze tekort wanneer je wilt zoeken op betekenis in plaats van exacte trefwoorden.

In 2026 is de vector database niet langer een nicheoplossing voor academici of AI-onderzoekslaboratoria. Het is de ruggengraat van vrijwel elke serieuze AI-applicatie: van chatbots met bedrijfskennis (RAG-systemen) tot aanbevelingsengines, fraudedetectie en multimodale zoekmachines die tekst, afbeeldingen en audio combineren.

Definitie: Vector Database

Een vector database is een gespecialiseerd databasesysteem dat geoptimaliseerd is voor het opslaan, indexeren en doorzoeken van vectorembeddings — wiskundige representaties van data (tekst, afbeeldingen, audio) als lijsten van getallen in een hoog-dimensionele ruimte. Zoeken gebeurt op basis van cosine similarity of euclidische afstand, waardoor semantisch vergelijkbare items gevonden worden zelfs zonder exacte woordovereenkomst.

Stel je voor: een gebruiker zoekt naar "goedkope vakantiebestemmingen in Zuid-Europa" in jouw reisdatabase. Een traditionele zoekengine vindt alleen documenten met exact die woorden. Een vector database vindt ook artikelen over "budgetvriendelijke reizen naar Portugal", "zonvakantie Griekenland betaalbaar" en "goedkoop zomers Italië" — simpelweg omdat ze semantisch dicht bij elkaar liggen in de vectorruimte.

Hoe Werken Vector Databases? De Techniek Uitgelegd

Om vector databases echt te begrijpen, moet je drie kernconcepten beheersen: embeddings genereren, vectoren indexeren, en Approximate Nearest Neighbor (ANN) zoekopdrachten uitvoeren.

1

Data omzetten naar Embeddings

Ruwe data (tekst, afbeeldingen, audio) wordt via een embedding model omgezet naar een vector van floats. Een tekstzin wordt bijvoorbeeld een array van 1536 getallen (bij OpenAI's text-embedding-3-small). Semantisch gelijkwaardige zinnen produceren vectoren die dicht bij elkaar liggen in de vectorruimte.

2

Vectoren opslaan met Metadata

De gegenereerde vectoren worden opgeslagen in de vector database, samen met metadata (originele tekst, bron-URL, timestamp, categorie). Deze metadata is cruciaal voor filtering en het teruggeven van bruikbare resultaten.

3

Indexering met ANN-algoritmen

Om snel te kunnen zoeken in miljoenen vectoren, worden speciale indexstructuren gebruikt zoals HNSW (Hierarchical Navigable Small World), IVF (Inverted File Index) of ANNOY. Deze algoritmen offeren een kleine hoeveelheid nauwkeurigheid in voor enorme snelheidswinst.

4

Similarity Search uitvoeren

Een zoekvraag wordt ook omgezet naar een embedding. De database zoekt dan de k nearest neighbors (kNN) in de vectorruimte en retourneert de meest semantisch verwante items, eventueel gecombineerd met metadata-filters (hybrid search).

Hoe Similarity Berekend Wordt

Methode Formule Beste voor Waardebereik
Cosine Similarity cos(θ) = (A·B) / (|A||B|) Tekstembeddings, documentvergelijking -1 tot 1 (1 = identiek)
Euclidische Afstand √Σ(Aᵢ - Bᵢ)² Afbeeldingen, ruimtelijke data 0 tot ∞ (0 = identiek)
Dot Product A·B = ΣAᵢBᵢ Aanbevelingssystemen Onbegrensd
Manhattan Distance Σ|Aᵢ - Bᵢ| Anomaliedetectie 0 tot ∞ (0 = identiek)

Praktische Codevoorbeelden

Genoeg theorie — laten we kijken hoe je een vector database daadwerkelijk gebruikt in een productieomgeving. We behandelen drie populaire opties: Pinecone (managed cloud), Weaviate (open-source) en pgvector (PostgreSQL extensie).

1. RAG-pipeline met Pinecone en OpenAI

Dit is een realistisch voorbeeld van een Retrieval-Augmented Generation (RAG) systeem dat bedrijfsdocumenten doorzoekbaar maakt voor een chatbot:

import os
from openai import OpenAI
from pinecone import Pinecone, ServerlessSpec
import hashlib

# Initialiseer clients
client = OpenAI(api_key=os.getenv("OPENAI_API_KEY"))
pc = Pinecone(api_key=os.getenv("PINECONE_API_KEY"))

INDEX_NAME = "bedrijfsdocumenten"
EMBED_MODEL = "text-embedding-3-small"
EMBED_DIMS = 1536

# Maak index aan als die nog niet bestaat
if INDEX_NAME not in pc.list_indexes().names():
    pc.create_index(
        name=INDEX_NAME,
        dimension=EMBED_DIMS,
        metric="cosine",
        spec=ServerlessSpec(cloud="aws", region="eu-west-1")
    )

index = pc.Index(INDEX_NAME)

def genereer_embedding(tekst: str) -> list[float]:
    """Genereer een embedding via OpenAI API."""
    response = client.embeddings.create(
        input=tekst,
        model=EMBED_MODEL
    )
    return response.data[0].embedding

def indexeer_document(doc_id: str, tekst: str, metadata: dict):
    """Sla een document op als vector in Pinecone."""
    embedding = genereer_embedding(tekst)
    index.upsert(vectors=[{
        "id": doc_id,
        "values": embedding,
        "metadata": {
            **metadata,
            "tekst": tekst[:1000]  # Sla eerste 1000 tekens op als metadata
        }
    }])
    print(f"Document '{doc_id}' geïndexeerd.")

def zoek_relevante_documenten(vraag: str, top_k: int = 5, filter: dict = None):
    """Zoek de meest relevante documenten voor een vraag."""
    vraag_embedding = genereer_embedding(vraag)
    resultaten = index.query(
        vector=vraag_embedding,
        top_k=top_k,
        include_metadata=True,
        filter=filter  # Bijv. {"afdeling": {"$eq": "finance"}}
    )
    return resultaten.matches

def rag_antwoord(vraag: str, afdeling: str = None) -> str:
    """Genereer een antwoord op basis van relevante documenten (RAG)."""
    filter = {"afdeling": {"$eq": afdeling}} if afdeling else None
    docs = zoek_relevante_documenten(vraag, top_k=3, filter=filter)

    if not docs:
        return "Geen relevante documenten gevonden."

    # Bouw context op uit gevonden documenten
    context = "\n\n".join([
        f"[{doc.metadata.get('titel', 'Onbekend')}]\n{doc.metadata.get('tekst', '')}"
        for doc in docs
    ])

    # Genereer antwoord met GPT-4
    response = client.chat.completions.create(
        model="gpt-4o",
        messages=[
            {"role": "system", "content": (
                "Je bent een behulpzame assistent. Beantwoord vragen uitsluitend "
                "op basis van de gegeven context. Als het antwoord niet in de "
                "context staat, zeg dat dan eerlijk."
            )},
            {"role": "user", "content": f"Context:\n{context}\n\nVraag: {vraag}"}
        ],
        temperature=0.1
    )
    return response.choices[0].message.content

# Voorbeeld gebruik
if __name__ == "__main__":
    # Indexeer voorbeelddocumenten
    documenten = [
        {
            "id": "hr-001",
            "tekst": "Medewerkers hebben recht op 25 vakantiedagen per jaar. "
                     "Vakantiedagen kunnen worden opgebouwd en overgedragen naar het volgende jaar.",
            "metadata": {"titel": "Vakantiebeleid", "afdeling": "hr"}
        },
        {
            "id": "finance-001",
            "tekst": "Declaraties tot €150 kunnen direct worden ingediend zonder goedkeuring. "
                     "Bedragen boven €150 vereisen manager-goedkeuring binnen 5 werkdagen.",
            "metadata": {"titel": "Declaratiebeleid", "afdeling": "finance"}
        }
    ]

    for doc in documenten:
        indexeer_document(doc["id"], doc["tekst"], doc["metadata"])

    # Stel een vraag
    antwoord = rag_antwoord("Hoeveel vakantiedagen heb ik?", afdeling="hr")
    print(f"Antwoord: {antwoord}")

2. pgvector: Vector Search in PostgreSQL

Als je al PostgreSQL gebruikt, is pgvector de meest pragmatische keuze — geen nieuwe infrastructuur nodig:

-- Installeer de pgvector extensie
CREATE EXTENSION IF NOT EXISTS vector;

-- Maak een tabel aan voor productembeddings
CREATE TABLE producten (
    id          SERIAL PRIMARY KEY,
    naam        TEXT NOT NULL,
    beschrijving TEXT,
    categorie   TEXT,
    prijs       DECIMAL(10,2),
    embedding   vector(1536)  -- OpenAI text-embedding-3-small dimensie
);

-- Maak een HNSW index aan voor snelle similarity search
CREATE INDEX ON producten USING hnsw (embedding vector_cosine_ops)
WITH (m = 16, ef_construction = 64);

-- Voeg een product in (embedding als array van floats)
INSERT INTO producten (naam, beschrijving, categorie, prijs, embedding)
VALUES (
    'Ergonomische bureaustoel',
    'Verstelbare lendensteuн, ademend mesh materiaal, 8 uur comfort',
    'kantoormeubelen',
    249.99,
    '[0.023, -0.045, 0.112, ...]'::vector  -- Vervang door echte embedding
);

-- Semantic search: vind de 5 meest vergelijkbare producten
-- (query_embedding zou gegenereerd worden door je applicatie)
SELECT
    naam,
    beschrijving,
    categorie,
    prijs,
    1 - (embedding <=> '[0.021, -0.041, 0.108, ...]'::vector) AS similarity_score
FROM producten
WHERE categorie = 'kantoormeubelen'  -- Metadata filter
ORDER BY embedding <=> '[0.021, -0.041, 0.108, ...]'::vector
LIMIT 5;
# Python-implementatie met pgvector en asyncpg
import asyncpg
import numpy as np
from openai import OpenAI

client = OpenAI()

async def zoek_producten(zoekvraag: str, categorie: str = None, top_k: int = 5):
    """Zoek producten op basis van semantische gelijkenis."""
    # Genereer embedding voor de zoekvraag
    embedding = client.embeddings.create(
        input=zoekvraag,
        model="text-embedding-3-small"
    ).data[0].embedding

    embedding_str = "[" + ",".join(map(str, embedding)) + "]"

    conn = await asyncpg.connect(dsn=os.getenv("DATABASE_URL"))

    query = """
        SELECT naam, beschrijving, prijs,
               1 - (embedding <=> $1::vector) AS score
        FROM producten
        WHERE ($2::text IS NULL OR categorie = $2)
        ORDER BY embedding <=> $1::vector
        LIMIT $3
    """

    resultaten = await conn.fetch(query, embedding_str, categorie, top_k)
    await conn.close()
    return resultaten

3. Weaviate: Hybrid Search met BM25 + Vectoren

import weaviate
import weaviate.classes as wvc

# Verbind met lokale Weaviate instantie
client = weaviate.connect_to_local()

# Definieer een collectie met automatische vectorisatie
artikelen = client.collections.create(
    name="Kennisbank",
    vectorizer_config=wvc.config.Configure.Vectorizer.text2vec_openai(
        model="text-embedding-3-small"
    ),
    generative_config=wvc.config.Configure.Generative.openai(
        model="gpt-4o"
    ),
    properties=[
        wvc.config.Property(name="titel", data_type=wvc.config.DataType.TEXT),
        wvc.config.Property(name="inhoud", data_type=wvc.config.DataType.TEXT),
        wvc.config.Property(name="afdeling", data_type=wvc.config.DataType.TEXT),
    ]
)

# Hybrid search: combineer keyword (BM25) en vector search
resultaten = client.collections.get("Kennisbank").query.hybrid(
    query="thuiswerken beleid",
    alpha=0.5,        # 0 = puur BM25, 1 = puur vector, 0.5 = mix
    limit=5,
    filters=wvc.query.Filter.by_property("afdeling").equal("hr")
)

for item in resultaten.objects:
    print(f"Titel: {item.properties['titel']}")
    print(f"Score: {item.metadata.score}")

client.close()

Vergelijking: Welke Vector Database Kies Je?

De keuze voor de juiste vector database hangt af van je use case, bestaande infrastructuur en team-expertise. Hier is een eerlijke vergelijking van de meest populaire opties in 2026:

Database Type Sterke punten Zwakke punten Beste voor Prijs
Pinecone Managed Cloud Zero ops, schaalbaar, snelle setup Vendor lock-in, duur bij schaal, geen SQL Snel starten, productie-RAG €0 (starter) / gebruik-based
Weaviate Open-source / Cloud Hybrid search, multimodaal, GraphQL API Complexe configuratie, geheugenintensief Enterprise semantisch zoeken Gratis (self-hosted)
pgvector PostgreSQL extensie Geen nieuwe infra, SQL joins, ACID Minder schaalbaar bij >10M vectoren Bestaande Postgres-omgevingen Gratis (open-source)
Qdrant Open-source / Cloud Rust-based, snel, payload filtering Kleiner ecosysteem dan Pinecone High-performance on-premise Gratis (self-hosted)
Chroma Open-source Embedded, Python-native, eenvoudig Niet production-ready voor grote schaal Prototyping, lokale LLM-apps Gratis
Milvus Open-source / Cloud Hoogste schaal, GPU-acceleratie Complexe deployment (Kubernetes) Billions-scale productie Gratis (self-hosted)

Kies Pinecone als...

Je snel een werkende RAG-applicatie wilt bouwen zonder infrastructuurbeheer. Ideaal voor startups en teams zonder dedicated DevOps.

Kies pgvector als...

Je PostgreSQL al in productie hebt en minder dan 5-10 miljoen vectoren verwacht. Minimale operationele overhead, maximale SQL-flexibiliteit.

Kies Weaviate als...

Je hybrid search (keyword + semantisch) nodig hebt, met multimodale zoekmogelijkheden en een enterprise-grade oplossing on-premise.

Best Practices voor Productie

Vector databases in productie brengen is een ander verhaal dan een proof-of-concept draaien. Hier zijn de kritische best practices die je behoedt voor dure fouten:

Top 7 Productie Best Practices

  1. Chunking strategie is alles: Splits documenten in semantisch coherente chunks van 256-512 tokens. Te kleine chunks missen context; te grote chunks verdunnen de embedding. Gebruik overlapping chunks (10-20%) om context aan chunkgrenzen te behouden.
  2. Embedding model consistentie: Gebruik altijd hetzelfde model voor indexering én zoekopdrachten. Wissel nooit van embedding model zonder de hele index te herbouwen.
  3. Metadata-filtering is geen vervanging voor structuur: Ontwerp je metadata schema zorgvuldig — filterbare velden (categorie, datum, afdeling) moeten exact kloppen; gebruik ze voor pre-filtering om zoekruimte te verkleinen vóór vector similarity berekening.
  4. Monitor embedding kwaliteit: Track de gemiddelde similarity score van topresultaten over tijd. Een dalende score suggereert dataset drift of modelveroudering.
  5. Namespace/tenant isolatie: Gebruik namespaces (Pinecone) of tenants (Weaviate) voor multi-tenant applicaties. Dit voorkomt data-lekkage tussen klanten.
  6. Batch upserts, geen individuele inserts: Indexeer in batches van 100-500 vectoren tegelijk voor 10x betere throughput.
  7. Re-ranking als tweede stap: Gebruik een cross-encoder reranker (bijv. Cohere Rerank, bge-reranker) na de initiële ANN-zoekopdracht voor significant betere relevantie.

Chunking Strategie in Code

from langchain.text_splitter import RecursiveCharacterTextSplitter
from typing import List, Dict

def chunk_document(tekst: str, doc_metadata: dict) -> List[Dict]:
    """
    Splits een document in overlappende chunks met metadata.
    Volgt best practice: 512 tokens, 10% overlap.
    """
    splitter = RecursiveCharacterTextSplitter(
        chunk_size=512,
        chunk_overlap=51,           # ~10% overlap
        length_function=len,
        separators=["\n\n", "\n", ". ", " ", ""]  # Volgorde van voorkeur
    )

    chunks = splitter.split_text(tekst)

    resultaat = []
    for i, chunk in enumerate(chunks):
        resultaat.append({
            "id": f"{doc_metadata['doc_id']}_chunk_{i}",
            "tekst": chunk,
            "metadata": {
                **doc_metadata,
                "chunk_index": i,
                "totaal_chunks": len(chunks),
                "char_start": tekst.find(chunk)  # Positie in origineel document
            }
        })

    return resultaat

def batch_indexeer(chunks: List[Dict], index, batch_size: int = 200):
    """Indexeer chunks in batches voor maximale throughput."""
    for i in range(0, len(chunks), batch_size):
        batch = chunks[i:i + batch_size]

        # Genereer embeddings voor de hele batch in één API-call
        teksten = [c["tekst"] for c in batch]
        embeddings = client.embeddings.create(
            input=teksten,
            model="text-embedding-3-small"
        ).data

        vectors = [
            {
                "id": chunk["id"],
                "values": emb.embedding,
                "metadata": chunk["metadata"]
            }
            for chunk, emb in zip(batch, embeddings)
        ]

        index.upsert(vectors=vectors)
        print(f"Batch {i//batch_size + 1} geïndexeerd ({len(vectors)} chunks)")

Praktijkcase: Kennisbank Chatbot bij een Verzekeraar

Een Nederlandse verzekeraar met 2.300 medewerkers implementeerde een interne RAG-chatbot op basis van Weaviate + Azure OpenAI. De kennisbank bevatte 85.000 documenten (polisvoorwaarden, procedures, FAQ's).

Uitdagingen: Polisvoorwaarden bevatten veel juridisch jargon en overlappende terminologie. Keyword search gaf te veel irrelevante resultaten; pure semantic search miste exacte polisreferentienummers.

Oplossing: Hybrid search met alpha=0.4 (60% BM25 / 40% vector) gecombineerd met een Cohere reranker. Chunks van 400 tokens met 80 tokens overlap, metadata-filtering op productcategorie.

Resultaat: MRR@5 (Mean Reciprocal Rank) steeg van 0.43 (pure keyword) naar 0.81 (hybrid + reranking). Gemiddelde antwoordtijd: 2.1 seconden. 78% van vragen beantwoord zonder escalatie naar medewerker.

Conclusie: Wanneer Wel en Wanneer Niet?

Vector databases zijn krachtig, maar ze zijn geen silver bullet. Hier is een eerlijk overzicht van wanneer je ze wel of juist niet moet inzetten:

Gebruik vector databases WEL voor:

  • RAG-systemen en AI-chatbots
  • Semantische zoekfunctionaliteit
  • Aanbevelingssystemen
  • Duplicate detectie op grote schaal
  • Multimodale zoektoepassingen
  • Anomaliedetectie in hoog-dimensionele data

Gebruik vector databases NIET voor:

  • Exacte ID-lookups (gebruik gewone DB)
  • Transactionele data (geen ACID garanties)
  • Kleine datasets (<10.000 items) — Elasticsearch volstaat
  • Eenvoudige keyword search zonder semantiek
  • Als je team geen AI/ML expertise heeft

Vuistregel voor 2026:

Begin met pgvector als je Postgres hebt. Migreer naar Pinecone of Weaviate als je meer dan 5 miljoen vectoren nodig hebt of geavanceerde features zoals hybrid search en multimodale indexen.

In 2026 is de vraag niet meer "of