Wat zijn embeddings
Embeddings zijn numerieke vectorrepresentaties van tekst waarbij semantisch vergelijkbare teksten dicht bij elkaar liggen in de vectorruimte. "Hond" en "hond loopt in het park" liggen dichter bij elkaar dan "hond" en "broodrooster", ook al hebben ze geen woorden gemeen.
Toepassingen: semantisch zoeken (vind documenten op betekenis, niet op trefwoorden), clustering (groepeer gerelateerde documenten), aanbevelingen (suggereer vergelijkbare items), anomaliedetectie en classificatie.
Modelnaam en SDK zijn gewijzigd
Het oude model text-embedding-004 is op 14 januari 2026 uitgeschakeld en werkt niet meer. Gebruik nu gemini-embedding-001. Ook de oude google-generativeai-bibliotheek is vervangen door de nieuwe google-genai SDK met een Client-object. De voorbeelden hieronder zijn op die nieuwe SDK gebaseerd.
Installeren en authenticeren
Installeer de nieuwe SDK en zet je API-sleutel in een omgevingsvariabele.
pip install google-genai
export GEMINI_API_KEY="jouw_sleutel"
De client leest de sleutel automatisch uit GEMINI_API_KEY, je hoeft hem niet expliciet door te geven.
Een embedding genereren
from google import genai
from google.genai import types
client = genai.Client()
result = client.models.embed_content(
model="gemini-embedding-001",
contents="Machine learning helpt computers te leren zonder expliciet geprogrammeerd te worden.",
config=types.EmbedContentConfig(task_type="RETRIEVAL_DOCUMENT"),
)
embedding = result.embeddings[0].values
print(f"Vectordimensies: {len(embedding)}")
print(f"Eerste 5 waarden: {embedding[:5]}")
Standaard geeft gemini-embedding-001 een vector van 3072 dimensies terug. De maximale invoer is 2048 tokens per tekst en het model ondersteunt meer dan 100 talen.
Task types voor betere kwaliteit
Het model is geoptimaliseerd voor verschillende taken via task_type in de EmbedContentConfig:
| Task type | Gebruik |
|---|---|
RETRIEVAL_DOCUMENT |
Documenten die doorzocht worden |
RETRIEVAL_QUERY |
Zoekquery van de gebruiker |
SEMANTIC_SIMILARITY |
Vergelijken van tekstparen |
CLASSIFICATION |
Classificatietaken |
CLUSTERING |
Groeperingstaken |
QUESTION_ANSWERING |
Vraag-antwoord paren |
FACT_VERIFICATION |
Feitcontrole |
CODE_RETRIEVAL_QUERY |
Zoeken in codebases |
Gebruik RETRIEVAL_DOCUMENT voor documenten en RETRIEVAL_QUERY voor zoekopdrachten:
def embed_document(text: str) -> list:
result = client.models.embed_content(
model="gemini-embedding-001",
contents=text,
config=types.EmbedContentConfig(task_type="RETRIEVAL_DOCUMENT"),
)
return result.embeddings[0].values
def embed_query(query: str) -> list:
result = client.models.embed_content(
model="gemini-embedding-001",
contents=query,
config=types.EmbedContentConfig(task_type="RETRIEVAL_QUERY"),
)
return result.embeddings[0].values
Kies altijd het passende task type
Een document embedden als RETRIEVAL_QUERY geeft suboptimale zoekresultaten, ook al lijkt het technisch te werken. Belangrijk: de query en de documenten moeten met dezelfde modelnaam en dezelfde output_dimensionality worden geëmbed, anders zijn de vectoren niet vergelijkbaar.
Kleinere vectoren met output_dimensionality
gemini-embedding-001 gebruikt Matryoshka Representation Learning, waardoor je de vector kunt verkleinen vanaf de standaard 3072 dimensies. Kleinere vectoren zijn sneller te vergelijken en goedkoper op te slaan, maar verliezen wat nauwkeurigheid. Bij elke andere waarde dan 3072 moet je de vector zelf normaliseren voordat je gelijkenis berekent.
import numpy as np
result = client.models.embed_content(
model="gemini-embedding-001",
contents="Voorbeeldtekst om te embedden.",
config=types.EmbedContentConfig(
task_type="RETRIEVAL_DOCUMENT",
output_dimensionality=768,
),
)
waarden = np.array(result.embeddings[0].values)
genormaliseerd = waarden / np.linalg.norm(waarden)
print(f"Dimensies: {len(genormaliseerd)}")
Meerdere teksten in één call
teksten = [
"Python is een programmeertaal",
"JavaScript draait in de browser",
"Rust biedt geheugen-veiligheid",
"Go is ontworpen voor concurrency",
]
result = client.models.embed_content(
model="gemini-embedding-001",
contents=teksten,
config=types.EmbedContentConfig(task_type="RETRIEVAL_DOCUMENT"),
)
embeddings = [e.values for e in result.embeddings]
print(f"{len(embeddings)} embeddings gegenereerd")
Cosinus-gelijkenis berekenen
Bij de standaard 3072 dimensies zijn de vectoren al genormaliseerd, dus is het inproduct gelijk aan de cosinus-gelijkenis. De functie hieronder normaliseert voor de zekerheid, zodat hij ook klopt bij een aangepaste output_dimensionality.
import numpy as np
def cosine_similarity(vec1: list, vec2: list) -> float:
v1 = np.array(vec1)
v2 = np.array(vec2)
return float(np.dot(v1, v2) / (np.linalg.norm(v1) * np.linalg.norm(v2)))
tekst1 = "Python is een populaire programmeertaal"
tekst2 = "Python wordt veel gebruikt voor data science"
tekst3 = "Amsterdam is de hoofdstad van Nederland"
emb1 = embed_document(tekst1)
emb2 = embed_document(tekst2)
emb3 = embed_document(tekst3)
print(f"Gelijkenis 1-2: {cosine_similarity(emb1, emb2):.4f}")
print(f"Gelijkenis 1-3: {cosine_similarity(emb1, emb3):.4f}")
Semantisch zoeksysteem bouwen
import numpy as np
from dataclasses import dataclass
from typing import List
@dataclass
class Document:
id: str
tekst: str
embedding: List[float] = None
def index_documents(documenten: List[Document]) -> List[Document]:
teksten = [doc.tekst for doc in documenten]
result = client.models.embed_content(
model="gemini-embedding-001",
contents=teksten,
config=types.EmbedContentConfig(task_type="RETRIEVAL_DOCUMENT"),
)
for doc, emb in zip(documenten, result.embeddings):
doc.embedding = emb.values
return documenten
def search(query: str, documenten: List[Document], top_k: int = 3) -> List[tuple]:
query_result = client.models.embed_content(
model="gemini-embedding-001",
contents=query,
config=types.EmbedContentConfig(task_type="RETRIEVAL_QUERY"),
)
query_emb = np.array(query_result.embeddings[0].values)
scores = []
for doc in documenten:
doc_emb = np.array(doc.embedding)
score = float(np.dot(query_emb, doc_emb) / (np.linalg.norm(query_emb) * np.linalg.norm(doc_emb)))
scores.append((score, doc))
scores.sort(key=lambda x: x[0], reverse=True)
return scores[:top_k]
docs = [
Document("1", "Python is een krachtige programmeertaal voor data science."),
Document("2", "Django is een web framework voor Python."),
Document("3", "React is een JavaScript library voor UI."),
Document("4", "Machine learning modellen worden getraind op data."),
]
docs = index_documents(docs)
resultaten = search("Welke talen zijn goed voor webontwikkeling?", docs)
for score, doc in resultaten:
print(f"Score: {score:.4f} | {doc.tekst[:60]}...")
Embeddings opslaan in een vectordatabase
Voor productie sla je embeddings op in een vectordatabase zodat je niet bij elke zoekopdracht opnieuw hoeft te embedden. Onderstaand voorbeeld gebruikt Chroma. Zorg dat de dimensie van je collectie overeenkomt met de output_dimensionality die je gebruikt.
import chromadb
chroma_client = chromadb.Client()
collection = chroma_client.create_collection("kennisbank")
collection.add(
documents=[doc.tekst for doc in docs],
embeddings=[doc.embedding for doc in docs],
ids=[doc.id for doc in docs],
)
query_result = client.models.embed_content(
model="gemini-embedding-001",
contents="Welke talen zijn goed voor webontwikkeling?",
config=types.EmbedContentConfig(task_type="RETRIEVAL_QUERY"),
)
query_emb = query_result.embeddings[0].values
results = collection.query(
query_embeddings=[query_emb],
n_results=3,
)
Bespaar met de Batch API
Voor grote corpora die je niet realtime hoeft te embedden, gebruik je de Batch API. Dat is ongeveer de helft goedkoper dan losse realtime-aanvragen en is ideaal om in één keer een hele kennisbank te indexeren.
Hoeveel tekst kan ik per embedding verwerken?
gemini-embedding-001 accepteert tot 2048 tokens per invoer. Langere teksten moet je splitsen in chunks. Gebruik overlappende chunks, bijvoorbeeld 500 woorden met 50 woorden overlap, voor betere zoekkwaliteit.
Kan ik de dimensie van de vector aanpassen?
Ja, via de output_dimensionality-parameter. De standaard is 3072 dimensies; veelgebruikte kleinere waarden zijn 1536 en 768. Kleinere vectoren zijn sneller en goedkoper, maar minder nauwkeurig. Normaliseer de vector zelf zodra je een andere waarde dan 3072 kiest.
Waarom werkt mijn oude code met text-embedding-004 niet meer?
Dat model is op 14 januari 2026 uitgeschakeld en geeft nu een foutmelding. Stap over op gemini-embedding-001 en op de nieuwe google-genai SDK met een Client-object. Let op dat de standaarddimensie veranderde van 768 naar 3072, dus bestaande indexen moet je opnieuw opbouwen of expliciet op 768 zetten.
Hoe lees ik de embedding uit het antwoord?
In de nieuwe SDK staat het resultaat in result.embeddings, een lijst van objecten met een attribuut values. De eerste vector haal je op met result.embeddings[0].values. Dit verschilt van de oude SDK, die result["embedding"] gebruikte.
Wat kost embedden met de Gemini API?
gemini-embedding-001 kost ongeveer 0,15 dollar per miljoen tokens bij realtime-aanvragen en ongeveer 0,075 dollar per miljoen tokens via de Batch API. Controleer de actuele tarieven op de officiële prijspagina van Google, want prijzen kunnen wijzigen.
Moet ik query en documenten met hetzelfde task type embedden?
Nee, gebruik juist verschillende task types: RETRIEVAL_DOCUMENT voor de opgeslagen documenten en RETRIEVAL_QUERY voor de zoekopdracht. Wel moeten beide hetzelfde model en dezelfde output_dimensionality gebruiken, anders zijn de vectoren onvergelijkbaar.