Toen we voor een klant in de energiesector een agent implementeerden om rapporten uit een SCADA-systeem te automatiseren, verliepen de eerste dagen probleemloos. Daarna begonnen de stille fouten: de agent riep de juiste tool aan, maar stuurde de argumenten in het verkeerde formaat — een getal waar een string verwacht werd, null waar een waarde vereist was. Het gevolg? De tool gaf een fout terug, de agent negeerde die en ging gewoon verder. Het rapport werd gegenereerd, het zag er correct uit, maar de cijfers klopten niet. Het kostte ons meerdere dagen om de oorzaak te achterhalen.
Tool calling — het aanroepen van externe tools en systemen via een LLM — is de kernfunctionaliteit van elke productie-agent. En tegelijkertijd zijn grootste bron van stille fouten. Dit artikel richt zich op concrete patronen: hoe u toolschema's ontwerpt, argumenten valideert, fouten afhandelt en retry-logica bouwt die werkt onder echte omstandigheden.
Waarom tool calling misloopt — niet zoals u denkt
Wanneer ontwikkelaars de onbetrouwbaarheid van agenten onderzoeken, zoeken ze de oorzaak meestal in het model. Slecht redeneren, onvoldoende begrip van de opdracht, verkeerde interpretatie. In de praktijk zien we dat de meeste productie-incidenten een andere oorzaak hebben: slecht ontworpen toolschema's, ontbrekende argumentvalidatie, onafgehandelde toolfouten.
De vier meest voorkomende fatronen die we tegenkomen:
- De agent roept de verkeerde tool aan — omdat de beschrijvingen te veel op elkaar lijken, vaag zijn of onvoldoende onderscheidend
- De agent roept de juiste tool aan met verkeerde argumenten — omdat het schema niet nauwkeurig beschrijft welke waarden geldig zijn
- De agent negeert een toolfout — omdat niemand het geval heeft afgehandeld waarbij een tool een uitzondering retourneert
- De agent belandt in een retry-loop — omdat de retry-logica niet begrensd is en geen exponentiële backoff heeft
Geen van deze problemen heeft te maken met de intelligentie van het model. Ze zijn allemaal oplosbaar op ingenieursniveau. En ze herhalen zich ongeacht de gebruikte agentarchitectuur.
Ontwerp van het toolschema: precisie boven beknoptheid
Het toolschema is wat de agent ziet wanneer hij beslist wat hij aanroept. Als het schema onnauwkeurig is, schat de agent — en schatten in een deterministisch systeem is een probleem.
Basisregels voor schemaontwerp die in de praktijk werken:
De toolnaam moet een actief werkwoord + object zijn. get_sensor_reading is goed. sensor is slecht. data is zeer slecht. Een LLM oriënteert zich op de naam — als twee toolnamen op elkaar lijken, zal de agent ze door elkaar halen, ongeacht de kwaliteit van de beschrijving.
De beschrijving moet verduidelijken wanneer de tool wél en wanneer níet gebruikt moet worden. Het is onvoldoende om "retourneert sensorgegevens" te schrijven. Schrijf: "Roep alleen aan wanneer u live-waarden van een specifiek apparaat op basis van ID nodig heeft. Niet gebruiken voor historische data — gebruik daarvoor get_sensor_history." Expliciete negatie is even belangrijk als een positieve beschrijving.
Elke parameter moet een type, beschrijving en voorbeeld hebben. sensor_id: string is onvoldoende. Schrijf: "Sensor-ID in het formaat PLANT_ZONE_NNN, bijv. A_LINE1_042. Voer nooit alleen een getal in zonder prefix." Het model raadt het formaat niet — u moet het laten zien.
Markeer optionele parameters expliciet — en vermeld de standaardwaarde en wat er gebeurt als u ze weglaat. Zonder dit genereert de agent herhaaldelijk null of veronderstelt een verkeerde waarde.
Voorbeeld van een schema voor en na:
# Voor (typisch slecht schema)
{
"name": "sensor_data",
"description": "Gets sensor data",
"parameters": {
"id": {"type": "string"},
"from": {"type": "string"},
"to": {"type": "string"}
}
}
# Na (productie-schema)
{
"name": "get_sensor_reading",
"description": "Reads current live value from a field sensor.
Use ONLY for real-time values. For historical ranges use get_sensor_history instead.
Returns {value, unit, timestamp, status}. Throws SensorOfflineError if device unreachable.",
"parameters": {
"sensor_id": {
"type": "string",
"description": "Sensor identifier in format PLANT_ZONE_NNN, e.g. A_LINE1_042"
},
"unit": {
"type": "string",
"enum": ["raw", "normalized"],
"description": "Output unit type. Default: 'raw'",
"default": "raw"
}
},
"required": ["sensor_id"]
}Het verschil zit niet in elegantie — het zit in de mate waarin de agent de juiste tool aanroept met geldige argumenten. Uit de praktijk: door de schema's te corrigeren reduceerden we het percentage onjuiste aanroepen met meer dan de helft, zonder enige wijziging aan het model.
Argumentvalidatie: het model niet op zijn woord geloven
Zelfs met een goed schema genereert het model soms argumenten die syntactisch geldig maar semantisch onjuist zijn. from_date: "2026-13-45" is een geldige string, maar een ongeldige datum. count: -5 is een geldig getal, maar heeft geen zinvolle betekenis.
Argumentvalidatie vóór de daadwerkelijke toolaanroep is verplicht, geen optimalisatie.
Drie validatielagen die we aanbevelen:
1. JSON Schema-validatie — de eenvoudigste laag. Verifieer dat de agent het juiste type voor elke parameter heeft meegestuurd. De meeste frameworks (LangGraph, LangChain) doen dit automatisch als u een schema heeft, maar een expliciete laag is zekerder.
2. Domeinvalidatie — verificatie dat de waarden zinvol zijn in uw domeincontext. Datums binnen een redelijk bereik, ID's in het juiste formaat, numerieke waarden binnen een geldig bereik. Dit moet u zelf programmeren — geen enkel generiek framework doet dit voor u.
3. Referentiële integriteit — verificatie dat de gerefereerde entiteit bestaat. sensor_id: "A_LINE1_042" moet u toetsen aan het echte sensorregister — het model kan onmogelijk weten welke ID's geldig zijn.
Wanneer validatie mislukt, gooi dan niet zomaar een uitzondering. Retourneer een gestructureerde foutmelding die het model vertelt *wat* er fout was en *hoe* dat te corrigeren:
# Slecht
raise ValueError("Invalid sensor_id")
# Goed
return {
"error": "INVALID_ARGUMENT",
"parameter": "sensor_id",
"received": "A_LINE_42",
"message": "Format must be PLANT_ZONE_NNN. Did you mean A_LINE1_042?",
"hint": "Call list_sensors() to get valid IDs for this plant"
}Deze foutmelding is de invoer voor de volgende stap van de agent — hoe informatiever, hoe groter de kans dat de agent zichzelf corrigeert zonder onnodige escalatie.
Error handling: toolfouten zijn normaal, geen uitzondering
Een van de grootste conceptuele problemen die we in productie-agenten zien: de code is geschreven voor het happy path. Wanneer een tool een fout retourneert, gedraagt de agent zich onvoorspelbaar — hij blokkeet of gaat verder met een foutieve staat.
De productierealiteit: tools falen. API's hebben uitval. Databases zijn tijdelijk onbereikbaar. SCADA-systemen retourneren een timeout. Dit zijn geen randgevallen — het zijn alledaagse gebeurtenissen in een industriële omgeving.
Basispatroon voor error handling bij elke tool:
def call_tool_safely(tool_fn, args, max_retries=3):
for attempt in range(max_retries):
try:
result = tool_fn(**args)
return {"status": "ok", "data": result}
except TransientError as e:
if attempt == max_retries - 1:
return {
"status": "error",
"type": "TRANSIENT",
"message": str(e),
"hint": "Service temporarily unavailable, retry later"
}
time.sleep(2 ** attempt) # exponential backoff
except PermanentError as e:
return {
"status": "error",
"type": "PERMANENT",
"message": str(e),
"hint": "Do not retry. Escalate to human operator."
}
except Exception as e:
return {
"status": "error",
"type": "UNKNOWN",
"message": str(e),
"hint": "Unexpected error. Log and escalate."
}Kernprincipes:
- Onderscheid tijdelijke vs permanente fouten. Een timeout is tijdelijk (retry). Een autorisatiefout is permanent (niet retryen, escaleer). Het model moet weten wat het vervolgens moet doen.
- Exponentiële backoff — geen vaste pauze. Elke retry wacht 2× langer (1s, 2s, 4s...). Voorkomt overbelasting van systemen bij uitval.
- Maximum aantal retries — altijd begrensd. Zonder limiet kan de agent in een oneindige loop terechtkomen. Uit de praktijk: een retry-rate van circa 12 % is normaal in productiesystemen — als u dat niet beheerst, kunt u het totale aantal aanroepen en de kosten verdubbelen.
- Gestructureerde foutmelding — de agent moet informatie ontvangen over het type fout en de aanbevolen vervolgstap.
Meer over de kostengevolgen van retry-logica leest u in het artikel over de kosten van AI-agenten in productie.
Idempotentie: de vergeten voorwaarde voor betrouwbaarheid
Wanneer een agent een toolaanroep herhaalt, moet het veilig zijn om die meerdere malen met dezelfde argumenten aan te roepen. Is dat niet het geval — dan heeft u een probleem.
Idempotente aanroep: get_sensor_reading(sensor_id="A_LINE1_042") meerdere keren aanroepen retourneert dezelfde (of equivalente) waarde zonder bijwerking. Veilig om te retryen.
Niet-idempotente aanroep: create_work_order(equipment_id="P-042", type="inspection") meerdere keren aanroepen maakt meerdere werkorders aan. Een ramp bij retry.
Regel voor productiesystemen: elke tool die een schrijfoperatie, mutatie of actie met bijwerking uitvoert, moet beschermd zijn tegen dubbele aanroepen.
Beschermingspatronen:
Idempotency key — de agent genereert een unieke sleutel voor elke taak en stuurt die mee met de aanroep. De backend registreert de sleutel en bij een tweede aanroep met dezelfde sleutel wordt het resultaat van de eerste aanroep teruggegeven zonder de actie opnieuw uit te voeren:
def create_work_order(equipment_id, type, idempotency_key):
existing = db.find_by_key(idempotency_key)
if existing:
return existing # Geeft het eerder opgeslagen resultaat terug
result = actually_create_work_order(equipment_id, type)
db.store(idempotency_key, result)
return resultStatuscontrole vóór actie — controleer vóór een schrijfoperatie of de toestand al overeenkomt met het doel. Als er al een werkorder voor apparaat P-042 voor vandaag bestaat, geen nieuwe aanmaken. Dit is eenvoudiger dan een idempotency key en geschikt voor businessdomeinen met een natuurlijke uniciteit.
Expliciete documentatie in het schema — elke tool met een bijwerking moet in de beschrijving vermelden: "Deze actie is onomkeerbaar / maakt een record aan. Roep alleen aan als u zeker weet dat aan de voorwaarden is voldaan." Een model dat deze informatie ontvangt, is voorzichtiger.
Waarom de agent de verkeerde tool aanroept — en hoe u dat vermindert
Een van de raadselachtigere problemen: de agent roept consequent tool_A aan in plaats van tool_B, ook al is tool_B de juiste. Er zijn meerdere oorzaken en elke heeft een andere oplossing.
Te gelijkende namen en beschrijvingen. Als u get_equipment_data en fetch_equipment_info heeft, zal de agent deze tools door elkaar halen. Oplossing: loop uw volledige toolset door en controleer of namen en beschrijvingen eenduidig onderscheidend zijn. Als twee collega's het verschil niet kunnen aangeven zonder de code te lezen, kan het model dat ook niet.
Te veel tools tegelijk. Een LLM heeft beperkte aandachtscapaciteit. Als u een agent 30 tools in één prompt voorschotelt, stijgt de kans op een verkeerde keuze. Oplossingspatroon: toolset routing — de agent kiest eerst een groep tools (bijv. "sensortools" vs. "rapportagetools"), daarna selecteert hij de specifieke tool uit die groep. Dit houdt verband met het MCP-protocol — servers fungeren als natuurlijke toolgroepen.
Ontbrekende context in de prompt. Als de systemprompt de agent niet vertelt in welke context hij werkt, schat het model de juiste tool op basis van de gespreksgeschiedenis. Expliciete context in de systemprompt — "U werkt in het productiebeheerssysteem, niet in het HR-systeem" — vermindert fouten bij toolselectie.
Trainingsbias van het model. Sommige modellen hebben trainingsdata die bepaalde aanroeppatronen begunstigt. Als de agent consequent de verkeerde tool aanroept ondanks een correct schema, probeer dan de toolnaam te wijzigen — soms helpt dat al.
Retry-logica: vijf regels die productie redden
Retry is een onmisbaar onderdeel van betrouwbaar tool calling. Maar slecht geïmplementeerde retry is erger dan helemaal geen retry — het kan een tijdelijke storing omzetten in een cascade-storing.
Regel 1: Retry alleen tijdelijke fouten. HTTP 503 (Service Unavailable) en timeouts zijn tijdelijk. HTTP 400 (Bad Request) en 403 (Forbidden) zijn permanent — retry helpt niet, u verspilt alleen resources.
Regel 2: Exponentiële backoff met jitter. Een vaste pauze (elke seconde retryen) overbelast het systeem bij uitval. Exponentiële backoff met willekeurige jitter (±20 %) spreidt de belasting:
delay = min(base_delay * (2 ** attempt) + random.uniform(0, 0.3), max_delay)Regel 3: Absoluut limiet op pogingen. Drie pogingen zijn doorgaans voldoende. Vijf is het maximum. Meer retries → groter risico op een retry storm (alle agenten retryen tegelijk een uitgevallen systeem).
Regel 4: Circuit breaker bij herhaalde uitval. Als een bepaalde tool herhaaldelijk faalt, hem tijdelijk "ontkoppelen" en alle verdere aanroepen direct als CIRCUIT_OPEN retourneren. Na een afkoelperiode één testerende aanroep doorlaten. Dit beschermt ook uw backendsystemen.
Regel 5: Fallback-actie. Wanneer zowel retry als circuit breaker falen — wat doet de agent dan? Escaleert hij naar een menselijke operator, gaat hij verder zonder die informatie, of stopt hij? Elke kritieke tool moet een gedefinieerde fallbackstrategie hebben. Zonder dat is de agent onvoorspelbaar bij storingen — wat de directe aanleiding is voor HITL-gates.
Tool calling testen vóór productie
De betrouwbaarheid van tool calling kan niet uitsluitend worden geverifieerd door handmatig het happy path te testen. Een aantal testpatronen die we aanbevelen:
Mock toolset testing — maak mockversies van alle tools die verschillende combinaties van antwoorden retourneren: succes, tijdelijke fout, permanente fout, misvormde uitvoer. Start de agent tegen deze mocks en observeer het gedrag.
Boundary testing van argumenten — test handmatig welke argumenten het model genereert voor randgevallen: lege string, null-waarden, extreme getallen, datums in het verleden/de toekomst. Het schema moet deze gevallen expliciet uitsluiten, anders faalt de agent er herhaaldelijk op.
Chaos testing — injecteer met een bepaalde kans (bijv. 20 %) een fout in de tool tijdens het testen. Verifieert of de retry-logica en het fallback-gedrag in de praktijk werken, niet alleen in unittests.
Trace review — voor productiedeployment is observability en tracing onmisbaar. Elke toolaanroep moet worden vastgelegd met argumenten, resultaat, latentie en eventuele fout. Zonder dit weet u bij een productie-incident niet eens waar u moet beginnen zoeken.
Veelgestelde vragen
Hoeveel tools kan een agent tegelijk hebben?
Uit de praktijk: boven de 15–20 tools in één context verslechtert de betrouwbaarheid van de selectie aanzienlijk. De oplossing is toolset routing of MCP-servers — de agent kiest eerst de juiste groep tools, vervolgens de specifieke tool uit die groep. Als u 50+ tools heeft, is hiërarchische navigatie een noodzaak, geen optimalisatie.
Wat te doen als de agent gehallusineerde argumenten genereert — waarden die niet bestaan?
Twee stappen: ten eerste moet het schema expliciet het formaat van geldige waarden tonen inclusief voorbeelden. Ten tweede moet validatie de existentie van gerefereerde entiteiten controleren (bijv. sensor_id aan het register toetsen) en een gestructureerde fout retourneren met een hint hoe geldige waarden te vinden. Aanvullend: een tool list_available_X() (bijv. list_sensors()) helpt de agent zelf geldige ID's op te zoeken zonder hallucinatie.
Is het veilig om een agent onomkeerbare acties automatisch te laten uitvoeren?
Dat hangt af van de context. Voor laagrisico-acties (data lezen, rapporten genereren) is volledige automatisering in orde. Voor onomkeerbare acties (aanmaken van orders, aanpassen van configuraties, verzenden van berichten) bevelen we een expliciete HITL-gate aan — de agent stelt de actie voor, een menselijke operator keurt goed. De EU AI Act vereist vanaf augustus 2026 menselijk toezicht voor hoog-risico AI-systemen — dit impliceert direct HITL voor acties met potentieel ernstige gevolgen.
Wat is de snelste manier om het percentage onjuiste aanroepen te verlagen?
Uit de praktijk heeft het corrigeren van toolschema's het grootste effect — concreet onderscheidende beschrijvingen, voorbeelden van geldige waarden en expliciete negatieve afbakening ("gebruik dit niet voor..."). Het tweede grote effect komt van het verminderen van het aantal tools in één context. Deze twee wijzigingen vereisen geen aanpassing van het model of de architectuur.
Hoe kunt u de kwaliteit van tool calling in productie monitoren?
Leg elke aanroep vast met: timestamp, tool_name, argumenten (zonder PII), status (ok/error), type fout, latentie, attempt number. Monitor de foutrate per tool — als één tool een foutpercentage heeft dat 3× hoger ligt dan het gemiddelde, is dat een signaal om het schema te auditen. Tools als Langfuse (self-hostable) of LangSmith maken dit mogelijk zonder eigen observability-infrastructuur.
Conclusie
*Tool calling is de plek waar de meeste betrouwbaarheidsproblemen van agenten beginnen — en tegelijkertijd de plek waar ze puur op ingenieursniveau oplosbaar zijn, zonder het model te wijzigen. Goed ontworpen schema's, argumentvalidatie, gestructureerde error handling en begrensde retry-logica transformeren een agent van een onvoorspelbaar systeem naar een beheersbaar instrument. Als u een agent naar productie wilt brengen of momenteel betrouwbaarheidsproblemen ondervindt met een bestaand systeem, beoordelen we graag uw specifieke situatie — we ontwerpen de toolschema's, validaties en error handling die passen bij uw omgeving.*
