Duiding8 minGevorderd

Gemini function calling: bouw de agent-loop in Python

Wat is function calling in Gemini en hoe bouw je de agent-loop zelf? Uitleg plus werkende Python-code met gemini-3.8-flash en de Interactions API.

Conceptueel stilleven bij artikel 'Gemini function calling: bouw de agent-loop in Python'AI-beeld

Een taalmodel dat alleen praat, komt snel tegen zijn grenzen aan. Vraag je Gemini naar de status van jouw bestelling, dan kan het die niet weten: die gegevens staan in jouw systeem en niet in de trainingsdata. Function calling overbrugt dat gat. Dezelfde techniek ligt onder vrijwel elke AI-agent. Hieronder lees je eerst kort hoe het werkt. Daarna bouw je de agent-loop zelf na in Python met gemini-3.8-flash, zonder framework ertussen.

Update 4 november 2026 Dit stuk voegt twee gidsen van 25 juli en 6 augustus samen. De code is herschreven voor de Interactions API, die sinds juni 2026 algemeen beschikbaar is. Het model is nu gemini-3.8-flash. Een functieaanvraag heet daar een function_call-stap. De structuur is nagelopen tegen google-genai 2.34.0 van 29 oktober.

Wat is function calling in Gemini?

Function calling laat Gemini bepalen welke van jouw functies nodig is en met welke waarden, maar voert die functie zelf niet uit. Het model geeft een gestructureerd verzoek terug met een naam en argumenten. Jouw code draait de functie en stuurt de uitkomst terug, waarna Gemini er een leesbaar antwoord van maakt. Je houdt zo zelf de controle over wat er echt gebeurt (Bron: Google).

De lus heeft vier stappen:

  1. Beschrijf de functie. Je vertelt Gemini hoe de functie heet, wat hij doet en welke parameters hij verwacht.
  2. Gemini kiest en vult in. Heeft het model een functie nodig, dan krijg je een function_call-stap terug met de naam, de argumenten en een eigen id, bijvoorbeeld {"type": "function_call", "id": "...", "name": "get_order_status", "arguments": {"order_id": "123"}}.
  3. Jij voert uit. Jouw applicatie bevraagt de database, roept een API aan of doet de berekening. Gemini heeft hier geen toegang toe.
  4. Resultaat terug. Je stuurt de uitkomst terug met datzelfde id als call_id. Het model maakt er tekst van of vraagt de volgende functie aan.

Beginner-tip Zie Gemini als een receptionist die de vraag begrijpt en het juiste formulier invult. Jouw programma is de collega achter het loket die de klus daadwerkelijk uitvoert. Het model raakt je systemen dus nooit rechtstreeks aan.

Wat heb je nodig om te beginnen?

Drie dingen.

  • Een API-sleutel. Maak er een in Google AI Studio. Na het accepteren van de voorwaarden maakt AI Studio zelf een standaardproject en sleutel aan. Zet de sleutel in de omgevingsvariabele GEMINI_API_KEY, bijvoorbeeld met export GEMINI_API_KEY=jouw-sleutel, zodat hij niet in je code belandt (Bron: Google).
  • De SDK. De Interactions API vraagt google-genai 2.3.0 of nieuwer en Python 3.11 of hoger. De huidige versie is 2.34.0 (Bron: PyPI). Installeer met pip install --upgrade google-genai.
  • Het model. De API-naam van Gemini 3.8 Flash is gemini-3.8-flash (Bron: Google). Meer over dat model, de prijs en de Cyber-variant lees je in Gemini 3.8 Flash: wat het kost, en wie de cyberversie krijgt.

De gratis laag van 3.8 Flash heeft lagere limieten dan de betaalde. Voor leren en kleine projecten is dat meestal genoeg. Let op dat een gratis project opgeslagen gesprekken één dag bewaart en een betaald project 55 dagen (Bron: Google). Voor het voorbeeld hieronder maakt dat niets uit, want de hele lus duurt seconden.

Hoe bouw je de agent-loop stap voor stap?

Het voorbeeld is een kleine bestelassistent met twee functies. Eerst de lus in woorden: het model krijgt je vraag en de lijst functies. Vraagt het om een functie, dan draai jij hem en stuur je het resultaat terug. Dat herhaal je tot het model een gewoon antwoord geeft.

Stap 1: schrijf gewone functies

Begin met normale Python. In een echte agent zit hier een database of API achter. Voor het voorbeeld volstaat vaste data.

import json
from google import genai

client = genai.Client()  # leest GEMINI_API_KEY uit je omgeving
MODEL = "gemini-3.8-flash"


def get_order_status(order_id: str) -> dict:
    orders = {"123": "onderweg", "456": "in behandeling"}
    return {"order_id": order_id, "status": orders.get(order_id, "onbekend")}


def get_delivery_date(order_id: str) -> dict:
    dates = {"123": "2026-11-06", "456": "2026-11-10"}
    return {"order_id": order_id, "bezorgdatum": dates.get(order_id)}


