Email automation
Email-agent bij een expediteur: 3u40 naar 22 minuten
De ops-desk opende vroeger Outlook om 06:45 en keek pas weer op rond lunchtijd. 4.300 wekelijkse Portbase-meldingen stemmen zichzelf nu af terwijl de waterkoker fluit.

Het is 06:45 op een dinsdag in Breda. De ops-lead schenkt koffie in, opent haar laptop en kijkt toe hoe de waterkoker opnieuw kookt voordat de inbox klaar is met laden. Tussen 18:00 gisteravond en 06:00 vanochtend duwde Portbase 612 aankomstmeldingen in de gedeelde arrivals@-mailbox. CargoWise One druppelde er nog eens 480 statusupdates bij. Ergens in die stapel zitten zes containers die voor 09:00 een douaneagent aan de lijn nodig hebben, drie die op een kade staan met de verkeerde release-referentie, en één die de terminal al kwijt is.
Dit is de ochtendlijke manifestscan. Tot vorig november kostte die 3 uur en 40 minuten, elke werkdag, ook de dagen waarop zij de enige op kantoor was.
De ochtendlijke manifestscan
Breda ligt veertig minuten van de Maasvlakte. Onze klant, een expediteur uit Breda met 29 mensen die we hier niet bij naam noemen, doet zee, lucht en volledige containerlading voor industriële verladers in de Benelux. Hun ruggengraat is CargoWise One, het TMS van WiseTech Global dat in stilte ongeveer een derde van het Europese mid-market draait. Hun ogen en oren bij de haven komen van Portbase, het Port Community System van Rotterdam dat aankomst-, aangifte- en releaseberichten van terminals, douane en rederijen bundelt.
De twee systemen praten niet met elkaar. Portbase mailt. CargoWise heeft zijn eDoc-inbox en een eAdaptor-API, maar het formaatverschil betekent dat de meeste operators referentienummers met de hand overtikken. Voor het team in Breda was dat vijf stappen per melding:
- de Portbase-aankomstmelding openen
- het bill of lading of de air waybill opzoeken
- in CargoWise zoeken naar de bijbehorende zending
- ETA, schip, containernummer en douanestatus verifiëren
- alles wat afwijkt markeren voor het douanegesprek van 09:00
612 mails in één nacht, vijf stappen per stuk, twaalf seconden per stap op goede dagen. De rekensom is somber. De rekensom is ook misleidend, want slechte dagen zijn niet gelijk verdeeld: maandagochtend draagt 60% van de weekendbacklog en de eerste dinsdag van de maand draagt de douanecorrecties van de vorige maand.
Waar Portbase en CargoWise van elkaar verschillen
Als de twee feeds altijd overeen zouden komen, was de agent een opgevoerde rule engine. Dat is niet zo. Drie patronen van onenigheid komen telkens terug.
Verouderde ETA aan de carrier-kant. Portbase krijgt de werkelijke afmeertijd binnen enkele minuten van het terminal operating system. CargoWise erft de ETA van de booking van de rederij, en die loopt soms 6 tot 18 uur achter. Als de douaneagent om 08:50 een CargoWise-scherm opent, krijgt hij het oude nummer, stuurt hij de verkeerde slot naar de vervoerder en kost dat een demurrage-uur aan de terminal gate.
Vrijgegeven versus release-pending. De douane zet een vrijgave in AGS, het Nederlandse aangiftesysteem, en Portbase spiegelt dat binnen minuten als een release-bericht. CargoWise ziet pas een douanestatus zodra de douaneagent de zending handmatig doorzet. Er is een venster, meestal zo'n 45 minuten, waarin Portbase "vrijgegeven" zegt en CargoWise nog "vastgehouden" toont. Een planner die te goeder trouw CargoWise leest, vertelt de klant dat de container vaststaat terwijl die allang vrij is.
Spookcontainers. Als een container wordt overgestouwd in Rotterdam (wat vaker gebeurt dan rederijen willen toegeven), stuurt Portbase een nieuwe aankomstmelding onder hetzelfde bill of lading maar met een ander visit reference. CargoWise ziet dit als een dubbele zending, tenzij iemand het met de hand reconcilieert. Tegen het eind van de maand betaalt de expediteur dubbel voor facturen die niemand goed kan uitleggen.
Elk van deze drie is een beslissing van vijf minuten voor een senior operator en een paniek van veertig minuten voor een junior. Het punt van de agent is niet om de beslissing te nemen. Het is om de onenigheid zichtbaar te maken, het bewijs erbij te plakken en de juiste zending op het juiste moment voor de juiste mens te leggen.
De vorm van de agent
We bouwden dit als een single-purpose email-agent, geen algemene assistent. Hij heeft één inbox, drie tools en een strikt outputschema. Het geheel draait op een kleine VPS in Frankfurt met een Postgres-database voor state. De pseudocode is onspectaculair:
async def handle_arrival(email):
parsed = parse_portbase_xml(email.body)
if not parsed.bill_of_lading:
return queue_for_human(email, reason="no_bl")
cw = await cargowise.find_shipment(
bl=parsed.bill_of_lading,
container=parsed.container_number,
)
if cw is None:
return queue_for_human(email, reason="no_match")
disagreements = compare(parsed, cw)
if not disagreements:
await cargowise.append_note(cw.id, parsed.summary())
return
severity = classify(disagreements)
user = route_to_user(cw, severity)
await draft_brief(user, parsed, cw, disagreements)
Niets exotisch. De reden dat de agent werkt is niet de modelkeuze, het is het schema. De Portbase-XML-envelop is well-formed en stabiel; de CargoWise eAdaptor-objecten zijn gedocumenteerd. Zodra je je aan die twee contracten verbindt, doet het taalmodel alleen waar het goed in is: ernst classificeren, de onenigheid in twee zinnen samenvatten en het korte stukje schrijven dat in de ochtendwachtrij van de operator belandt.
Het model beslist nooit of een container wordt vrijgegeven. Het schrijft nooit terug naar CargoWise behalve als append-only-notitie op een zending die al bestaat. Het mailt nooit de klant. Dat plafond is niet onderhandelbaar, en de systeemtests falen luid als een van die oppervlakken in de tool-lijst van de agent opduikt.
AEO-gating en het probleem met douanereferenties
Dit is het deel van de build waar we het meest trots op zijn, en het raakt een vraag die elke operationele AI-build uiteindelijk moet beantwoorden: waar trek je de grens tussen wat het model mag overdenken en wat het überhaupt niet mag zien? Een model dat je bestandssysteem kan lezen, kan erop schrijven. Een model dat klantgegevens kan zien, kan ze lekken. De enige guardrail die het bouwen waard is, is er één waar het model niet omheen kan redeneren.
Expediteurs leven hiermee. Iedereen met AEO-status (Authorised Economic Operator, het trusted-trader-programma van de EU) draagt een stille operationele plicht: douane-identificatoren zoals MRN-nummers, AGS-aangifte-ID's en BE/IM-nummers mogen niet zichtbaar zijn voor medewerkers buiten de geautoriseerde kring. Een junior planner die drie weken in dienst is, hoort nooit een MRN in haar inbox te zien, ook al zou de agent het technisch kunnen ophalen. De AEO-audit gebeurt eens in de drie jaar en de auditor leest inbox-steekproeven.
Het patroon dat we kozen is bewust dom. Douane-identificatoren worden bij het parsen getagd, uit de payload gestript en in een aparte Postgres-tabel bewaard, gekoppeld aan het shipment-ID. De drafting-prompt krijgt ze nooit te zien. De brief van de agent verwijst naar een zending via het CargoWise house bill-nummer; als de ontvanger het MRN nodig heeft, klikt die door naar CargoWise, waar de bestaande role-based access control bepaalt of het getoond wordt. Het model kan letterlijk niet lekken wat het nooit gezien heeft.
Als een gereguleerd veld het context window van het model kan bereiken, ga er dan vanuit dat het uiteindelijk in een concept terechtkomt. Strip bij het parsen, niet bij de output. Outputfilters zijn een struikeldraad, geen hek.
Dit klinkt streng en dat is het. We hebben een redactie-stap aan de outputkant overwogen. Die had twee weken eerder live kunnen zijn. Hij had ook gefaald de eerste keer dat een prompt-injection binnenkwam, verpakt als Portbase-melding, wat op een publiek port community system een kwestie van wanneer is, niet of. De output-filter-versie van deze agent zit één slim geformuleerd kwaadaardig XML-veld verwijderd van stilletjes het douaneprofiel van je klant naar een concurrent mailen.
Van 3u40 naar 22 minuten
Het kopgetal is eerlijk. We hebben het venster van de ochtendscan gemeten tussen oktober 2025 (handmatige nulmeting, 21 werkdagen) en april 2026 (agent live, drie maanden ingespeeld, 22 werkdagen). De mediane tijd van "06:30 inbox openen" tot "ops-lead heeft de wachtrij leeg" ging van 220 minuten naar 22 minuten. De variantie viel ook in: de handmatige nulmeting had een standaarddeviatie van 47 minuten, de agent-run heeft 6.
Wat het kopgetal verbergt is interessanter. Die 22 minuten zijn vrijwel volledig de onenigheids-wachtrij. De agent markeert op dit moment tussen de 18 en 31 zendingen per ochtend. De lead heeft per stuk 30 tot 90 seconden nodig om die op te lossen, omdat de brief al de Portbase-samenvatting, de CargoWise-status en een classificatie van één regel bevat over welk van de drie onenigheidspatronen van toepassing is. Ze leest de mail niet meer, ze keurt een beslissing goed.
De resterende circa 580 meldingen per ochtend die schoon matchen, bereiken nooit een mens. Ze worden append-only notities op de bijbehorende CargoWise-zending, met tijdstempel en traceerbaar. Als er stroomafwaarts iets misgaat, is het audit trail er, en de AEO-auditor ziet een schone keten van verantwoording.
Het deel dat ons verraste: de junior planner die de inbox triage deed, besteedt haar eerste anderhalf uur nu aan klantgesprekken. De ops-director schat dit op ruwweg één volledige FTE aan teruggewonnen capaciteit. De running cost van de agent, inclusief de modelrekening, de VPS en de e-mailinfrastructuur, zit onder de €180 per maand. In Q1 zijn er twee nieuwe verladers aangenomen zonder een nieuwe medewerker.
Wat dit niet is
Het is geen algemene assistent. De ops-desk kan er geen vragen aan stellen. Hij vat de dag niet samen, schrijft geen klantupdates en rankt geen zendingen op marge. Hij doet één klus, op één mailbox, met één beschrijfbaar oppervlak (het notitieveld in CargoWise) en één strak geschemd output. Elke keer dat we in de verleiding kwamen om zijn scope te verbreden, hebben we het teruggedraaid. De betrouwbaarheid van het systeem is precies de betrouwbaarheid van zijn kleinste contract.
Hij is niet autonoom in enige betekenisvolle zin. Het model verplaatst nooit een container, autoriseert nooit een douanevrijgave, stuurt nooit een uitgaande mail. De brief ligt voor een mens totdat de mens ernaar handelt.
En hij is niet af. Het volgende dat we willen toevoegen is voyage-level reconciliatie: als een schip wordt overgestouwd en Portbase meldingen heropent onder nieuwe visit references, moet de agent de lineage consolideren en één bijgewerkte zending tonen in plaats van drie verwarde. Dat werk staat gepland voor Q3 2026. We schrijven er pas over zodra het live is.
Een versie die je deze week kunt uitproberen
Toen we de email-agent bouwden voor de Bredase expediteur, liepen we er telkens tegenaan dat zowel de goedkope route (laat het model alles beslissen) als de veilige route (laat het model niks beslissen) faalt. We hebben dat opgelost door de grens bij de datalaag te trekken in plaats van bij de outputlaag; dat werk valt bij ABN onder AI-agents.
Het kleinste wat je deze week kunt doen: open de laatste zeven dagen van je operationele inbox met het grootste volume, tel het aantal verschillende afzenderdomeinen en label elke mail als "schoon gematcht" of "had een mens nodig". De verhouding tussen die twee getallen is het enige cijfer dat je nodig hebt om te beslissen of een email-agent het bouwen waard is.
Kern
De betrouwbaarheid van een email-agent is gelijk aan de betrouwbaarheid van zijn kleinste contract, niet de slimheid van zijn model.
FAQ
Waarom niet gewoon de eigen Portbase-integratie van CargoWise One gebruiken?
Die bestaat, maar de format mapping die hij meelevert dekt alleen de simpelste vorm van aankomstmelding. Restows, gedeeltelijke vrijgaven en gecorrigeerde MRN's vallen nog steeds terug op handmatige verwerking, en daar komt de ochtendbacklog precies vandaan.
Schrijft de agent ooit terug naar CargoWise One?
Alleen als append-only notitie op zendingen die al bestaan. Hij maakt nooit een zending aan, wijzigt nooit financiële velden en verandert nooit de douanestatus. De CargoWise-rol die voor de integratie wordt gebruikt is read-only op elk veld behalve die notitie.
Past deze aanpak bij AEO-compliance?
Ja, omdat douane-identificatoren nooit in het context window van het model komen. Ze worden bij het parsen gestript, apart opgeslagen en alleen toegankelijk via de bestaande role-based access control van CargoWise. De agent heeft niets om te lekken.
Wat gebeurt er als de agent geen CargoWise-match kan vinden voor een Portbase-melding?
Dan zet hij de mail in de wachtrij voor een mens, met de reden getagd (no_match, no_bl, ambiguous_bl). De lead ruimt die als eerste op, want dat zijn meestal de zendingen waar het release-venster verloopt.