Duiding7 minGevorderd

AI-agent lokaal bouwen met Ollama: tool calling in Python

Bouw een AI-agent die offline draait: tool calling met Ollama en de Python-bibliotheek, een model dat tools aankan en de code. Je data blijft lokaal.

Conceptueel stilleven bij artikel 'AI-agent lokaal bouwen met Ollama: tool calling in Python'AI-beeld

Wil je AI inzetten op klantdossiers, interne documenten of gevoelige aanvragen zonder dat die bij een externe aanbieder terechtkomen, dan kan een agent op je eigen machine dat doen. Wat Ollama is en hoe je het installeert staat in wat Ollama is en hoe lokale modellen werken. Dit stuk gaat over de volgende stap: een agent bouwen die zelf functies aanroept, helemaal offline.

Update 4 november 2026 Gecontroleerd tegen de huidige Ollama-documentatie (Ollama v0.40.14 van 3 november, de Python-bibliotheek 0.6.3 van 29 september). Het origineel koos llama3.1 en de OpenAI-bibliotheek. Nu gebruikt het voorbeeld qwen3.5:4b en de eigen ollama-bibliotheek. De tekst waarschuwt ook voor de standaard contextlengte en voor de cloudmodellen, die je privacybelofte kunnen breken.

Hoe werkt tool calling met Ollama?

Een taalmodel voert zelf niets uit. Bij tool calling beschrijf je in je verzoek welke functies bestaan. Het model antwoordt dan met een gestructureerde aanroep in plaats van tekst: de functienaam en de argumenten. Jouw code voert die functie uit. Het resultaat gaat terug naar het model als bericht met rol tool en het veld tool_name, waarna het model verder redeneert of een eindantwoord geeft (Bron: Ollama docs).

Dat herhaal je in een lus tot het model geen aanroepen meer teruggeeft. Die lus is de agent. Het model kan ook meerdere aanroepen tegelijk teruggeven, dus loop altijd over alle tool_calls en stuur per aanroep één antwoord terug.

Welk model en welke bibliotheek heb je nodig?

Je hebt drie dingen: Ollama, Python en een model dat tools ondersteunt. De Python-bibliotheek installeer je met pip install ollama. De huidige versie is 0.6.3 van 29 september 2026, onder MIT-licentie en voor Python 3.8 of nieuwer (Bron: PyPI).

Kies het model op de eigenschap tools. Qwen 3.5 heeft die, naast vision en thinking. De 4B-variant is zo’n 3,3 tot 4 GB groot met een contextvenster van 256K (Bron: Ollama modelpagina). Dat past op een gewone laptop. Heb je een zware videokaart of veel werkgeheugen, dan is qwen3.8:27b sterker, maar met 18 GB ook een stuk zwaarder (Bron: Ollama modelpagina). Meer over dat model lees je in Qwen 3.8 27B: gratis AI-model dat op je eigen computer draait. Het oude llama3.1 ondersteunt tools nog steeds, maar is een jaar oud.

ollama pull qwen3.5:4b
python3 -m venv venv
source venv/bin/activate
pip install ollama

Hoe bouw je de agent?

Als voorbeeld nemen we een agent die binnenkomende aanvragen triëert. Hij zoekt de klant op aan de hand van het klantnummer, zet de aanvraag in een categorie en schrijft dat weg in een lokaal bestand. Twee gewone Python-functies zijn de tools. De bibliotheek leidt het schema af uit de typehints en de docstring, dus die docstring is wat het model leest om te kiezen.

import json
from ollama import chat

MODEL = "qwen3.5:4b"

KLANTEN = {
    "10482": {"naam": "Bakkerij De Gouden Korst", "contract": "Zakelijk Plus"},
    "20931": {"naam": "Van Dijk Installatie", "contract": "Basis"},
}
CATEGORIEEN = {"storing", "factuur", "contract", "overig"}


def zoek_klant(klantnummer: str) -> str:
    """Zoek een klant op via het klantnummer en geef naam en contract terug."""
    klant = KLANTEN.get(str(klantnummer).strip())
    return json.dumps(klant, ensure_ascii=False) if klant else "Onbekend klantnummer"


def zet_in_categorie(categorie: str, samenvatting: str) -> str:
    """Zet de aanvraag in een categorie (storing, factuur, contract of overig) met een samenvatting van één zin."""
    if categorie not in CATEGORIEEN:
        return f"Ongeldige categorie. Kies uit: {', '.join(sorted(CATEGORIEEN))}"
    regel = {"categorie": categorie, "samenvatting": samenvatting}
    with open("triage.jsonl", "a", encoding="utf-8") as bestand:
        bestand.write(json.dumps(regel, ensure_ascii=False) + "\n")
    return "Opgeslagen"