FUNCTIES = {f.__name__: f for f in (get_order_status, get_delivery_date)}

Het woordenboek FUNCTIES is meteen je toegangslijst: het model kan alleen aanvragen wat daarin staat.

Stap 2: beschrijf de functies voor het model

Per functie maak je een declaratie met naam, uitleg en een schema voor de parameters. Deze beschrijving is hoe Gemini snapt wanneer en hoe het je functie mag gebruiken. Hoe scherper de uitleg, hoe beter de keuze (Bron: Google).

ORDER_ID = {"order_id": {"type": "string", "description": "Ordernummer, bijvoorbeeld 123"}}

TOOLS = [
    {
        "type": "function",
        "name": "get_order_status",
        "description": "Geeft de huidige status van een bestelling.",
        "parameters": {"type": "object", "properties": ORDER_ID, "required": ["order_id"]},
    },
    {
        "type": "function",
        "name": "get_delivery_date",
        "description": "Geeft de verwachte bezorgdatum van een bestelling.",
        "parameters": {"type": "object", "properties": ORDER_ID, "required": ["order_id"]},
    },
]

Stap 3: stuur de vraag en de functies naar Gemini

Een eerste aanroep gaat met client.interactions.create. Krijg je geen function_call-stap terug, dan was er geen functie nodig en staat het antwoord in output_text.

interaction = client.interactions.create(
    model=MODEL,
    input="Wat is de status van bestelling 123?",
    tools=TOOLS,
)
for step in interaction.steps:
    if step.type == "function_call":
        print(step.id, step.name, step.arguments)

Stap 4: voer uit en geef het resultaat terug

Draai de functie met de argumenten van het model en stuur de uitkomst terug in een tweede aanroep. Daarin verwijs je met previous_interaction_id naar de eerste beurt, zodat de server de geschiedenis kent. De tools moet je opnieuw meegeven, want die gaan niet vanzelf mee naar de volgende beurt (Bron: Google).

Stap 5: pak het samen in een lus

Nu stap 3 en 4 samen, met een plafond op het aantal rondes en foutafhandeling:

def run_agent(vraag: str, max_rondes: int = 10) -> str:
    interaction = client.interactions.create(model=MODEL, input=vraag, tools=TOOLS)
    afgehandeld = set()

    for _ in range(max_rondes):
        calls = [
            s for s in interaction.steps
            if s.type == "function_call" and s.id not in afgehandeld
        ]
        if not calls:
            return interaction.output_text  # geen functie meer nodig: klaar

        resultaten = []
        for call in calls:
            afgehandeld.add(call.id)
            try:
                uitkomst, fout = FUNCTIES[call.name](**call.arguments), False
            except Exception as e:  # geef de fout terug, laat de lus niet crashen
                uitkomst, fout = {"error": str(e)}, True
            resultaten.append({
                "type": "function_result",
                "name": call.name,
                "call_id": call.id,
                "is_error": fout,
                "result": [{"type": "text", "text": json.dumps(uitkomst)}],
            })

        interaction = client.interactions.create(
            model=MODEL,
            previous_interaction_id=interaction.id,
            tools=TOOLS,
            input=resultaten,
        )

    return "Gestopt: maximaal aantal rondes bereikt."


print(run_agent("Wat is de status van bestelling 123 en wanneer komt bestelling 456 aan?"))

Vraag je naar twee bestellingen tegelijk, zoals hier, dan kan het model beide aanvragen in één beurt teruggeven. De lus handelt ze dan samen af. De set afgehandeld zorgt dat een stap die je al hebt uitgevoerd nooit een tweede keer draait. De code is gecontroleerd tegen de typen van google-genai 2.34.0 en met een nagebootste respons doorlopen. Draai hem met je eigen sleutel om het echte gedrag van het model te zien.

Hoe voorkom je dat de lus op hol slaat?

Zonder plafond kan een model in theorie eindeloos functies blijven aanvragen. Vier maatregelen vangen het meeste af:

  • Een hard rondelimiet. Het max_rondes in de code hierboven is het simpelste vangnet. Tien rondes is ruim voor de meeste taken.
  • Alleen functies die je wilt toestaan. Wil je niet dat het model bestellingen kan annuleren, dan geef je die functie niet mee. Het opzoeken in FUNCTIES zorgt dat een verzonnen functienaam een fout geeft in plaats van code draait.
  • Fouten teruggeven met is_error. Dan kan het model zijn volgende stap aanpassen in plaats van dat je script stopt.
  • Argumenten valideren. Het model vult de parameters in op basis van jouw beschrijving. Controleer wat je binnenkrijgt voordat je er een database mee bevraagt.

Wil je ook meten of je agent na een prompt- of modelwissel nog hetzelfde doet, bouw dan een testbank zoals in AI-agents evalueren met een LLM-judge.

Kan Gemini meerdere functies tegelijk aanvragen?

