← Blog

Voice agents

Voice agent uitrol: 14 fysio-praktijken in twee talen

Een voice agent die de telefoon van een fysioketen met 47 medewerkers in twee talen afhandelt klinkt simpel, totdat je de maandagochtend van de receptioniste in kaart brengt.

Jacob Molkenboer· Oprichter · A Brand New Company· 9 jun 2026· 9 min
Crème bakelieten telefoonhoorn op donkergroen leren onderlegger, groen lint, gevouwen papieren kaartjes, koperen bel.

Maandagochtend, 8:42. Drie telefoonlijnen knipperen op de centrale receptie van een fysioketen met 47 medewerkers in Utrecht. De receptioniste heeft voor elk van de 14 praktijkagenda's een tab openstaan, een Excel-sheet met regels voor gezinskorting aan de muur geplakt, en een collega in de praktijk Overvecht vraagt via Teams of de nieuwe Arabisch-sprekende patiënt nou bij Lange Nieuwstraat of Kanaleneiland is ingeboekt. Om 9:15 staat de wachtrij veertien personen diep en heeft iemand opgehangen. Dit is de ochtend die bepaalt of een voice agent zijn geld waard is.

Dit is het gesprek dat we afgelopen winter aannamen. De keten wilde één nummer, één belofte aan de beller, en nul verloren gezinskortingen. Geen chatbot die zich voordoet als voice agent, geen voice agent die receptioniste speelt. Ze wilden gewoon dat de telefoonlijn werkte.

Wat volgt is de playbook die we gebruikten. Het is een mening. Over 18 maanden ziet de stack er anders uit. De volgorde niet.

Twee weken meeluisteren voordat we ook maar iets bouwden

Voordat we ook maar één SDK aanraakten, zaten we tien werkdagen naast twee receptionistes en tagden we elk inkomend gesprek. Het tagging-schema was bewust grof: nieuwe intake, vervolgafspraak, verzetten, annuleren, vraag over facturatie, 'is therapeut X er vandaag', en een rest-categorie 'overig'. We tagden ook de gesproken taal en of de beller halverwege wisselde.

Het cijfer dat ertoe deed: 71% van de gesprekken viel in drie categorieën (nieuwe intake, verzetten, annuleren). De overige 29% was een lange staart die geen enkele voice agent op dag één moet aanraken. We schreven die regel op het whiteboard en lieten 'm daar staan voor de rest van het project.

Er kwam nog een tweede patroon boven dat niemand eerder in cijfers had gevangen. Ongeveer één op de zes bellers sprak Nederlands maar had Arabisch als voorkeurstaal voor schriftelijke communicatie in het EPD staan, en ongeveer de helft van die bellers wisselde van taal zodra het gesprek medisch werd. Dat ene datapunt bepaalde de taalstrategie meer dan welke model-benchmark dan ook.

Kernpunt

Doe een audit van de gesprekken voordat je een model kiest. De vorm van je inkomend volume vertelt je welke 70% je automatiseert en welke 30% je voor een mens reserveert.

Eén bron van waarheid voor de agenda, geen uitzonderingen

De keten draaide op een Nederlands fysiotherapie-EPD met een klinische agenda (één per behandelaar) en een aparte 'blok'-agenda voor behandelkamers. Veertien praktijken, ongeveer 60 behandelaars, vier soorten ruimtes. We brachten het boekingsmodel op papier in kaart voordat we een regel code schreven.

De regel waar we op uitkwamen: het EPD is de enige die schrijft. De voice agent leest beschikbaarheid via de appointment API van het EPD en schrijft nieuwe boekingen weg via hetzelfde endpoint. Geen caching van slots langer dan 60 seconden, geen parallelle Google Calendar, geen 'sync-laag'. Sync-lagen tussen twee agenda's zijn precies hoe je een vrijdag om half vijf dubbel boekt.

// Single read path, single write path
async function findSlots(clinicId: string, specialty: string, window: DateRange) {
  return ehr.appointments.searchAvailability({
    clinic: clinicId,
    specialty,
    from: window.start,
    to: window.end,
    durationMin: 25,
  })
}

async function bookSlot(input: BookingInput) {
  // EHR is the lock holder. If the slot was just taken,
  // the EHR returns 409 and we re-quote to the caller.
  return ehr.appointments.create(input)
}

Het 409-pad is het pad waar niemand over praat en het pad dat ertoe doet. Wanneer twee bellers op woensdag om 14:00 in twee verschillende praktijken om dezelfde slot vragen, weigert het EPD de tweede boeking. De agent moet zich verontschuldigen, opnieuw beschikbaarheid uitlezen, en de eerstvolgende slot in de taal van de beller aanbieden, zonder te klinken alsof hij een script voorleest. Dit specifieke pad hebben we vaker getest dan elk ander.

De voice stack waar we op uitkwamen