Let op de str(...) in de eerste functie en de controle op toegestane waarden in de tweede. Het officiële voorbeeld van de bibliotheek werkt met een conversie omdat teruggegeven argumenten niet altijd precies bij het schema passen. Een klein model stuurt een klantnummer soms als getal. Een foutmelding als tekst terugsturen helpt het model bovendien om zichzelf te corrigeren.

Dan de lus zelf:

TOOLS = {functie.__name__: functie for functie in (zoek_klant, zet_in_categorie)}

SYSTEEMPROMPT = (
    "Je triëert binnenkomende aanvragen. Staat er een klantnummer in de tekst, "
    "zoek dan eerst die klant op. Zet de aanvraag daarna in precies één categorie. "
    "Meld tot slot in één zin wat je gedaan hebt."
)


def draai_agent(aanvraag: str, max_stappen: int = 6) -> str:
    berichten = [
        {"role": "system", "content": SYSTEEMPROMPT},
        {"role": "user", "content": aanvraag},
    ]
    for _ in range(max_stappen):
        antwoord = chat(
            model=MODEL,
            messages=berichten,
            tools=list(TOOLS.values()),
            options={"temperature": 0},
        )
        berichten.append(antwoord.message)
        if not antwoord.message.tool_calls:
            return antwoord.message.content
        for call in antwoord.message.tool_calls:
            functie = TOOLS.get(call.function.name)
            if functie is None:
                uitkomst = f"Onbekende tool: {call.function.name}"
            else:
                try:
                    uitkomst = functie(**call.function.arguments)
                except Exception as fout:
                    uitkomst = f"Fout: {fout}"
            berichten.append(
                {"role": "tool", "tool_name": call.function.name, "content": str(uitkomst)}
            )
    return "Gestopt: maximaal aantal stappen bereikt."


if __name__ == "__main__":
    print(draai_agent("Klantnummer 10482: de pinautomaat in onze winkel valt sinds vanochtend steeds uit."))

Drie dingen in deze lus zijn bewust gekozen. Het assistent-bericht met de aanroepen komt vóór de toolresultaten in berichten, zoals de documentatie voorschrijft. Een onbekende toolnaam of een uitzondering stopt de agent niet maar gaat als tekst terug naar het model. En max_stappen voorkomt dat een model dat in cirkels draait je machine bezig houdt.

Na een run staat er een regel in triage.jsonl met de gekozen categorie en samenvatting. Welke woorden het model kiest, verschilt per run en per model. Meet daarom niet op gevoel: hoe je zo’n agent met vaste testcases controleert, staat in AI-agents evalueren met een LLM-judge, dat ook op Ollama en Qwen 3.5 draait.

Waarom staat mijn contextlengte te laag voor een agent?

Ollama kiest de contextlengte op basis van je videogeheugen: onder 24 GiB start het met 4.096 tokens, tussen 24 en 48 GiB met 32.000, vanaf 48 GiB met 256.000. Voor agents raadt de documentatie minstens 64.000 tokens aan (Bron: Ollama docs). Bij elke ronde in de lus groeit het gesprek met toolresultaten, dus een klein venster loopt snel vol en het model vergeet dan je instructie.

Zet de lengte bij het starten van de server:

OLLAMA_CONTEXT_LENGTH=64000 ollama serve

Een groter venster kost geheugen. Controleer met ollama ps in de kolom PROCESSOR of het model niet deels naar de processor uitwijkt, want dat maakt de agent traag.

Hoe houd je de agent echt offline en privé?

Dat het model lokaal draait, is niet genoeg. Vier punten bepalen of je belofte klopt.

Draai alleen lokale modellen. Ollama zegt dat het je prompts en data niet ziet als je lokaal draait. Bij de optionele cloudmodellen worden prompts en antwoorden wel verwerkt op afstand, volgens Ollama zonder opslag of training (Bron: Ollama docs). Wil je die route uitsluiten, zet dan OLLAMA_NO_CLOUD=1 of "disable_ollama_cloud": true in ~/.ollama/server.json en herstart Ollama. Je verliest daarmee de cloudmodellen en de webzoekfunctie.

Laat Ollama aan je eigen computer vastzitten. Standaard luistert hij op 127.0.0.1:11434. Zet je hem via OLLAMA_HOST open voor je netwerk, dan kan iedereen daar je model gebruiken. Hoe je dat afschermt, lees je in de gids Lokale AI-server afschermen: zo voorkom je LLMjacking.