Ja. Het model kan meerdere onafhankelijke functies in één beurt aanvragen (parallel) en functies achter elkaar aanroepen waarbij de uitkomst van de ene de invoer van de volgende is. Beide patronen vangt de lus hierboven op.

Met tool_choice in generation_config stuur je het gedrag. De standaard is auto: het model beslist zelf. Met any dwing je een functieaanroep af, met none verbied je die. Met validated laat je het model het schema nauwkeuriger volgen. Met allowed_tools beperk je welke functies in aanmerking komen (Bron: Google).

Gevorderden Gemini 3-modellen kunnen ingebouwde tools zoals Google Search combineren met je eigen functies in één verzoek. Bij streaming komen functieaanroepen binnen als step.delta-events. De argumenten moet je eerst aan elkaar plakken voordat je de functie draait. Werk je stateless met store=False, dan stuur je de volledige geschiedenis zelf mee en moet je de thought- en function_call-stappen ongewijzigd terugsturen. In de gewone werkwijze regelt de SDK de thought signatures voor je. Google raadt een overzichtelijke set van 10 tot 20 tools aan.

Werkt dit ook met generate_content?

Ja. Google noemt generate_content volledig ondersteund, maar adviseert de Interactions API voor alle nieuwe projecten (Bron: Google). Het grootste verschil zit in het bijhouden van het gesprek. Bij generate_content stuur je de functieaanvraag van het model en jouw functieantwoord zelf mee in contents. Bij Interactions draagt previous_interaction_id de status. Bouw je nu iets nieuws, dan is de Interactions-variant uit dit stuk de kortste route. Heb je bestaande code, dan hoef je niet meteen om.

Waarom de lus zelf bouwen als er frameworks zijn?

Omdat je anders blind vertrouwt op een aanroep die je niet begrijpt. Wat CrewAI een “tool” noemt, is jouw functiedeclaratie. Wat LangGraph als een “node” tekent, is een ronde door deze lus. Heb je de lus één keer met de hand gedraaid, dan lees je elk framework anders en weet je waar je moet kijken als een functie niet wordt aangeroepen of de lus niet stopt.

Wat een agent verder is en waar agents nu in de praktijk vastlopen, lees je in AI-agents in 2026: wat zijn ze en wat kun je er echt mee. Hoe dezelfde lus opschaalt naar een team agents dat elkaars werk controleert, zie je in Antigravity Teamwork: een team AI-agents op één klus zetten.

Lees verder

Veelgestelde vragen

Wat is function calling in de Gemini API?

Function calling is de manier waarop Gemini toegang krijgt tot jouw code en gegevens. Je beschrijft welke functies er zijn: een naam, een uitleg en de parameters. Als het model besluit dat een functie nodig is, geeft het geen tekst terug maar een gestructureerd verzoek met de functienaam en de ingevulde argumenten. Gemini voert de functie zelf niet uit. Dat doet jouw code, waarna je de uitkomst terugstuurt en het model er een leesbaar antwoord van maakt.

Heb ik een betaald account nodig om function calling te proberen?

Nee. Op de prijzenpagina van Google staat Gemini 3.8 Flash in de gratis laag als gratis te gebruiken. Je maakt een API-sleutel aan in Google AI Studio en zet die in de omgevingsvariabele GEMINI_API_KEY. De limieten van de gratis laag zijn lager dan bij betaald gebruik en staan per model in AI Studio. De betaalde prijs is 0,75 dollar per miljoen invoertokens en 3,75 dollar per miljoen uitvoertokens tot en met 31 december 2026, daarna het dubbele.

Wat is het verschil tussen de Interactions API en generate_content?

Bij generate_content stuur je zelf bij elke beurt de hele gespreksgeschiedenis mee en lees je het antwoord uit candidates en parts. De Interactions API bewaart de gespreksstatus op de server en geeft een lijst typed stappen terug, zoals function_call en function_result. Je vervolgt een gesprek met previous_interaction_id. Google noemt generate_content nog volledig ondersteund, maar raadt de Interactions API aan voor alle nieuwe projecten.

Wat is het verschil tussen een chatbot en een agent?

Een chatbot antwoordt met tekst en stopt. Een agent mag tussendoor acties laten uitvoeren: een berekening maken, een API bevragen, een bestand lezen. Dat verschil komt vrijwel volledig uit de lus in dit artikel. Zodra een model functies mag aanvragen en jouw code ze uitvoert en terugstuurt, verandert een chatbot in iets dat dingen doet.

Waarom zou ik de agent-loop zelf bouwen als frameworks dit al doen?

Frameworks als LangGraph, CrewAI en AutoGen verpakken deze lus in een paar regels. Handig in productie, maar bij een functie die niet wordt aangeroepen of een lus die niet stopt helpt het enorm als je weet wat eronder gebeurt. Eén keer met de hand bouwen maakt elk framework daarna leesbaar.

Bronnen

Waar deze informatie vandaan komt.