# Embeddings genereren met de Gemini API [[TOC]] ## 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. :::warn title="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. ```bash 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 ```python 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: ```python 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 ``` :::tip title="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. ```python 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 ```python 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`. ```python 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 ```python 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. ```python 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, ) ``` :::tip title="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. ::: :::faq ### 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. :::