Beperk wat de tools mogen. Een agent die tekst van buiten leest en daarna iets kan wegschrijven, is gevoelig voor prompt-injectie: een aanvraag kan instructies bevatten. In het voorbeeld kan de agent alleen een regel toevoegen aan één bestand en een klant opzoeken. Geef je een tool de macht om te mailen, te verwijderen of een shell te starten, vraag dan eerst menselijke goedkeuring.

Test het echt. Trek de netwerkkabel eruit of zet wifi uit en draai de agent opnieuw. Werkt hij nog, dan gaat er niets naar buiten.

Kan ik bestaande OpenAI-code hergebruiken?

Ja, voor een agent die je al tegen een cloud-API schreef. Ollama biedt een OpenAI-compatibele API op http://localhost:11434/v1/. Je zet de basis-URL om en vult een willekeurige sleutel in, want die wordt genegeerd (Bron: Ollama docs).

from openai import OpenAI

client = OpenAI(base_url="http://localhost:11434/v1/", api_key="ollama")

Chat completions ondersteunt tools, maar tool_choice niet: je kunt het model dus niet dwingen een bepaalde tool te kiezen. Voor opties zoals de contextlengte is de eigen ollama-bibliotheek handiger. De documentatievoorbeelden gebruiken die ook.

Waar liggen de grenzen van een lokale agent?

Kleine modellen maken meer fouten bij het kiezen van tools en het invullen van argumenten dan de grote cloudmodellen. Voor afgebakende taken zoals aanvragen triëren, documenten samenvatten of gegevens ordenen is een model van 4 tot 9 miljard parameters vaak genoeg. Moet de agent lang plannen of veel tools combineren, dan loop je eerder tegen de grens aan. Ga dan een maat omhoog voordat je naar de cloud kijkt.

De winst is privacy en geen kosten per aanroep, de prijs is hardware en het zelf beheren van de omgeving. Wil je meer over lokale modellen, hardware en keuzes op een rij, begin dan bij de hardwaregids lokale LLM’s draaien.

Lees verder

Veelgestelde vragen

Welke Ollama-modellen ondersteunen tool calling?

Op ollama.com/search kun je filteren op de eigenschap tools; elke modelpagina vermeldt onder capabilities of tool calling werkt. Qwen 3.5 en Qwen 3.8 hebben die eigenschap, net als het oudere Llama 3.1. Voor een eerste agent op een gewone laptop is qwen3.5:4b een praktische keuze: ongeveer 3,3 tot 4 GB. Heb je een zware videokaart, dan is qwen3.8:27b (18 GB) sterker. Controleer de modelpagina altijd zelf, want de lijst verandert elke paar weken.

Hoe geef je een Python-functie als tool aan Ollama?

Je geeft de functie zelf mee in de tools-lijst van ollama.chat. De bibliotheek bouwt het schema uit de typehints en de docstring, dus beschrijf in de docstring kort wat de functie doet. Je kunt ook een handgeschreven JSON-schema meegeven als je meer controle wilt. Het model antwoordt met tool_calls, jouw code roept de functie aan en je stuurt de uitkomst terug als bericht met role tool en tool_name.

Gaat er data naar de cloud als ik Ollama gebruik?

Niet bij lokale modellen: Ollama zegt in zijn FAQ je prompts en data dan niet te zien. Bij de optionele cloudmodellen worden prompts wel op afstand verwerkt. Wil je dat uitsluiten, zet dan OLLAMA_NO_CLOUD=1 of de optie disable_ollama_cloud in server.json en herstart Ollama. Daarmee verlies je ook de cloudmodellen en webzoekfunctie.

Waarom maakt mijn lokale agent soms fouten bij tool calling?

Kleine modellen sturen argumenten niet altijd in het juiste formaat, bijvoorbeeld een getal waar je een tekst verwacht. Zet daarom in je tools een conversie en een controle op toegestane waarden, zoals de officiële voorbeelden ook doen. Geef een foutmelding als tekst terug zodat het model zichzelf kan corrigeren. Bouw ook een maximum aantal stappen in zodat de lus niet eindeloos draait.

Kan ik mijn bestaande OpenAI-code hergebruiken met Ollama?

Ja. Ollama biedt een OpenAI-compatibele API op http://localhost:11434/v1/. Je zet de basis-URL om en vult een willekeurige api_key in, want die wordt genegeerd. Chat completions ondersteunt tools, maar tool_choice niet. Wil je Ollama-specifieke opties zoals de contextlengte, dan is de eigen ollama-bibliotheek handiger.

Bronnen

Waar deze informatie vandaan komt.

  1. Ollama docs: Clouddocs.ollama.com