We hebben drie pipelines getest voordat we er één kozen. De shortlist:

  • Speech-to-text in Nederlands en Arabisch met tolerantie voor code-switching. Het Nederlandse model van Deepgram hield zich staande op telefoon-audio; voor Arabisch vielen we terug op een zelf-gehoste Whisper-large endpoint, omdat Modern Standaard Arabisch plus Levantijns dialect over 8kHz-lijnen te wisselend was op de kant-en-klare opties.
  • Een LLM in het midden, geprompt als boekingsagent met een smalle tool-lijst (beschikbaarheid zoeken, slot voorstellen, boeking bevestigen, overdragen aan mens). Het model is vervangbaar. Het tool-oppervlak niet.
  • Neurale TTS die niet klinkt als een IVR uit 2019. We kwamen uit op ElevenLabs multilingual voor beide talen, met één voice-ID per taal en dezelfde persona-naam (Jasmijn) over beide heen. De beller hoort één identiteit, geen twee.

De orchestration draait op LiveKit Agents, tussen de SIP-trunk (KPN-zakelijke lijn, gerouteerd via een Twilio Programmable Voice nummer voor het internationale traject) en onze tool-laag. We kozen LiveKit boven zelf bouwen, omdat barge-in afhandeling en turn detection op een meertalige lijn precies het soort werk is waar je niet de eerste wilt zijn die de edge case ontdekt.

Taaldetectie zonder die ongemakkelijke stilte

Het eerste prototype nam op in het Nederlands, luisterde twee seconden, en schakelde over naar Arabisch als het Arabische fonemen herkende. Bellers haatten het. De pauze klonk als een gebroken lijn en de oudere patiënten hingen op.

De oplossing was klein en verdient een eigen alinea. We nemen op met een tweetalige opener: "Praktijk Utrecht, met Jasmijn. Goedemorgen. اهلا و سهلا." De Arabische begroeting is kort, herkenbaar, en geeft een beller met Arabisch als eerste taal de ruimte om in het Arabisch te antwoorden. De STT-laag draait beide taalmodellen parallel voor de eerste uiting en kiest welke de hoogste confidence teruggeeft. Vanaf turn twee zetten we de taal vast, tenzij de beller midden in een zin overschakelt, wat in Utrecht vaker gebeurt dan je zou denken.

De lock-and-release logica is het stuk dat het langst kostte om af te stellen. Een patiënt die een hamstringblessure beschrijft, laat het Nederlandse woord hamstring vallen in een Arabische zin. De agent mag dat ene token niet interpreteren als een taalwissel. We voegden een heuristiek toe: minstens drie opeenvolgende content-tokens in de nieuwe taal, of één volledige zinsgrens, voordat we de TTS-stem omdraaien.

Gezinskorting-logica als code, niet als prompt

Dit was het stuk waar de keten het meest nerveus over was. Hun kortingsregels stonden nergens opgeschreven in het EPD. Twee ouders en één kind krijgen 12% korting op een gedeelde maandfactuur als ze allemaal in dezelfde praktijk boeken. Voeg een tweede kind toe en de korting wordt 15%, maar alleen als de afspraak van minstens één ouder op dezelfde dag valt. Pleegplaatsingen tellen mee. Grootouders op hetzelfde adres tellen mee. Een patiënt die vorig jaar verhuisde niet, maar zijn laatste zes boekingen staan nog wel in het gezinsdossier.

Niets daarvan zit in de prompt. De prompt is voor toon en beurtwisseling. Recht op korting is een deterministische functie die de agent aanroept voordat een boeking wordt bevestigd:

type FamilyContext = {
  householdId: string
  members: Array<{ patientId: string; role: 'parent' | 'child' | 'other' }>
  sharedClinicId: string | null
  sameDayParentBooking: boolean
}

function familyDiscountTier(ctx: FamilyContext): 0 | 12 | 15 {
  if (!ctx.sharedClinicId) return 0
  const children = ctx.members.filter(m => m.role === 'child').length
  if (children < 1) return 0
  if (children === 1) return 12
  return ctx.sameDayParentBooking ? 15 : 12
}

De agent leest de huishoudgegevens uit het EPD, berekent het tarief, en vertelt de beller wat hij gaat betalen voordat de boeking definitief wordt. Als de berekening onzeker is (een pleegplaatsing met een startdatum in de toekomst, een gezinsdossier dat vorige week is samengevoegd en nog niet gereconcilieerd), draagt de agent over. We logden in de eerste maand 41 zulke overdrachten. De receptionistes waren dankbaar voor elke afzonderlijke.

Let op

Codeer prijsregels niet in de LLM-prompt. Prompts driften; prijzen mogen dat niet. Houd het model bij toon en beurtwisseling, en zet elke cent in deterministische code die je finance-team kan lezen.

Overdracht aan een mens, ontworpen vóór de livegang

