Voice agents
Voice agents in havenlogistiek: een Rotterdamse case study
Een Rotterdamse havendienstverlener verbrandde vier fte op douane-callbacks. Een drietalige voice agent vangt nu 920 calls per week op, zonder BSN op disk.

Dinsdag 06:40 in Rotterdam. De dispatchruimte zit één verdieping boven de truckyard. Drie telefoonlijnen staan al rood als de waterkoker klikt. Twee zijn Duitse douaneagenten met vragen over CMR-nummers uit de lading van gisteren; één is een Poolse chauffeur die nog op Maasvlakte II staat en wil weten welke lane voor hem is. De dispatcher is nog niet eens gaan zitten.
Dit was het belpatroon dat we vonden toen de operator zijn boeken voor ons opende. Een havendienstverlener van 44 mensen in Rotterdam, actief in containerexpeditie en douane-afhandeling, verbrandde ongeveer vier fulltime bureaus op inkomende callbacks. Douaneagenten, chauffeurs en terminaloperators wilden steeds dezelfde drie dingen: een CMR-status, een bevestigde lane en een ETA voor de container. Negentig procent van die calls had al een deterministisch antwoord in de Cargonaut-feed van de operator staan. De mensen waren een doorgeefluik.
Negentig dagen geleden hebben we een voice agent live gezet die deze calls afhandelt in Nederlands, Duits en Engels. Hij verwerkt nu ongeveer 920 callbacks per week, escaleert vier procent naar een mens en schrijft geen enkele BSN naar disk. Zo zag het bouwwerk er werkelijk uit.
Het werk dat vier bureaus vulde
De intakemix was scheef. Als je de call logs van de afgelopen zes maanden bekeek:
- Ongeveer 58 procent van de inkomende calls waren CMR-statusvragen: waar is zending NL12345678, is hij geklaard, is hij vrijgegeven voor pickup.
- Ongeveer 23 procent waren lane- en gatevragen: welk Maasvlakte-schap, welke RTM-terminal, welk slotvenster.
- Ongeveer 12 procent waren ETA-vragen: de douaneagent in Bremen wil weten wanneer we hem fysiek binnen hebben.
- De resterende zeven procent was de long tail: ADR-papierwerk, T1-transitvragen, facturatiegeschillen en af en toe een mens die een mens nodig had.
Taalverdeling, gewogen naar belvolume: Nederlands 60, Duits 25, Engels 15. De Duitse bellers waren bijna allemaal douaneagenten uit de corridor Hamburg, Bremen en de Rijn. De Engelstaligen waren een mix van Britse exporteurs en rederij-desks in Singapore en Dubai. De Nederlandse bellers waren al de rest: chauffeurs, intern personeel, Belgische expediteurs en omliggende operators.
Al die CMR-, lane- en ETA-antwoorden stonden al in de Cargonaut-feed van de operator. Cargonaut is de IT-ruggengraat van de Rotterdamse havencommunity: het draagt de douanestatus, terminalevents en voormeldingen die een container van kade naar truck verplaatsen. De data was er. De bottleneck was dat een mens vierhonderd keer per dag een scherm hardop moest voorlezen, in drie talen.
Waarom voice en niet een chat agent
In de eerste scopingcall duwden we hard op een chat-first aanpak. Verkeerd instinct. De bellers zaten niet aan een bureau. Ze zaten in truckcabines, bij terminalgates, op het platform van een heftruck met één oortje in. Ze belden omdat het alternatief was om langs de kant te gaan en te typen. Mail was al een optie; het volume op de inkomende lijn vertelde ons waarom niemand het gebruikte.
Latency was de andere reden. Een douaneagent die jaagt op een releasewindow heeft dertig seconden voordat de terminal verder gaat. Een antwoord als 'we mailen je de status binnen vijf minuten', hoe beleefd ook, kost de operator een laneslot. Voice was het enige kanaal dat antwoorden snel genoeg teruggaf om te tellen.
Architectuur, in gewone taal
De agent zit achter de bestaande SIP-trunk van de operator. Inkomende calls komen binnen op de PBX zoals altijd; we hebben een nieuwe dial-plan regel toegevoegd die elke call van een bekend agentnummer (gematcht tegen het CRM) doorzet naar het SIP-endpoint van de agent in plaats van naar de menselijke wachtrij. Alles wat geen match heeft, komt nog steeds eerst bij een mens, en die mens kan doorzetten naar de agent als de vraag toch routine blijkt.
De agent zelf is een dunne orchestratielaag rond drie tools: een Cargonaut-lookup, een CRM-lookup en een gestructureerde overdracht naar de menselijke wachtrij. Het model doet taaldetectie, intent parsing en de readback. Alles wat state bevat, zit buiten het model.
Zo ziet de Cargonaut-tooldefinitie er ongeveer uit, licht geanonimiseerd:
const tools = [
{
name: "cmr_status",
description:
"Fetch the current customs and terminal status for a consignment by CMR number.",
input_schema: {
type: "object",
properties: {
cmr_number: {
type: "string",
pattern: "^[A-Z]{2}\\d{8}$",
description: "ISO country prefix plus 8 digits, e.g. NL12345678",
},
caller_msisdn: {
type: "string",
description:
"E.164 caller number, scopes the lookup to the caller's dossier.",
},
},
required: ["cmr_number", "caller_msisdn"],
},
},
];
Het pattern doet ertoe. Een regex op het CMR-nummer betekent dat het model geen zendings-ID kan verzinnen. Als de douaneagent het prefix onduidelijk uitspreekt, leest de voice agent het terug en wacht op bevestiging voordat hij de tool call doet. Dat hebben we op de dure manier geleerd: het eerste prototype verzon vol vertrouwen een NL-nummer dat niet bestond, en de douaneagent hing halverwege de readback op.
BSN buiten disk houden
Nederlandse privacyregels behandelen het BSN als bijzonder gevoelige identifier. Je mag het niet zomaar opslaan, niet doorgeven aan een externe verwerker zonder expliciete grondslag, en de Autoriteit Persoonsgegevens heeft partijen beboet die het mis hadden. Douaneagenten dragen aan de telefoon echter soms het BSN van een chauffeur voor om identiteit aan de gate te verifiëren. We moesten aannemen dat het in de audiostream zou belanden en daarop ontwerpen.
De pipeline doet drie dingen om BSN buiten disk te houden:
- De transcriptiestream loopt door een redactor voordat er iets gelogd wordt. Een numerieke token van negen cijfers die voldoet aan de BSN-checksum (de 11-proef) wordt in het transcript vervangen door
[BSN_REDACTED]voordat de regel wordt weggeschreven. - De ruwe audio blijft de hele call in een vluchtige in-memory buffer staan en wordt daarna weggegooid. Verder wordt niets bewaard dan het geredacteerde transcript en de gestructureerde tool calls.
- De modelleverancier is contractueel gebonden aan een zero-retention setup. Geen prompt logging, geen completion logging, geen training op de data. Dat was de doorslaggevende afweging voor de leverancierskeuze.
Recente bewegingen van leveranciers richting verplichte 30 dagen dataretentie breken dit ontwerp volledig. Werk je onder de AVG en raakt je callflow een BSN, IBAN of douaneaangifte, lees dan de retentieclausule vóór je de prijspagina leest.
Dit is niet theoretisch. Er is een gestaag patroon van modelleveranciers die stilletjes hun retentiebeleid aanscherpen voor misbruikmonitoring; sommige eisen nu een venster van 30 dagen, ook voor betalende API-klanten. Voor een interne coding assistant is dat prima. Voor een Nederlandse haven-operator wiens callaudio een BSN kan bevatten, is het een no-go. Twee van de eerste drie scopingweken zijn opgegaan aan het schriftelijk uitonderhandelen van zero-retention voorwaarden.
Wat brak in week één
De eerste week live-calls leverde een lijst aan failure modes op die in geen van onze testscripts opdoken.
Duitse prosodie
Duitse bellers bouwen lange zinnen met het werkwoord aan het einde. 'Ich rufe Sie an wegen der Containerlieferung die ich gestern angemeldet habe und ihr System sagt der Container ist noch im Terminal.' Het model wilde bij de komma al inbreken. We hebben de silence threshold voor de-DE 600 milliseconden omhoog gezet en daarmee het barge-in gestopt. De Nederlandse en Engelse thresholds bleven staan.
Havenjargon
Rotterdamse dispatchers gebruiken vocabulaire dat het model niet met volle zekerheid kende. 'Loods' kan zowel een loodsgebouw als een scheepvaartloods betekenen, afhankelijk van de context. 'Het schap' is een aanlegplek. 'De los' is het lossingsslot. We bouwden een kleine in-context glossary, geïnjecteerd op systeem-prompt niveau voor calls die als haven-domein waren gemarkeerd, en dat fixte het grootste deel. Fine-tunen hebben we niet gedaan. Een glossary tijdens runtime was goedkoper en makkelijker te auditen.
Te zelfverzekerde agent
Dit was de pijnlijke. Op dag drie vertelde de agent een douaneagent dat een zending was vrijgegeven voor pickup, terwijl die in werkelijkheid alleen door de douane was; de terminal had nog geen groen licht gegeven. De douaneagent stuurde een truck voor niks. We hebben de tool-response parser aangescherpt, zodat de agent nu customs_cleared en terminal_released als aparte states behandelt en weigert ze samen te plakken. Daarnaast hebben we de eerste honderd calls per dag de daaropvolgende twee weken een supervisor pass gegeven, wat nog twee twijfelgevallen ving voordat die een klant bereikten.
Heb je de Hacker News-thread van deze week gevolgd over een coding agent die in een Fedora-setup ongerelateerde bestanden begon te verwijderen, dan is de les dezelfde als die wij op dag drie leerden. Een autonome agent in een operationele loop heeft een strak, smal getypeerd contract met de wereld nodig én een mens die betaald wordt om de eerste honderd outputs te lezen. De schade was bij ons een verspilde trucksrit. In andere settings is het erger geweest.
Cijfers na negentig dagen
De operator heeft de agent inmiddels iets langer dan drie maanden in productie. De cijfers, peildatum vorige week:
- 920 inkomende calls per week volledig end-to-end afgehandeld zonder menselijke tussenkomst, op een gemiddelde van 1.020 totaal inkomende calls per week.
- 4,2 procent van de calls geëscaleerd naar een mens, vooral ADR-papierwerk en facturatiegeschillen.
- Mediaan time-to-answer daalde van 23 minuten (de oude voicemail-en-terugbellen loop) naar 11 seconden.
- Twee van de vier douane-callback bureaus zijn herverdeeld naar actief expeditie- en douaneagentwerk. De andere twee dekken escalaties en nachtdiensten.
- Nul BSN-waarden weggeschreven naar enige persistente store, wekelijks geverifieerd met een grep over het transcriptarchief tegen de BSN-checksum.
De kostenstructuur is eerlijk: de agent is niet gratis, de SIP-minuten zijn niet gratis en het integratiewerk was een traject van zes weken. Terugverdientijd kwam in maand drie, sneller dan onze prognose en langzamer dan de salespitch zou hebben beloofd.
Wat we anders zouden doen
Twee dingen, achteraf gezien.
We hebben de IVR-fallback overdreven. We bouwden een zorgvuldig drielaags menu voor bellers die een mens wilden, en het bleek dat één optie ('druk 9 om iemand te spreken') genoeg was geweest. De 4 procent bellers die escaleert doet dat omdat de agent het zegt, niet omdat ze tegen het menu vochten.
We hebben de after-hours routing voor Duitsland onderschat. Duitse douaneagenten bellen om 06:30 Rotterdamse tijd, wat ook 06:30 in Hamburg is, en wij hadden de agent om 07:00 live laten gaan in lijn met de rest van het dienstrooster van de operator. Dat kostte ons drie weken aan geïrriteerde mails van douaneagenten voor we het doorhadden. De fix was één cron-regel.
De audit van vijf minuten die je vandaag kunt draaien
Draai je een operatie met veel callbacks, dan kun je de case voor een voice agent toetsen zonder één regel code te schrijven. Trek de call log van afgelopen week. Tag elke call met de vraag die gesteld werd, niet met de beller. Tel de top drie van vragen op. Vormt die top drie meer dan de helft van je volume, en zitten de antwoorden allemaal in een systeem dat je zelf al draait, dan heb je een probleem in voice-agent vorm.
Toen we deze voice agent voor de Rotterdamse operator bouwden, struikelden we steeds over het BSN-on-the-wire probleem: de redactie moest stroomopwaarts gebeuren van elke logregel, elk audit trail, elke observability hook. Uiteindelijk losten we het op door de redactor in hetzelfde proces als de transcriptiestream te draaien, nog voordat de regel het geheugen verliet. Dat soort werk doen we binnen onze praktijk rond AI-agents, en dat is meestal het stuk dat de demo's overslaan.
Kern
Voice agents betalen zich terug als bellers één vrije hand hebben, dertig seconden geduld, en drie herhaalbare vragen in een systeem dat je zelf al draait.
FAQ
Waarom een voice agent in plaats van een chat agent voor douane-callbacks?
Douaneagenten en chauffeurs bellen vanuit cabines, gates en kadeplatforms met één vrije hand. Mail hadden ze al en gebruikten ze niet. Voice was het enige kanaal dat antwoorden snel genoeg teruggaf om een laneslot vast te houden.
Hoe houdt de agent het Nederlandse BSN buiten disk?
Inline-redactie binnen het transcriptieproces vervangt elke negencijferige token die de BSN 11-proef doorstaat met een placeholder vóór logging. De audio staat in een vluchtige buffer en wordt na de call weggegooid. Het leverancierscontract is zero-retention.
Welk percentage calls gaat nog naar een mens?
Ongeveer 4,2 procent over de eerste 90 dagen. De meeste escalaties zijn ADR-papierwerk, T1-transit edge cases en facturatiegeschillen. Statusvragen, lane-bevestigingen en ETA-vragen worden end-to-end afgehandeld.
Integreert de agent met Cargonaut- en Portbase-data?
Ja. Hij roept een dunne Cargonaut-lookup tool aan die douane- en terminalstatus teruggeeft op basis van CMR-nummer, gescoped op het dossier van de beller via het CRM. Het model krijgt nooit ruwe feed payloads te zien; het ziet een genormaliseerd status-object.
Hoe lang duurde de bouw van scope tot productie?
Zes weken bouwen, daarna twee weken begeleide live-calls voordat de supervisor pass werd losgelaten. De terugverdientijd voor de operator landde in maand drie, tegenover een interne prognose van maand vier.