Nieuws & Analyses

RAG bouwen met pgvector en Gemini: Postgres als vectordatabase

Met pgvector wordt je eigen PostgreSQL de vectoropslag voor RAG. Zo koppel je Gemini-embeddings en een Gemini-model tot een werkend zoek- en antwoordsysteem.

RAG bouwen met pgvector en Gemini: Postgres als vectordatabase

Wie een RAG-systeem bouwt, krijgt al snel het advies om er een vectordatabase bij te nemen. Draait je applicatie al op PostgreSQL, dan is dat vaak een extra dienst te veel. Met de extensie pgvector slaat Postgres zelf de embeddings op en zoekt het op betekenis. Gemini levert in deze gids zowel de embeddings als het uiteindelijke antwoord.

Het idee van RAG (retrieval-augmented generation) is eenvoudig: het taalmodel antwoordt niet uit zijn geheugen, maar krijgt eerst de relevante stukken uit jouw eigen documenten mee. Wil je het concept eerst rustig uitgelegd zien, lees dan wat RAG is in gewone taal. En wil je helemaal niets bouwen, dan laat onze gids over RAG zonder te bouwen met ChatGPT zien hoe ver je met kant-en-klare functies komt.

Wat pgvector toevoegt aan Postgres

pgvector geeft je een kolomtype vector, afstandsoperators en twee indextypes. Voor RAG gebruik je meestal cosine distance, de operator <=>; daarnaast zijn er <-> (Euclidische afstand), <#> (negatief inner product) en <+> (L1) (Bron: pgvector).

De belangrijkste keuze is de index. HNSW bouwt een gelaagde graaf, zoekt sneller bij dezelfde nauwkeurigheid dan IVFFlat en kan worden aangemaakt voordat er data in de tabel staat, omdat er geen trainingsstap is. Daar staat een langzamere indexbouw en meer geheugengebruik tegenover (Bron: pgvector).

Eén harde grens bepaalt je ontwerp: een gewone vector-kolom kun je indexeren tot 2.000 dimensies, halfvec tot 4.000 (Bron: pgvector). Gemini-embeddings komen standaard met 3.072 dimensies, maar je kunt 128 tot 3.072 opvragen; Google raadt 768, 1536 of 3072 aan (Bron: Gemini API-docs). Kies dus 768 of 1536 en je zit nergens mee.

💡 Beginner-tip: een embedding is een lange rij getallen die de betekenis van een tekst vastlegt. Teksten over hetzelfde onderwerp krijgen rijen die dicht bij elkaar liggen. “Wat kost het abonnement?” vindt zo ook een alinea over “tarieven”, terwijl er geen woord overeenkomt.

De RAG-loop in vier stappen

Installeer eerst de Python-pakketten: pip install google-genai "psycopg[binary]" pgvector numpy. Zet je API-sleutel in de omgevingsvariabele GEMINI_API_KEY; de client pakt die automatisch op.

Stap 1: extensie, tabel en index

CREATE EXTENSION IF NOT EXISTS vector;

CREATE TABLE documents (
    id        bigserial PRIMARY KEY,
    title     text,
    content   text NOT NULL,
    embedding vector(768)
);

CREATE INDEX ON documents USING hnsw (embedding vector_cosine_ops);

Op een eigen server installeer je pgvector eerst als pakket, bijvoorbeeld postgresql-18-pgvector uit de PostgreSQL-APT-repository; bij veel hostingpartijen zit de extensie er al in (Bron: pgvector).

Stap 2: een embedfunctie

import numpy as np
import psycopg
from pgvector.psycopg import register_vector
from google import genai
from google.genai import types

client = genai.Client()  # leest GEMINI_API_KEY

def embed(text: str) -> np.ndarray:
    result = client.models.embed_content(
        model="gemini-embedding-2",
        contents=text,
        config=types.EmbedContentConfig(output_dimensionality=768),
    )
    return np.array(result.embeddings[0].values)

conn = psycopg.connect("postgresql://localhost/rag", autocommit=True)
register_vector(conn)

