Waarom gestructureerde output
Taalmodellen genereren standaard vrije tekst. Voor applicaties die data verwerken heb je betrouwbare, parseerbare output nodig. JSON uit vrije tekst halen is fragiel: markdown-codeblokken, extra uitleg, wisselende veldnamen en ontbrekende verplichte velden breken je verwerking.
De Gemini API ondersteunt twee niveaus van gestructureerde output: de JSON-modus (altijd geldige JSON, vrije structuur) en een response schema (JSON dat voldoet aan een schema dat jij bepaalt).
Werk met de huidige SDK
De oude google-generativeai SDK (import google.generativeai as genai) is uitgefaseerd; de ondersteuning eindigde op 30 november 2025. Gebruik de unified google-genai SDK: from google import genai. Installeren doe je met pip install google-genai. Alle voorbeelden hieronder gebruiken die SDK.
JSON-modus inschakelen
De eenvoudigste methode: zet response_mime_type op "application/json".
from google import genai
from google.genai import types
import json
import os
client = genai.Client(api_key=os.environ["GEMINI_API_KEY"])
response = client.models.generate_content(
model="gemini-2.5-flash",
contents="Geef een lijst van 5 grote Europese steden met hun land en inwoneraantal.",
config=types.GenerateContentConfig(
response_mime_type="application/json",
),
)
data = json.loads(response.text)
print(data)
Met de JSON-modus krijg je altijd geldige JSON terug, maar het model bepaalt zelf de structuur. Voor voorspelbare verwerking voeg je een schema toe.
Schema definieren met response_schema
Voor een vaste structuur geef je een schema mee. Dat kan als platte JSON Schema-dictionary:
schema = {
"type": "array",
"items": {
"type": "object",
"properties": {
"stad": {"type": "string"},
"land": {"type": "string"},
"inwoners": {"type": "integer"},
"hoofdstad": {"type": "boolean"},
},
"required": ["stad", "land", "inwoners", "hoofdstad"],
},
}
response = client.models.generate_content(
model="gemini-2.5-flash",
contents="Geef een lijst van 5 grote Europese steden.",
config=types.GenerateContentConfig(
response_mime_type="application/json",
response_schema=schema,
),
)
steden = json.loads(response.text)
for stad in steden:
print(f"{stad['stad']} ({stad['land']}): {stad['inwoners']:,} inwoners")
Pydantic-modellen gebruiken
De Python SDK accepteert Pydantic-modellen direct als schema. Dat is doorgaans de prettigste werkwijze: je krijgt type-validatie, IDE-ondersteuning en compactere code. Bovendien lees je het resultaat direct uit via response.parsed, zonder zelf json.loads te hoeven aanroepen.
from pydantic import BaseModel
from typing import Optional
class Stad(BaseModel):
naam: str
land: str
inwoners: int
hoofdstad: bool
bijzonderheid: Optional[str] = None
class StedenLijst(BaseModel):
steden: list[Stad]
gegenereerd_op: str
response = client.models.generate_content(
model="gemini-2.5-flash",
contents="Geef 5 grote Europese steden.",
config=types.GenerateContentConfig(
response_mime_type="application/json",
response_schema=StedenLijst,
),
)
result: StedenLijst = response.parsed
for stad in result.steden:
print(f"{stad.naam}: {stad.inwoners:,}")
Vraag een lijst zonder wrapper-model
Heb je alleen een lijst nodig? Geef dan list[Stad] rechtstreeks als response_schema. Je hoeft geen extra wrapper-klasse te maken: response.parsed is dan meteen een Python-lijst van Stad-objecten.
Enums in schema's
Beperk een veld tot een vaste set waarden met een enum. Een str-gebaseerde Enum of een Literal werkt allebei.
from enum import Enum
class Prioriteit(str, Enum):
LAAG = "laag"
MIDDEL = "middel"
HOOG = "hoog"
KRITIEK = "kritiek"
class Taak(BaseModel):
titel: str
prioriteit: Prioriteit
geschatte_uren: float
toewijzing: str
class TakenLijst(BaseModel):
taken: list[Taak]
sprint_nummer: int
response = client.models.generate_content(
model="gemini-2.5-flash",
contents=(
"Maak een sprint-backlog voor een webshop-project. "
"Genereer 5 taken met realistische schattingen."
),
config=types.GenerateContentConfig(
response_mime_type="application/json",
response_schema=TakenLijst,
),
)
backlog: TakenLijst = response.parsed
Geneste objecten en arrays
Schema's mogen zo diep genest zijn als nodig. Gemini vult de hele boom in.
class Adres(BaseModel):
straat: str
huisnummer: str
postcode: str
stad: str
land: str
class Contactpersoon(BaseModel):
naam: str
email: str
telefoon: Optional[str] = None
adres: Adres
rollen: list[str]
class Bedrijf(BaseModel):
naam: str
kvk_nummer: str
website: str
contactpersonen: list[Contactpersoon]
sector: str
response = client.models.generate_content(
model="gemini-2.5-flash",
contents="Genereer een voorbeeldbedrijf in de IT-sector met 2 contactpersonen.",
config=types.GenerateContentConfig(
response_mime_type="application/json",
response_schema=Bedrijf,
),
)
bedrijf: Bedrijf = response.parsed
Tekst extraheren met schema
Gestructureerde output is sterk bij het uittrekken van data uit vrije tekst of documenten. Je stuurt de tekst mee en laat Gemini de velden invullen.
class Persartikel(BaseModel):
kop: str
datum: Optional[str] = None
auteur: Optional[str] = None
samenvatting: str
kernpunten: list[str]
sentiment: str
categorie: str
tekst = """Amsterdam, 15 maart 2026 - Google heeft vandaag aangekondigd...
(lang nieuwsbericht)"""
prompt = f"Extraheer informatie uit dit artikel:
{tekst}"
response = client.models.generate_content(
model="gemini-2.5-flash",
contents=prompt,
config=types.GenerateContentConfig(
response_mime_type="application/json",
response_schema=Persartikel,
),
)
artikel: Persartikel = response.parsed
Goede gewoonten voor productie
Een paar punten die je code robuust houden:
- Valideer altijd na ontvangst. Ook met
response_schemais Pydantic-validatie een goed vangnet. Wikkelresponse.parsedofModel.model_validate_json(response.text)in eentry/excepten log de ruwe respons bij een fout. - Houd schema's zo plat als kan. Heel diep geneste of optionele velden maken de output minder stabiel. Splits desnoods in twee aanroepen.
- Pin je model op een stabiele versie. Gebruik een vaste naam zoals
gemini-2.5-flashin plaats van een latest- of preview-alias, zodat gedrag niet onaangekondigd verandert. - Let op modeluitfasering. Gemini 2.0 Flash en 2.0 Flash-Lite zijn op 1 juni 2026 uitgezet. Controleer bij oudere code of het modelnaam-argument nog bestaat en stap zo nodig over op een nieuwer model.
Is response_schema beschikbaar voor alle Gemini-modellen?
Gestructureerde output met response_schema werkt op de huidige modellen, waaronder de Gemini 2.5- en 3.x-reeksen zoals gemini-2.5-flash, gemini-2.5-pro en de nieuwere 3.x-modellen. Oudere of zeer lichte modellen ondersteunen soms alleen response_mime_type zonder schema; raadpleeg de modelpagina van Google voor de actuele lijst.
Wat als het model verplichte velden weglaat?
Met response_schema dwingt Gemini de structuur af, inclusief verplichte velden. Bouw voor productie toch een validatiestap in met Pydantic, zodat een onverwachte respons netjes wordt opgevangen in plaats van je verwerking te breken.
Moet ik response.text zelf parsen?
Niet als je een Pydantic-model als schema gebruikt. De google-genai SDK vult dan response.parsed met een kant-en-klaar object. Geef je een platte JSON Schema-dictionary mee, dan parse je response.text zelf met json.loads.
Kan ik ook XML of andere formaten opvragen?
De gegarandeerde schema-binding werkt alleen voor JSON. Voor andere formaten gebruik je JSON als tussenstap, of je vraagt in de prompt om een specifiek formaat. Die laatste route is minder betrouwbaar.
Werkt gestructureerde output ook in een chatsessie?
Ja. Maak een sessie met client.chats.create en geef per bericht een config mee aan send_message, of stel een vaste config in bij het aanmaken van de chat. De structuur wordt dan voor elk antwoord afgedwongen.
Welk model kies ik het best?
Voor de meeste extractie- en classificatietaken is gemini-2.5-flash een goede, snelle en voordelige keuze. Heb je zwaardere redenering of complexe geneste schema's nodig, kies dan een pro-variant. Pin altijd een stabiele modelnaam in productie.