Chat agents
GGZ chat agent case: 30 seconden tot C-SSRS-escalatie
Een Utrechtse GGZ-praktijk met 22 mensen moest 1.720 chatvragen per week triëren over een 13 jaar oud EPD en een eigen ROM-archief, zonder één C-SSRS ≥4-signaal te missen. Dit is wat we bouwden.

Dinsdagochtend 07:42
Een cliënt opent het portaal van een Utrechtse GGZ-praktijk en typt één zin: "Ik weet niet of ik dit nog kan." Het is 07:42. De receptie is er pas om 08:30. De intake-coördinator zit tot 10:00 in een teamoverleg. De behandelaar opent de inbox na de eerste sessie van de dag, rond 11:15.
Drieënhalf uur stilte in een chatbox bij een GGZ-praktijk is op zichzelf al een failure mode. Tel daar de Wkkgz bij op en het is ook een juridisch risico. Zodra de praktijk op de hoogte is van een calamiteit, gaat de klok naar de Inspectie Gezondheidszorg en Jeugd lopen. Die klok pauzeert niet omdat de inbox vol stond.
Dit is het verhaal van hoe we die drieënhalf uur terugbrachten tot dertig seconden, bovenop een EPD van dertien jaar oud.
De stack die we erfden
De praktijk heeft 22 mensen op de loonlijst: 9 behandelaren (GZ-psychologen, klinisch psychologen, één psychiater), 6 basis-psychologen, 4 in administratie, 2 in management, en één ICT-functie die wordt gedragen door een externe die 12 uur per week beschikbaar is. Hun EPD is USERvision, geïnstalleerd in 2013 en sindsdien ter plekke gepatcht. Hun ROM-archief — de SQL-store achter elke BSI-23, OQ-45 en CORE-OM die ooit door een cliënt is ingevuld — is een eigen build op SQL Server 2016. Microsoft's extended support voor SQL Server 2016 stopt op 14 juli 2026. We schrijven dit drie weken voor die datum. Migratie staat op de roadmap voor 2026, maar moest wachten op de chat agent en het wachtlijst-portaal.
Het portaal bestond al. Een dunne React-app die voor de webform-endpoints van USERvision hing. Wat er niet was: triage. Elk bericht kwam in één FIFO-queue terecht, gepolld door welke administratief medewerker er net even tijd had. Een logistieke vraag over een herhaalrecept en de zin van 07:42 werden identiek behandeld. Maandagochtend lag er een backlog van 180 berichten. Vrijdagmiddag 60. Het klinische signaal zat midden tussen beide in.
De Wkkgz-klok in normaal Nederlands
Artikel 11 van de Wkkgz verplicht elke Nederlandse zorgaanbieder tot een intern systeem voor incidentmeldingen. Daarnaast moet de praktijk, zodra een incident voldoet aan de juridische definitie van een calamiteit — een gebeurtenis die heeft geleid tot, of had kunnen leiden tot, het overlijden of ernstig letsel van een cliënt — dit melden bij de Inspectie Gezondheidszorg en Jeugd. De richtlijn van de IGJ is "onverwijld" en in de praktijk binnen drie werkdagen na het moment waarop de praktijk dit redelijkerwijs had kunnen weten.
Die laatste bijzin maakte de inbox tot een juridisch artefact. Zodra een high-risk-bericht in het portaal is afgeleverd, is de praktijk op de hoogte. Blijft dat bericht negen uur ongelezen, dan wordt het standpunt "we wisten van niets" met het uur dunner. De taak van de chat agent is ervoor zorgen dat de praktijk het binnen dertig seconden weet, met een papieren spoor dat dat bewijst.
Hoe 1.720 vragen per week er echt uitzien
Voor we iets bouwden, lazen we vier weken aan inbox. Het volume was 1.720 berichten per week, zonder noemenswaardige seizoenscurve. De verdeling:
- ~72% logistiek — afspraken verzetten, factuur-vragen, parkeren, herhaalrecepten.
- ~22% intake en wachtlijst — vragen over zorgvraagtypering, wachttijd, eigen risico.
- ~3% klinische content — symptomen, bijwerkingen van medicatie, slaap, paniek.
- ~0,4% triggert een C-SSRS-herafname. Dat zijn ruwweg zeven berichten per week.
0,4% klinkt onschuldig tot je het omzet naar wandklok. Zeven berichten per week is er één per vierentwintig uur. Blijft één daarvan een nacht ongelezen, dan begint maandagochtend met een telefoontje van de IGJ. Het product waar de praktijk om vroeg was geen chat agent. Het was het wegnemen van dat nachtelijke risico.
De C-SSRS-gate
We scoren elk binnenkomend bericht tegen een aangepaste Columbia Suicide Severity Rating Scale. De klinische versie heeft zes items; wij gebruiken de screeningsversie met de eigen drempels van de praktijk, schriftelijk vastgelegd met de verantwoordelijke psychiater. Alles wat 4 of hoger scoort — actieve ideatie met enige intentie om te handelen — komt in een aparte queue die alleen GZ-psychologen en de dienstdoende crisisdienst zien.
De SLA waar we ons aan committeerden is hard in zijn eenvoud: dertig seconden van bericht binnen tot toewijzing aan de queue. In dat venster zitten de classifier-call, de disagreement-check tegen een tweede model, de patiënt-lookup tegen de USERvision-replica, het ophalen van de laatste ROM-score, en de write naar de queue. De agent draait op een dedicated VPS in Frankfurt. We hebben tail-latency drie weken lang in shadow mode gemeten voordat we hem ook maar één bericht lieten routeren.
Voor deze berichten schrijft de agent nooit een klinisch antwoord. Het product is de queue, niet het antwoord.
Lezen uit een EPD van 13 jaar oud
USERvision uit 2013 heeft geen echte API. De portaal-endpoints die we zagen waren niet-geauthenticeerde form-handlers die rechtstreeks naar de MSSQL-backend van het EPD schreven. Die vanuit de agent aanroepen was sneller te bouwen geweest en onmogelijk te verdedigen tegenover de FG of de IGJ. Dus dat deden we niet.
Wat we wel deden:
- Een read-only replica van de USERvision MSSQL-database opgezet. Change Data Capture waar dat ondersteund werd, een snapshot per uur waar dat niet ging.
- Het ROM-archief vanaf SQL Server 2016 op dezelfde manier gespiegeld.
- Een dunne staging-laag in Postgres gebouwd die patient-id, BSN-hash, behandelaar, dossierstatus en de laatste drie ROM-scores normaliseert in één view.
- Onder geen enkele voorwaarde terugschrijven naar USERvision. Het EPD blijft de single source of truth; de writes van de agent gaan naar zijn eigen append-only audit log.
De read-only-beperking klinkt als een beperking. Het is het feature. Als de FG het systeem audit — en bij een praktijk die 1.720 berichten per week verwerkt, audit de FG het systeem — kan de praktijk laten zien dat de AI-laag observability is, geen autoriteit.
-- The view the agent reads from before it scores anything
CREATE VIEW staging.client_context AS
SELECT
c.client_id,
c.behandelaar_id,
c.dossier_status,
c.laatste_contact,
rom.bsi_23_total AS rom_bsi_latest,
rom.bsi_23_subscale_si AS rom_bsi_si,
rom.measured_at AS rom_measured_at,
ev.events_last_72h
FROM uservision_replica.client c
LEFT JOIN rom_replica.latest_rom_score rom
ON rom.client_id = c.client_id
LEFT JOIN events.client_event_summary ev
ON ev.client_id = c.client_id
WHERE c.dossier_status IN ('actief', 'wachtlijst', 'nazorg');
Het overdrachtsprotocol
Wanneer een bericht in de queue van de GZ-psycholoog landt, ziet de ontvangende behandelaar vier panelen. Het ruwe bericht, onaangeraakt. De C-SSRS-score met de redeneerstap van het model en de letterlijke zinnen die hem triggerden. De laatste drie contacten uit USERvision plus de meest recente ROM-subschaal voor suïcidale ideatie. En een voorgesteld antwoord-template: empathische erkenning plus het aanbod om binnen vijftien minuten terug te bellen. De behandelaar past het template aan voor verzending. Altijd.
Het doel van het voorgestelde template is niet snelheid. Het haalt de cognitieve kost van "wat schrijf ik" weg, zodat de behandelaar die kost kwijt kan aan "wat doe ik nu." In de praktijk herschrijft de behandelaar ongeveer de helft van elke suggestie. Dat is het juiste getal. Als de rewrite-rate naar nul zou gaan, zouden we ons zorgen maken dat de behandelaars niet meer aandachtig lezen.
Het paneel toont ook een freshness-indicator op elk datapunt. Is de ROM-score ouder dan vier uur, dan staat dat er rood in. De behandelaar bepaalt of een verouderde score nog te vertrouwen is, niet de agent.
Wat we niet hebben gebouwd
We hebben geen klinische chatbot gebouwd. De agent beantwoordt geen vragen over medicatie. Hij valideert geen emotionele toestanden. Hij schrijft geen geruststelling.
We hebben hem ook niet gebouwd om de makkelijke logistiek weg te triëren. Andere teams doen dat met kleinere, smallere agents bovenop dezelfde staging-laag. Deze had één taak: nooit een high-risk-bericht ongelezen laten liggen.
En we hebben hem niet gekoppeld aan de dialer of de SMS-gateway. Pakt een behandelaar een geëscaleerd bericht op, dan belt die de cliënt vanaf de eigen werktelefoon. Elk kanaal dat een patiënt raakt, laat hetzelfde audit-spoor achter als altijd. De chat agent is, vanuit het perspectief van de patiënt, onzichtbaar. Vanuit het perspectief van de IGJ is hij een logbestand dat bewijst dat de praktijk binnen dertig seconden handelde.
Wat er stuk ging in v1
De eerste zes weken waren niet de demo. Drie dingen moesten worden gefikst.
Nederlandse spreektaal scoorde te laag. De classifier was een fine-tuned multilingual model. Hij pikte "ik wil niet meer leven" moeiteloos op. Hij miste "ik zie het niet meer zitten," de meer gangbare Nederlandse formulering en in een klinische context onmiskenbaar high-risk. Hij miste ook "ik ben het zat" wanneer de omringende zin de betekenis klinisch maakte in plaats van logistiek. We voegden een tweede model toe met een andere prompt en verplichtten overeenstemming bij C-SSRS ≥3. Disagreement vanaf 3 routeert naar een menselijke review-queue, bemand door een senior administratief medewerker die getraind is om te escaleren maar niet om te antwoorden.
De replicatie van het ROM-archief liep achter. SQL Server 2016 op de on-prem-bak liep zondagavond vol tijdens een CDC-inhaalslag. De replica werd negen uur niet bijgewerkt. De agent bleef scoren, maar met verouderde context. We voegden de freshness-check hierboven toe en een Slack-page naar de externe als de lag boven een uur komt.
De behandelaars vertrouwden de queue niet. Natuurlijk niet. We logden de eerste acht weken elke escalatie, elke override en elke afwijzing en hielden iedere vrijdag een review met de GZ-psychologen en de verantwoordelijke psychiater. Na zes reviews zat de false-positive rate op 1,4% van de escalaties en verschoof het gesprek van "is dit ding veilig" naar "kunnen we een flag toevoegen voor eetstoornis-terugval." Die verschuiving was de mijlpaal.
De cijfers na acht maanden
Van oktober 2025 tot half juni 2026:
- 1.720 berichten per week, gemiddeld.
- 11 escalaties per dag naar de queue van de GZ-psycholoog, gemiddeld.
- 97,8% van de escalaties opgepakt binnen 30 seconden; de rest binnen 90.
- 4 bevestigde C-SSRS ≥4-events. Allemaal binnen 90 seconden bij een behandelaar; allemaal binnen 15 minuten klinisch teruggebeld.
- 0 gemiste Wkkgz-meldingstermijnen.
- 1,4% false-positive rate op escalaties, stabiel.
Het Wkkgz-getal is waar de bestuurder om geeft. De 1.720 is waar de administratief medewerkers om geven. Hun FIFO-backlog piekte vroeger op 180 berichten op maandagochtend. Hij piekt nu op 31. Vrijdagmiddag is om 16:00 leeg.
We beweren niet dat de agent een suïcide heeft voorkomen. Die data hebben we niet en die claim zouden we ook niet maken als we ze wel hadden. Wat we wel kunnen zeggen: vier keer in acht maanden zat een behandelaar te lezen en te bellen binnen vijftien minuten na een bericht dat in de oude workflow in een maandag-backlog van 180 had gelegen.
De chat agent verving niemand. Hij veranderde welke berichten ieder mens als eerste leest, en welke berichten ieder mens überhaupt leest.
Wat je maandag kunt doen
Run je een praktijk of kliniek met een vergelijkbare vorm — een verouderd EPD, een klein behandelteam, een inbox die logistiek niet scheidt van risico — dan is het kleinste nuttige wat je maandagochtend kunt doen: één week aan binnenkomende berichten lezen met een stopwatch. Tel hoeveel er klinisch zijn, hoeveel er taal rond suïcidaal-ideatie raken, en hoe lang ieder bericht ongelezen lag. Dat getal is je startpunt. Al het andere is een optimalisatie bovenop een probleem dat je niet gemeten hebt.
Toen we de chat agent voor deze Utrechtse praktijk bouwden, was niet de classifier het stuk dat we onderschatten. Het was de read-only replicatie van USERvision en het SQL Server 2016 ROM-archief — de agent observability-only houden tegen twee systemen die nooit bedoeld waren om in dit tempo uit gelezen te worden. We hebben dat opgelost met CDC-snapshots per uur, een Postgres staging-view en een freshness-check die de behandelaars zelf kunnen zien. Dat is het soort werk dat we doen als AI-agents voor klanten met verouderde klinische stacks.
Kern
Een chat agent in een GGZ-praktijk hoort nooit het klinische antwoord te schrijven. Het product is de queue en de context die snel voor een mens komen te liggen.
FAQ
Waarom is C-SSRS 4 de triggerdrempel en niet 3 of 5?
Een 4 op de Columbia Suicide Severity Rating Scale staat voor actieve ideatie met enige intentie om te handelen. Het is de klinisch geaccepteerde drempel waar de richtlijn verschuift van monitoren naar directe interventie. De praktijk heeft die drempel schriftelijk vastgelegd met de verantwoordelijke psychiater.
Kan de chat agent niet ook het klinische antwoord opstellen?
Technisch wel. Juridisch en klinisch niet. Het product van de agent is de escalatie-queue en de patiëntcontext. Het antwoord wordt elke keer geschreven door een GZ-psycholoog. Een LLM kan onder de Wkkgz geen klinische verantwoordelijkheid dragen.
Waarom een Postgres staging-laag in plaats van USERvision direct bevragen?
Om het EPD de single source of truth te houden en de actieve patiëntenzorg af te schermen van een 13 jaar oude database die nooit ontworpen is voor LLM-gestuurde query-patronen. De staging-laag is read-only; de agent schrijft nooit terug naar USERvision.
Hoe lang heeft het project end-to-end geduurd?
Twaalf weken van kickoff tot klinische uitrol, inclusief zes weken shadow mode waarin de agent elk bericht scoorde maar niets routeerde. Shadow mode is niet onderhandelbaar voor een triage-agent in een klinische setting.
Wat gebeurt er als het tweede model boven C-SSRS 3 oneens is met het eerste?
Het bericht routeert naar een menselijke review-queue, bemand door een senior administratief medewerker die getraind is om te escaleren maar niet om te antwoorden. Zo blijft disagreement uit de behandelaars-queue zonder dat het signaal verloren gaat.