register_vector zorgt dat psycopg numpy-arrays als pgvector-waarden doorgeeft (Bron: pgvector-python). Bij gemini-embedding-2 normaliseert Google ingekorte vectoren van 768 of 1536 dimensies automatisch; bij het oudere gemini-embedding-001 moet je dat zelf doen (Bron: Gemini API-docs).

Stap 3: documenten opslaan

def store(content: str, title: str | None = None):
    doc = f"title: {title or 'none'} | text: {content}"
    conn.execute(
        "INSERT INTO documents (title, content, embedding) VALUES (%s, %s, %s)",
        (title, content, embed(doc)),
    )

Let op het voorvoegsel. gemini-embedding-2 kent geen task_type-parameter meer; in plaats daarvan zet je de taak in de tekst zelf. Documenten krijgen het formaat title: {titel} | text: {inhoud} (zonder titel: title: none), vragen het formaat task: question answering | query: {vraag}. Google benadrukt dat je die formaten consequent moet gebruiken (Bron: Gemini API-docs). Splits lange documenten vooraf in stukken van een paar alinea’s; één embedding per heel handboek vindt weinig terug.

Stap 4: zoeken en antwoorden

def ask(question: str) -> str:
    q_vec = embed(f"task: question answering | query: {question}")
    rows = conn.execute(
        "SELECT content FROM documents ORDER BY embedding <=> %s LIMIT 5",
        (q_vec,),
    ).fetchall()
    context = "\n\n".join(r[0] for r in rows)

    response = client.models.generate_content(
        model="gemini-3.8-flash",
        contents=(
            "Beantwoord de vraag alleen met de context hieronder. "
            "Staat het antwoord er niet in, zeg dat dan.\n\n"
            f"Context:\n{context}\n\nVraag: {question}"
        ),
    )
    return response.text

De modelnaam voor het antwoord is de enige regel die je regelmatig zult aanpassen. Hoe je naar een nieuwer Flash-model overstapt en welke instellingen dan veranderen, staat in onze gids over overstappen naar Gemini 3.8 Flash. Wil je dat het model zelf beslist wanneer het zoekt, maak van ask dan een tool in de tool-calling-loop met Gemini.

⚡ Gevorderden: filter je op metadata (WHERE afdeling = 'hr'), dan past pgvector het filter pas toe ná de indexscan. Met de standaard hnsw.ef_search van 40 en een filter dat 10 procent van de rijen overlaat, houd je gemiddeld maar vier resultaten over. Zet SET hnsw.iterative_scan = strict_order; om de index verder te laten doorzoeken; dat werkt vanaf pgvector 0.8.0. Bouw je een grote index, verhoog dan tijdelijk maintenance_work_mem, zonder je server leeg te trekken (Bron: pgvector).

Waar het misgaat

De meeste problemen zitten niet in pgvector maar in de embeddings. Wissel je van embedding-model, dan zijn oude en nieuwe vectoren niet vergelijkbaar: Google schrijft letterlijk dat je bij een upgrade naar gemini-embedding-2 al je bestaande data opnieuw moet embedden (Bron: Gemini API-docs). Leg daarom bij elke rij vast met welk model hij is gemaakt. Dat bespaart je later een avond puzzelen over waarom de zoekresultaten ineens nergens op slaan.

Komt het juiste fragment wel terug maar op plek zeven, dan helpt een extra rerank-stap; zie onze gids over Cohere Rerank in je RAG. Liever een framework dat het knippen, embedden en zoeken voor je regelt? Dan is LlamaIndex een logische volgende stap.

Een aparte vectordatabase wordt pas interessant als je collectie zo groot wordt dat Postgres er merkbaar onder lijdt, als je geen Postgres zelf wilt beheren, of als je zoekfuncties nodig hebt die pgvector niet heeft. Tot die tijd geldt: je hebt al een database, je hebt al backups. Zet CREATE EXTENSION vector; in je volgende migratie.

Stand van zaken — bijgewerkt 2026-10-07

