Voice agents
Voice agent playbook: HiX, Exchange en de pijnregel
Een revalidatiecentrum in Enschede met 25 medewerkers verwerkt 1.260 afspraakwijzigingen per week via een voice agent. Zo koppelen we HiX, Exchange en een 7/10-pijnregel.

Vrijdag, 16:47. De balie van een revalidatiecentrum in Enschede heeft eenendertig voicemails uit het lunchuur staan, en een rij van zeven patiënten die nog in hun regenjas wachten. Twee van die voicemails zijn iemand die een knie-revalidatie twintig minuten opschuift. Eén is een man die zegt dat zijn rug eruit ligt en vraagt of er iets eerder kan. Op de vraag hoe erg het is, op een schaal van tien, geeft hij de receptioniste een 7. Ze kan de volgende lijn niet opnemen en tegelijk zijn pijnscore in HiX wegschrijven, dus ze doet het tweede half. Tegen de tijd dat iemand op de klinische afdeling de notitie leest, is het 18:30, zijn de behandelaars naar huis, en sluit het Zvw-rapportagevenster over drie en een half uur.
Dit is het scenario waarop een voice agent zichzelf verdient. Niet de demo waarin hij een kapper boekt. Het scenario waarin de prijs van een gemiste melding boven de 7/10 een man is die om 21:55 op zijn badkamervloer zit — drie minuten nadat hij gezien had kunnen worden.
Hieronder het playbook dat we gebruikten voor een centrum met 25 medewerkers, dat per week ongeveer 1.260 afspraakwijzigingen verwerkt — het merendeel saai, twee niet — over een dertien jaar oud ChipSoft HiX-EPD en een zelfgebouwd Exchange 2016-fysiotherapeutrooster dat het hoofd fysiotherapie ergens rond de verbouwing van 2019 heeft opgetuigd.
De twee systemen die het eens moeten worden
De voice agent is niet het moeilijke deel. Dat is hij nooit.
Het moeilijke deel: de waarheid van het centrum staat op twee plekken die elkaar haten. Patiëntafspraken, behandelplannen en de Zvw-aanspraakgegevens staan in HiX. Het fysiotherapeutrooster — wie er in welk kwartier in welke behandelzaal staat — staat in een Exchange 2016-agenda met eigen categorieën en een kleurgecodeerde schaduwspreadsheet die alleen de senior fysiotherapeut nog begrijpt.
Een afspraak verzetten betekent een write naar HiX en een write naar Exchange, in die volgorde, binnen dezelfde minuut — anders boekt het rooster stilletjes een knie en een schouder over elkaar heen. We hebben het zien gebeuren. De behandelaar improviseert dan tegenover twee patiënten, die beiden denken dat ze haar werk niet kent, en het centrum draait op voor het slot.
De eerste regel van het playbook is dus: de voice agent mag de beller niets bevestigen voordat beide writes binnen zijn. Geen ik geef het door. Geen we hebben het genoteerd. Pas bevestigen ná de round-trip.
De vorm van het systeem
Dit is de topologie die uiteindelijk in productie is gegaan.
caller
│
▼
SIP trunk (KPN) ── Twilio voice ──► Dutch ASR (Azure Speech, nl-NL)
│
▼
intent router
│
┌───────────────────────────────┼───────────────────────────────┐
▼ ▼ ▼
afspraak-wijziging pijn-trigger (≥7/10) algemene info
│ │
▼ ▼
HiX SIU^S14 over MLLP behandelaar queue (Teams)
│ + SMS pager to dienst-doend
▼
Exchange 2016 EWS write
│
▼
read-after-write confirm in Dutch
Niets exotisch. De enige mening in dat diagram is dat de intent router de kleinst mogelijke LLM-call is waar we mee weg kwamen — een gpt-4o-mini-achtig model op een private endpoint, beperkt tot vier intents, met de pijn-trigger als hardgecodeerde regex-fallback. We vertrouwden het model namelijk niet om nooit een zachte zeven of een gemompelde acht te missen.
De 50-secondenregel voor pijn
Het hoofd klinisch trok een grens. Als een beller uit zichzelf een pijnscore boven de 7 noemt, of een van een vaste lijst frases gebruikt (ik kan niet meer, vastgeslagen rug, tintelingen in mijn been, ik val bijna), dan moet de agent binnen vijftig seconden een behandelaar aan de lijn hebben — of hoorbaar gefaald hebben en doorgezet zijn naar de balie.
Vijftig seconden, omdat het klinische team heeft gemeten hoe lang de langste nog draagbare wachttijd voelde aan de ontvangstkant van een echt triagegesprek. Geen dertig, want dat is onrealistisch als de dienstdoende net haar handen wast. Geen negentig, want dan heeft de beller al opgehangen en is hij richting de spoedpost gereden.
PIJN_TRIGGER_PHRASES = (
"tintelingen", "kan niet meer", "val bijna", "vastgeslagen",
"uitstralend", "uitstraalt", "schiet door", "verlamd",
)
def should_escalate(transcript: str, pain_score: int | None) -> bool:
if pain_score is not None and pain_score >= 7:
return True
lowered = transcript.lower()
return any(p in lowered for p in PIJN_TRIGGER_PHRASES)
Dat is niet het slimme deel. Het slimme deel is wat de agent zegt terwijl hij de behandelaar belt. We probeerden eerst een moment, ik verbind u door. Bellers hingen in 19% van de gevallen binnen tien seconden op. We hebben het veranderd in ik blijf bij u aan de lijn, ik haal er nu een behandelaar bij — toen daalden de drop-offs tot onder de 3%. De agent vertelt vervolgens wat er gebeurt: ik heb Marleen gebeld, zij pakt zo op rond seconde 15, en nog tien seconden rond seconde 40. Mensen wachten bijna alles uit zolang ze weten wat er gaande is.
Als de behandelaar-queue tegen seconde 50 niet heeft opgenomen, zegt de agent ik zet u nu door naar de balie en valt het gesprek terug op een echte receptioniste — met het transcript al op haar scherm. We vallen nooit stil terug. De receptioniste ziet een rode banner met de pijnscore, de triggerzin en het BSN-gemaskeerde dossier van de beller open in HiX.
Laat een voice agent nooit naar een wachtrij escaleren zonder harde bovengrens op de wachttijd. De faalmodus is niet de patiënt wacht iets langer. De faalmodus is de patiënt hangt op en je bent én de melding én je audit trail kwijt. Zet die grens in code, niet in een Slack-afspraak.
Praten met HiX zonder een API die je vertrouwt
ChipSoft HiX heeft integraties. Ze zijn gedocumenteerd op de manier waarop hele grote enterprise-software gedocumenteerd is: grondig, maar voor iemand die er al werkt. De relevante kanalen, in volgorde van voorkeur: HL7v2 ADT/SIU-berichten over MLLP voor het verplaatsen van afspraken, en de nieuwere FHIR R4-endpoints voor de read-kant. We hebben tegen de FHIR R4 Appointment-resource aan gemapt.
We zijn op drie regels uitgekomen.
- Lees patiënt- en behandelstatus via FHIR R4 over het interne netwerk van het centrum. Cache 60 seconden. Vertrouw de cache nooit voor een write-beslissing.
- Schrijf afspraakverplaatsingen weg als HL7v2 SIU^S12 (nieuw), S14 (wijzigen) of S15 (annuleren) berichten over MLLP naar de HiX-integratieserver van het centrum.
- Verifieer elke write met een follow-up FHIR GET op de appointment-ID binnen vijf seconden. Bevestigt de read de write niet: één keer opnieuw proberen, dan escaleren naar de receptioniste met het transcript intact.
Die verificatie is niet optioneel. HiX accepteerde ons SIU-bericht, gaf een ACK terug en heeft de wijziging in de eerste pilotweek drie keer stilletjes laten vallen, omdat de agenda van de behandelaar een soft lock vanuit een andere module had. Vertrouw nooit een ACK alleen. Read-after-write, of het is niet gebeurd.
async def move_appointment(appt_id: str, new_slot: Slot) -> Result:
await hl7.send_siu_s14(appt_id, new_slot)
for _ in range(2):
await asyncio.sleep(2)
live = await fhir.get_appointment(appt_id)
if live.start == new_slot.start:
return Result.ok(live)
return Result.escalate("hix_write_unconfirmed")
Exchange 2016, ouder dan sommige patiënten
Het fysiotherapeutrooster draait op een on-prem Exchange 2016-server. Geen Microsoft Graph — dat is cloud-only. We praten ermee via EWS (Exchange Web Services), wat voor Exchange Online deprecated is, maar voor on-prem 2016-machines nog het ondersteunde protocol blijft. Eén service account met impersonation-rechten, één langlevende worker die de connectie open houdt.
Twee valkuilen kostten ons een week.
- Het rooster gebruikt eigen agendacategorieën (
Knie-NWO,Schouder-Post-OK,Bekken-FT) om ruimte en apparatuur aan te geven. Ze worden alsCategoriesop elk afspraakitem opgeslagen, en de senior fysio gebruikt ze om haar kleurweergave te filteren. De voice agent moet de hele categorielijst bewaren bij een verzetting, anders breekt het rooster stilletjes haar filter. We leveren inmiddels een unit test voor elke categorie die we ooit zijn tegengekomen, omdat we onszelf niet vertrouwen om ze te onthouden. - De tijdzone-afhandeling van Exchange 2016 voor Europe/Amsterdam klopt in theorie en is in de praktijk stuk als het agenda-item oorspronkelijk door een Mac Outlook-client tijdens een zomertijdovergang is aangemaakt. We normaliseren bij de read naar UTC, schrijven terug als Europe/Amsterdam met een expliciete
TimeZoneContext, en hebben in acht maanden precies vier spookuur-verschuivingen gelogd.
De klif van 22:00
De Zvw-aanspraakrapportage is de saaie regel waardoor de rest pas telt. Elke patiëntinteractie die de aanspraak raakt, moet vóór de end-of-day rapportagecyclus in HiX staan, en die loopt voor dit centrum om 22:00 naar de koepelorganisatie. Een no-show die om 22:04 wordt vastgelegd, is voor de facturatie geen no-show. Het is onbetaald werk.
Late avondoproepen — en het zijn er meer dan je denkt tussen 20:00 en 22:00, want dat is het moment waarop mensen op de bank ploffen en doorhebben dat hun rug erger is dan ze dachten — moeten vóór de klif door de HiX-write komen. De agent gedraagt zich na zonsondergang dus anders.
- Na 21:00 kort de agent zijn verduidelijkingsprompts in. Geen kunt u dat herhalen-loops. Twee pogingen, daarna doorschakelen naar de mobiel van de dienstdoende receptioniste.
- Tussen 21:30 en 22:00 triggert elke geslaagde afspraakwijziging een synchrone push naar de HiX-aanspraakmodule, niet de lazy queue die we overdag gebruiken. We nemen de latency erbij om aan de goede kant van 22:00 te blijven.
- Om 21:55 stopt de agent met het aannemen van niet-urgente wijzigingen en legt de cut-off in gewoon Nederlands uit: ik kan deze wijziging vandaag niet meer doorzetten in het systeem; ik plan u in voor de eerste behandelaar morgenochtend. Pijn-triggers escaleren altijd. Punt. Er is geen stille uur voor pijn.
Elke zorg-voice-agent heeft ergens een klok zitten die zijn werk van betaald in onbetaald omzet. Vind die klok, en zet hem in de prompt van de agent, de routinglogica en het dashboard.
Wat we bewust bij mensen lieten
Het playbook is ook een lijst dingen die we weigerden te automatiseren.
- Eerste intake voor een nieuw revalidatietraject. De agent verzamelt geen medische voorgeschiedenis. Alleen door een behandelaar, in persoon, met de goniometer.
- Annulering binnen 24 uur zonder reden. De agent verzamelt de reden in een vrij veld en zet hem in de queue voor review door een behandelaar; hij beslist niet over Zvw-rechtmatigheid.
- Gesprekken waarin de beller huilt. We hebben een kleine emotional-distress-classifier getraind op de Nederlandse ASR-output (stiltepercentage, toonhoogtevariantie, zucht-detectie) en routeren die gesprekken direct naar de receptioniste met een zacht ik geef u even iemand. We proberen niet te troosten. We zijn er niet voor uitgerust, en doen alsof is erger dan toegeven.
Wat het werkelijk verschoof
Vier maanden later, in een gemiddelde week.
- 1.260 afspraakwijzigingen end-to-end door de agent afgehandeld. 87% afgerond zonder dat een mens er aan kwam. 13% bewust naar een mens gerouteerd.
- Gemiddelde pijn-trigger-escalatie: 38 seconden van beller naar behandelaar. Langzaamst in productie: 49 seconden. Geen enkele boven het 50-secondenplafond.
- Nul gemiste Zvw-rapportages sinds week drie, toen we de sync van 21:30 van queued naar synchroon hebben verschoven.
- Teruggewonnen receptionistetijd: ongeveer 22 uur per week verdeeld over twee FTE, doorgezet naar fysieke intake en het backofficewerk waar nooit iemand tijd voor had.
Het getal dat ons het meest interesseert is het getal dat we nooit zien: de patiënt die op een vrijdagavond binnen het uur een behandelaar kreeg, omdat de agent zijn melding niet liet vallen toen hij zei dat zijn rug eruit lag.
Wat je vandaag kunt doen
Pak je rooster erbij voor het drukste uur van volgende week. Luister tien willekeurige opnames van datzelfde uur van vorige week terug. Tel de gesprekken waarin de receptioniste moest kiezen tussen de volgende lijn opnemen of een write in het systeem van vastlegging afmaken. Dat getal is je businesscase voor een voice agent — niet de demo, niet de keynote, gewoon dat getal, op je eigen klok.
Toen we dit voor het centrum in Enschede bouwden, was het moeilijke nooit de voice agent zelf; het was HiX en Exchange het eens laten worden over wat een verzette afspraak betekende, en de beller niets bevestigen tot ze het waren. Staar je naar een vergelijkbare tweesysteem-stack? Dan is dat de plek om te beginnen.
Kern
De echte taak van een voice agent in de zorg is niet praten met patiënten — het is twee koppige back-endsystemen het eens laten worden voordat het facturatievenster sluit.
FAQ
Kan een voice agent écht een Nederlands medisch gesprek voeren?
Alleen als de ASR op nl-NL is afgestemd en de agent nooit klinisch iets beslist. De onze triëert en routeert; behandelaars beslissen. Zowel Azure Speech als Whisper-medium handelen de klinische woordenschat goed genoeg af voor triage.
Hoe integreer je in de praktijk met ChipSoft HiX?
Twee kanalen. HL7v2 SIU-berichten over MLLP voor afspraak-writes, FHIR R4 voor read-bevestigingen. Vertrouw nooit een ACK bij een write. Altijd read-after-write op de appointment-ID voordat je iets aan de beller bevestigt.
En als het rooster nog op Exchange 2016 draait en niet op Microsoft 365?
Gebruik EWS met een service account dat impersonation-rechten heeft. Bewaar bij elke verzetting de eigen categorieën, en normaliseer tijdzones naar UTC bij de read. EWS is deprecated voor Exchange Online, maar wordt op on-prem 2016 nog ondersteund.
Is een escalatiedoel van 50 seconden realistisch in productie?
Ja, mits de agent het wachten in woorden uitlegt en de behandelaar-queue parallel aan het gesprek wordt gepiept. We meten gemiddeld 38 seconden in productie. Het harde plafond telt zwaarder dan het gemiddelde; laat het er nooit doorheen glippen.
Wat als de HiX-write na 22:00 faalt?
Behandel het Zvw-rapportagevenster van 22:00 in code als een harde klif. Na 21:55 stopt de agent met het aannemen van niet-urgente wijzigingen en legt de cut-off uit. Pijn-triggers blijven escaleren. Boekingen verschuiven naar de queue van de volgende ochtend.