Een AI-agent die echt doet wat hij moet.

Client

EU SaaS · geanonimiseerd

Industry

AI-agents · LangGraph

Engagement

90 dagen

Crafts

LangGraph · Tool-use · Evals · Salesforce

bunduk · support-agent · production
live

De briefing.

Tweehonderd support-tickets per week. De meeste over facturatie: dubbele afschrijvingen, terugbetalingen, adreswijzigingen, het gebruikelijke werk. Het support-team verdronk, het model kostte al meer dan het opleverde, en demo-bare AI stond bruikbare AI in de weg.

De briefing: bouw de kleinste agent die op betrouwbare wijze één benoemde klasse tickets afhandelt, terugbetalingen, met een eval set die je aan de auditor kunt laten zien, observability die je aan de CFO kunt laten zien, en een handoff-pad dat de gebruiker respecteert.

§ Architecture

A graph of five typed nodes.

Each node is its own function. Each transition is a typed contract. Failures route to a human, not the user. The whole thing fits on one page.

Inbound ticket
intake · queue
classify
intent · entities
fetch_context
salesforce · billing · KB
check_policy
rules · thresholds
resolve
tool-use · stripe / refund
handoff_human
if conf < 0.78
audit_log
tokens · cost · eval
resolved
reply + receipt
i/o node human hand-off

Wat we opleverden.

Een getypeerde LangGraph-agent met vijf knooppunten, drie tools, en een handoff-knooppunt dat weet wanneer hij níet moet handelen. Het model is klein waar het kan (classify op gpt-4o-mini) en groot alleen waar het moet (de resolve-stap). Elke transitie is een getypeerd contract. Een knooppunt retourneert een gestructureerd object, geen vrije tekst, zodat het volgende knooppunt precies weet wat het ziet.

Het hele ding past op één pagina code. We laten het zien.

agents/support_agent.py

LangGraph · 0.2.34
1from langgraph.graph import StateGraph, END
2from .tools import classify, fetch_order, check_policy, issue_refund, handoff
3
4# five nodes, fully typed
5graph = StateGraph(TicketState)
6graph.add_node("classify", classify)
7graph.add_node("fetch_context", fetch_order)
8graph.add_node("check_policy", check_policy)
9graph.add_node("resolve", issue_refund)
10graph.add_conditional_edges(
11  "check_policy", route_by_confidence,
12  {"high": "resolve", "low": "handoff"}
13)
14graph.add_node("handoff", handoff)
15app = graph.compile()  # ready in <120ms
ticket #84321 · refund · audit log
log
01# inbound ticket — Oct 14, 09:42 CET
02subject "Double charge on October invoice?"
03customer ord_84321 · plan_pro_annual
04
05classify intent=billing.refund_request · conf=0.94
06fetch_order ch_3OkW... €218.00 · 2 charges · Δt 4s
07check_policy rule=duplicate_charge · auto-refund: allowed
08resolve stripe.refund(ch_3OkW...) → €218.00 · ETA 3-5d
09handoff drafted reply · routed FYI → anna@support
10audit_log tokens=4205 · cost=€0.018 · latency=11.4s
11
12[done] ticket resolved · human-touched: 0 · sla: green
§ The eval set

A test suite for prompts.

Every change runs the eval set in CI. Regressions don't ship. The pass rate is the only metric we actually trust.

184
eval cases
real tickets · hand-labelled
96.2%
pass rate
last 30 days · ci-gated
1.9s
p95 latency
end-to-end · classify→resolve
€0.018
cost / turn
rolling 7-day average

We kunnen nu eindelijk naar de agent wijzen in een bestuursvergadering en de cijfers laten zien.

Engineering lead, EU klant
§ Prompt versions

Ninety days, seven shipped versions.

  1. v1.0Day 1First shipped prompt. 71% pass.
  2. v1.1Day 6Added Dutch + EN templates for tone.+9.0%
  3. v1.2Day 14Re-rank with reciprocal rank fusion.+5.4%
  4. v1.3Day 22Lowered hand-off threshold to 0.78.+2.1%
  5. v2.0Day 41Switched classify to typed function-calls.+4.8%
  6. v2.1Day 67Cached fetch_order for hot SKUs.p95 → 1.9s
  7. v2.2Day 90Added cost ceiling alerter (Slack #ops).ops
§ Where the budget goes

Most of the cost is in resolve.

Six steps. One of them does most of the thinking. Classify and check_policy stay cheap on purpose. We want the heavy model to only see context it needs.

  • classify18%small model · gpt-4o-mini
  • fetch_context12%vector search + RAG
  • check_policy9%function call · no llm
  • resolve47%drafting · main model
  • handoff6%summary for human
  • audit_log8%tags · cost · trace

Het resultaat.

Tweehonderd tickets per week, nul hallucinaties in negentig dagen. De mediaan-oplostijd op refund-tickets ging van elf minuten (menselijk) naar elf seconden (agent + audit). Het support-team handelt weer de gesprekken af die ook echt een mens nodig hebben.

Het belangrijkste: de eval set is het contract. Elke prompt-wijziging is een PR met een CI-run. Regressies komen niet in productie. Het team heeft een echt, meetbaar artefact om naar te wijzen wanneer de vraag opduikt: “werkt de AI eigenlijk?” Het antwoord staat op het dashboard.

200/wk
Tickets volledig afgehandeld door de agent
0
Hallucinaties in 90 dagen productie
11s
Mediaan-oplostijd, refund-tickets
€0.018
Kosten per opgelost ticket (rolling 7-dagen)