Alles hierboven blijft staan, ongeacht welk model er draait. De cijfers en versies hieronder zijn de bederfelijke laag.

OnderwerpStand
Nieuwste Gemini-embeddingmodelgemini-embedding-2 (sinds 22 april 2026), tekst plus beeld, audio, video en pdf, max. 8.192 invoertokens (Bron: Gemini API-docs)
Ouder embeddingmodelgemini-embedding-001, alleen tekst, met task_type; uitfasering gepland op 14 mei 2028 (Bron: Gemini deprecations)
Uitgefaseerdtext-embedding-004 (sinds 14 januari 2026), embedding-001
Dimensiesstandaard 3.072, instelbaar 128 tot 3.072; aanbevolen 768, 1536 of 3072
Prijs embeddingsgemini-embedding-2: 0,20 dollar per miljoen teksttokens, 0,10 dollar via Batch; gratis tier beschikbaar (Bron: Gemini API-prijzen)
Model voor het antwoordgemini-3.8-flash: 0,75/3,75 dollar per miljoen in/uit tot eind 2026, 1,50/7,50 vanaf 2027; goedkoper alternatief gemini-3.5-flash-lite (0,30/2,50) (Bron: Gemini API-prijzen)
pgvectorversie 0.8.7 (1 oktober 2026); iteratieve index-scans sinds 0.8.0 (Bron: pgvector changelog)
Indexlimietvector tot 2.000 dimensies, halfvec tot 4.000

Op de gratis tier van de Gemini API mag Google je invoer gebruiken om zijn producten te verbeteren; voor klant- of bedrijfsdata hoort je toepassing op de betaalde tier (Bron: Gemini API-prijzen).

#gemini#pgvector#rag#postgresql#embeddings#python

Veelgestelde vragen

Wat is pgvector?
pgvector is een open-source extensie voor PostgreSQL. Je krijgt er een kolomtype vector bij, operators om afstanden tussen vectoren te berekenen en twee indextypes (HNSW en IVFFlat) om snel de dichtstbijzijnde vectoren te vinden. Daarmee doet je gewone Postgres-database dienst als vectoropslag voor RAG, naast je bestaande tabellen, backups en rechten.
Welk Gemini-model gebruik ik voor embeddings?
Voor nieuwe projecten het nieuwste embedding-model uit de Gemini API; welk dat nu is, staat in de tabel Stand van zaken onderaan. Vraag via output_dimensionality 768 of 1536 dimensies op, zodat je kolom indexeerbaar blijft in pgvector. Oudere modellen zoals text-embedding-004 zijn uitgefaseerd; gebruik je die nog, dan moet je je hele collectie opnieuw embedden.
Wat is het verschil tussen HNSW en IVFFlat in pgvector?
HNSW bouwt een gelaagde graaf en geeft volgens de pgvector-documentatie een betere verhouding tussen snelheid en nauwkeurigheid, ten koste van een langzamere indexbouw en meer geheugen. IVFFlat bouwt sneller, maar heeft een trainingsstap en dus al data nodig voordat je de index aanmaakt. Voor de meeste RAG-projecten is HNSW de logische eerste keus.
Kan ik pgvector gebruiken bij een managed PostgreSQL-dienst?
Meestal wel. pgvector houdt zelf een lijst bij van hostingpartijen die de extensie aanbieden, en bij veel diensten zet je hem aan met CREATE EXTENSION vector. Controleer wel welke pgvector-versie je provider draait, want iteratieve index-scans voor gefilterde zoekopdrachten zitten pas in versie 0.8.0 en later.
Heb ik een aparte vectordatabase nodig voor RAG?
Niet als je al Postgres gebruikt en je collectie overzichtelijk is. pgvector houdt je data, metadata en vectoren in één database, met één backupstrategie. Een gespecialiseerde vectordatabase wordt interessant bij heel grote collecties, als je geen Postgres zelf wilt beheren, of als je zoekfuncties nodig hebt die pgvector niet biedt.