Elk voice agent project heeft dezelfde faalmodus: de agent draagt niet over wanneer hij dat zou moeten doen. We bouwden drie overdracht-triggers vooraf in:

  1. De beller zegt een variant van 'ik wil iemand spreken', in beide talen, inclusief de formuleringen die we uit de meeluister-logs verzamelden ("mag ik iemand spreken", "اريد ان اتحدث مع شخص", en het tiental beleefde Nederlandse omtrekkende bewegingen die hetzelfde betekenen). De trigger is een string match, geen model-aanroep. Latency telt.
  2. De korting- of verzekeringslogica geeft uncertain terug. Dat was het pleegplaatsings-geval en een handvol eigen-bijdrage-combinaties die we in de eerste audit hadden gemist.
  3. Het model roept zelf de interne need_human tool call aan. We lieten hem in de eerste maand neigen naar escalatie en scherpten de drempel later aan naarmate de logs zich opbouwden.

Overdracht betekent een warme transfer naar de bestaande receptiebalie van de keten, met een regeltje samenvatting die in het EPD popt: naam beller, wat hij wilde, wat de agent al wel of niet had geboekt, waarom hij eruit stapte. De receptioniste pakt het gesprek halverwege op, in plaats van bij nul te beginnen.

Zes cijfers die we de eerste maand volgden

Wij geloven niet in vanity dashboards. De operations lead van de keten kreeg een wekelijkse mail met zes cijfers, en alleen zes:

  • Gesprekken die de agent binnen twee belsignalen opnam.
  • Gesprekken volledig afgehandeld zonder mens (doel: 60% aan het einde van maand één).
  • Gemiddelde gespreksduur op afgehandelde gesprekken (doel: onder de 90 seconden).
  • Boekingsconflicten opgevangen door de 409-response van het EPD (doel: nul die als dubbele boeking bij de beller terechtkwamen).
  • Verkeerd toegepaste kortingstarieven gevonden door het wekelijkse reconciliatie-script (doel: nul).
  • Klachten van bellers in het notitieveld van het EPD die "de robot" of "het systeem" noemen (doel: dalend).

Tegen week vier loste de agent 64% van de gesprekken op zonder mens, de gemiddelde gespreksduur landde op 78 seconden, en de receptionistes deden weer het werk dat na 11:00 normaal van hun bord viel: no-shows nabellen, verzekeringsformulieren verwerken, patiënten begeleiden bij hun eerste afspraak. De keten heeft sindsdien geen FTE aan het receptieteam toegevoegd.

Wat we zouden overslaan als we vandaag begonnen

Twee dingen, beide het hardop noemen waard. Eén: probeer geen uitgaande herinneringen vanuit dezelfde agent op dag één. Uitgaand bellen heeft andere toestemmingseisen onder de Nederlandse Telecommunicatiewet, een ander succescriterium, en een ander model van 'is het gesprek goed gegaan'. Ship eerst inkomend, kijk er een maand naar, en beslis dan.

Twee: het model dat je vandaag kiest, is binnen 12 maanden vervangen. Dat is prima. Voice agents die echte afspraken boeken tegen echte agenda's doen vandaag echt werk, op modellen die nu al bestaan. De architectuur (één agenda als source of truth, deterministische prijzen, snelle overdracht) is wat blijft staan.

Toen we dit bouwden voor de Utrechtse keten, was het ding dat we steeds onderschatten hoeveel van het werk operationeel was, niet technisch: wie is eigenaar van de screen-pop UX van de receptie, wie kijkt de wekelijkse logs door, wie tekent af op de prompt-diff. We bouwen dat werk inmiddels vanaf week één in onze voice agent trajecten in, in plaats van week zes.

Wil je morgen beginnen? Doe de gesprek-audit. Twee receptionistes, tien dagen, één gedeelde spreadsheet, drie kolommen: soort gesprek, taal, wat er fout was gegaan als een robot had opgenomen. Dat blad is de briefing.

Kern

Doe een audit van de gesprekken voordat je een model kiest. De vorm van je inkomend volume vertelt je welke 70% je automatiseert en welke 30% strikt menselijk blijft.

FAQ

Waarom niet één meertalig model voor zowel Nederlandse als Arabische STT?

Kant-en-klare meertalige STT was wisselend op Arabisch in telefoonkwaliteit met Levantijns dialect. Voor turn één een Nederlands model en een zelf-gehoste Whisper-large voor Arabisch parallel draaien gaf ons schonere confidence scores.

Wat gebeurt er als de EPD-API tijdens een gesprek onbereikbaar is?

De agent valt niet terug op een gecachete agenda. Hij verontschuldigt zich, biedt aan een terugbelnummer op te nemen, en transfereert naar de receptiebalie. Boeken tegen verouderde beschikbaarheid is erger dan niet boeken.

Hoe lang duurde de uitrol van kickoff tot live gesprekken?

Tien werkdagen meeluisteren, vier weken bouwen en koppelen met het EPD, twee weken begeleide pilot in één praktijk, daarna een gefaseerde overgang van de overige 13 praktijken in drie weken.

Hebben jullie de receptionistes vervangen?

Nee. Het receptieteam is even groot. Ze namen geen routinematige boekingen meer aan en pakten het werk op dat eerder bleef liggen: no-shows nabellen, verzekeringsformulieren, triage aan de balie. Er is sindsdien niemand bijgekomen.

voice agentsai agentsautomationintegrationsworkflowcase study

Iets bouwen?

Start een project