Tool-loop met tijdslimiet, callbudget, stopreden en menselijke approval voor een externe actie.
Een stopgrens voorkomt niet vanzelf een dubbel effect; die grens werkt samen met policy en idempotency.

Een agent die zelf tools kiest, kan blijven zoeken, herformuleren en opnieuw aanroepen. Dat is soms nuttig, maar zonder grens wordt het een onduidelijke combinatie van kosten, latency en mogelijk dubbele acties. Bij Microsoft Agent Framework hoort een tool-loop daarom een expliciet contract te hebben, niet alleen een goede prompt.

De demo in dit artikel is een patroon, geen uitgevoerde productieproef. Microsoft publiceerde op 10 september 2026 Python-release 1.18.0, met een maximumduur en een stopredensignaal voor de tool-invocation-loop. Dat is precies een goede aanleiding om eerst het eigen ontwerp scherp te krijgen.

Waarom zijn timeout, callbudget en stopreden verschillende controls?

Een timeout begrenst verstreken tijd. Een callbudget begrenst hoeveel tools de agent daadwerkelijk kan gebruiken. Een stopreden verklaart welke grens of beslissing de uitvoering beëindigde. Je hebt alle drie nodig om een incident te kunnen begrijpen.

Control Vangt vooral af Lost niet zelf op
Tijdslimiet Hangen, trage downstreams Een tool die al een write uitvoerde
Callbudget Eindeloos zoeken of herhalen Eén extreem dure toolcall
Stopreden Onderzoek en herstel De onderliggende fout
Approval Risicovolle beslissing Onjuiste autorisatie buiten de agent

De release notes voor 1.18.0 bewijzen dat de framework-loop nu een duurgrens en stopreason kent. Ze bewijzen niet dat twintig seconden of zes calls voor jouw gegevens, tools en gebruikers veilig zijn. Kies die grens vanuit een concrete taak en meet hem.

Wat is een goed loopcontract?

Een goed loopcontract zegt vooraf wat de agent mag doen, wanneer hij stopt en wie beslist als hij niet verder kan. Voor een fictieve support-agent kan dat bijvoorbeeld zijn:

pattern: demo
task: "zoek een status en maak een conceptantwoord"
limits:
  max_duration_seconds: 20
  max_tool_calls: 6
safe_tools: [search_ticket, read_status, draft_reply]
approval_required: [send_reply, change_customer_data]
on_stop:
  timeout: "bewaar concept, meld TIME_BUDGET_EXCEEDED"
  call_budget: "bewaar trace, vraag om menselijke richting"
  approval_pending: "stop zonder volgende toolcall"

De belangrijke stap is de scheiding tussen lezen, ontwerpen en veranderen. Een tool met extern effect — een bericht sturen, een record wijzigen of een betaling starten — krijgt een approval-grens en een idempotency key. De looplimiet is dan geen bescherming waarop je alleen vertrouwt, maar een extra rem.

Stap 1: maak toolrisico zichtbaar vóór je een grens kiest

Classificeer iedere tool op effect. Een zoekactie kan meestal nogmaals. Een write kan onomkeerbaar of zichtbaar voor een klant zijn. Geef niet elke tool dezelfde retryregel.

Tooltype Voorbeeld Herstelpatroon
Read Zoek ticket Mag opnieuw binnen budget
Berekening Maak samenvatting Bewaar versie en invoer-ID
Write Verstuur bericht Approval + idempotency key
High impact Wijzig factuur Menselijke beslissing, geen automatische retry

Dit past bij de bredere aanpak voor AI-agenten in productie: tools zijn systeemgrenzen, geen verlengstuk van een prompt.

Stap 2: leg een stopreden vast die iemand kan gebruiken

Gebruik een kleine, stabiele set stopredenen. Vermijd logregels als “iets ging fout”; die zijn niet te groeperen in een dashboard of test.

type StopReason =
  | "COMPLETED"
  | "TIME_BUDGET_EXCEEDED"
  | "TOOL_CALL_BUDGET_EXCEEDED"
  | "APPROVAL_PENDING"
  | "TOOL_FAILURE"
  | "POLICY_DENIED";

function stop(reason: StopReason, runId: string) {
  audit.write({ runId, reason, at: new Date().toISOString() });
  return { status: "stopped", reason };
}

Het is pseudocode. De framework-API en precieze propertynamen kunnen wijzigen; controleer die altijd tegen de versie die je installeert. Het patroon blijft: zet de limiet buiten de prompt en schrijf het besluit als observatiegegeven.

Stap 3: behandel een timeout niet als een transactiegrens

Stel dat send_reply net een HTTP-request verstuurde toen de tijdslimiet inging. De client weet misschien niet of de server het bericht accepteerde. Nogmaals uitvoeren kan twee berichten opleveren.

Gebruik daarom een downstream-contract:

await mailer.send({
  messageId: `reply:${ticketId}:${draftVersion}`,
  idempotencyKey: `reply:${ticketId}:${draftVersion}`,
  requiresApproval: true,
});

Als de downstream-service geen idempotency ondersteunt, laat de recovery dan niet gokken. Bewaar de onzekere status, toon hem aan een bevoegde persoon en vraag om een expliciet vervolg. Voor complexere routes met state, nodes en approvalpunten biedt graph engineering een bruikbaar mentaal model.

Stap 4: test de stopgrens als een releasegate

Een limiet die alleen op papier bestaat, is geen control. Voeg minstens deze negatieve tests toe:

Test Verwachting
Zevende toolcall bij budget zes Stopreason TOOL_CALL_BUDGET_EXCEEDED; geen zevende call
Trage tool over tijdslimiet Stopreason TIME_BUDGET_EXCEEDED; run is terugvindbaar
Write zonder approval Stopreason APPROVAL_PENDING of POLICY_DENIED; geen write
Herstart na onzekere write Geen tweede effect zonder idempotencybevestiging
Ongeldige tooloutput Stopreason TOOL_FAILURE; geen stilzwijgende vervolgcall

Log minimaal run-ID, toolnaam, aantal calls, duur, stopreden, policybesluit en een referentie naar de approval. Log niet standaard volledige prompts, secrets of klantinhoud. Die dataminimalisatie maakt observability bruikbaarder én veiliger.

Wanneer kies je een ander patroon?

Kies een gewone queueworker voor korte, idempotente batches. Kies een workflow-engine als je fan-out, durable timers of compensaties nodig hebt. Kies geen zelfstandige agentloop voor een stap die juridisch, financieel of operationeel menselijke verantwoordelijkheid vraagt.

De kern is eenvoudig: een agentloop is pas beheersbaar als tijd, aantal calls, stopreden en herstel samen ontworpen zijn. Wil je die grens voor een bestaand systeem uitwerken, dan kun je een freelance AI architect inschakelen voor de keuze tussen agent, workflow en klassieke integratie.

Bronnen

  1. Microsoft Agent Framework, Python release 1.18.0, gepubliceerd 10 september 2026, geraadpleegd 17 september 2026. Beschrijft maximumduur en stopredensignaal voor de tool-loop; geeft geen universele veilige grenswaarden.
  2. Microsoft Agent Framework, repository, geraadpleegd 17 september 2026. Beschrijft project- en integratiescope; bewijst geen veilige lokale configuratie.
  3. Microsoft Agent Framework, AgentLoopMiddleware-documentatie, geraadpleegd 17 september 2026. Beschrijft begrensde looping en de approval escape hatch; bron en API kunnen wijzigen.