← Blog

Mobile apps

Tablet-PWA voor dierenartsen: einde klembord in Leiden

Een zaterdagochtend in Leiden, een bichon met een halfleesbaar briefje over paracetamol, en zes minuten om te bepalen of narcose veilig is. Het klembord moest weg.

Jacob Molkenboer· Oprichter · A Brand New Company· 25 aug 2024· 8 min
Veterinair klembord met handgeschreven notitie, gevouwen linnen doek, groen lint, waspotlood op ivoorpapier.

Het is zaterdagochtend in een kliniek aan de rand van Leiden. In de wachtkamer zitten twee katten, een bichon met een verbonden poot, en een tiener met een schoenendoos. De dienstdoende dierenarts is aan zijn derde koffie. Een assistente geeft hem een klembord met een intakeformulier voor de bichon. Eraan vastgeniet: een verwijsbrief van een naburige praktijk, plus een halfleesbaar briefje waarop staat "eigenaar denkt dat de hond dinsdag paracetamol heeft binnengekregen." De operatie staat gepland voor 11:00. De dierenarts heeft ruwweg zes minuten om te zien of er iets op dat formulier staat waardoor hij deze hond niet onder narcose mag brengen.

Dat is het moment waarop een papieren intakesysteem faalt. De informatie is er, het team is capabel, het falen is mechanisch: een mensenoog op drie stukken papier, onder tijdsdruk, op zoek naar een interactie die misschien één voetnoot is in een farmacologisch naslagwerk van 1.200 pagina's. Paracetamoltoxiciteit bij honden staat goed beschreven in de literatuur (zie de Merck Veterinary Manual), en toch is een handgeschreven krabbel onderaan een formulier precies het soort signaal dat onder echte praktijkomstandigheden gemist wordt.

Een Leidse dierenartsketen van 28 mensen, met zes klinieken verspreid over de Randstad, vroeg ons dit op te lossen. Dit is wat we gebouwd hebben, wat werkte, en de dingen die we onderweg verkeerd deden.

De intake-stack vóór de rebuild

Zes klinieken. Allemaal draaiend op hetzelfde Nederlandse praktijksysteem, ieder net iets anders ingericht door degene die de kliniek vier of vijf jaar eerder had opgezet. Intake ging op papier. Het papier werd aan het eind van de dag gescand. Scans gingen in een map per patiënt binnen het PMS. Niemand opende die scans opnieuw, tenzij een dossier escaleerde naar een verwijzing of een klacht.

De keten had twee dingen geprobeerd voordat ze ons belden. Een native iPad-app van een leverancier uit Eindhoven, die op de kliniekwifi ongeveer twaalf seconden nodig had om een patiëntdossier te laden. De assistenten gaven het na twee weken op en pakten het klembord weer. En een Google Form op het interne wiki, dat technisch werkte, maar geen koppeling had met het patiëntdossier. De data werd dus opnieuw overgetypt door de receptie, die al een wachtrij vaccinatieherinneringen had om te versturen.

De praktijkmanager zei het in het eerste scopinggesprek helder: "We hebben geen softwareprobleem, we hebben een frictieprobleem." Alles wat een stap toevoegde, raakte het team kwijt. Alles wat tijdens een consult een login vroeg, raakte het team kwijt. Alles wat van de dierenarts vroeg om een workflowregel te onthouden, raakte de dierenarts kwijt. De opdracht was klein van omvang, absoluut van norm: het nieuwe systeem moest vanaf dag één sneller zijn dan het klembord, anders was het bij oplevering al dood.

De tablet-PWA, concreet

We bouwden een Progressive Web App, vanaf één origin geserveerd, die het klinkteam opent op zes iPads (één per spreekkamer, plus een floater bij de receptie). Het is een PWA, geen native app, om de redenen die web.dev beschrijft: één codebase, geen App Store-review bij elke patch, directe installatie vanuit de browser, volledige offline-ondersteuning via een service worker. Het team installeert het één keer, pint het op het startscherm, en vanaf dat moment gedraagt het zich als elke andere app op de iPad.

Inloggen gebeurt één keer per apparaat, per dienst. Een assistente houdt de chip van het dier tegen de bestaande chiplezer van de kliniek, het dossier laadt vanuit de lokale cache (meestal 80 tot 150 milliseconden), en het intakeformulier is vooraf gevuld met het laatst bekende gewicht, huidige medicatie, allergieën en chronische aandoeningen. Wat nieuw is, typt de assistente in voor het oog van de eigenaar. De eigenaar ziet het scherm. De eigenaar corrigeert de spelling van de kattennaam. Die lus van vijf seconden is de helft van de waarde van het hele project: de eigenaar wordt medeauteur van het dossier in plaats van iemand tegen wie gepraat wordt.

Voor de engineers die meelezen: de cachestrategie is bewust saai. Workbox doet het meeste zware werk:

// service-worker.ts
import { registerRoute } from 'workbox-routing';
import { CacheFirst, StaleWhileRevalidate, NetworkOnly } from 'workbox-strategies';
import { BackgroundSyncPlugin } from 'workbox-background-sync';

// Patient records: serve cache, refresh in background.
registerRoute(
  ({ url }) => url.pathname.startsWith('/api/patients/'),
  new StaleWhileRevalidate({ cacheName: 'patients-v3' }),
);

// Static formulary: cache first, almost never changes.
registerRoute(
  ({ url }) => url.pathname.startsWith('/api/formulary/'),
  new CacheFirst({ cacheName: 'formulary-v3' }),
);

// Writes: queue offline, replay on reconnect.
const writeQueue = new BackgroundSyncPlugin('intake-writes', {
  maxRetentionTime: 24 * 60, // minutes
});

registerRoute(
  ({ url, request }) =>
    request.method === 'POST' && url.pathname.startsWith('/api/intake/'),
  new NetworkOnly({ plugins: [writeQueue] }),
  'POST',
);

De PMS-koppeling is een webhook in beide richtingen. Zodra de intake wordt opgeslagen, POST onze server binnen een seconde een gestructureerde payload naar het PMS. Wanneer het PMS een dossier bijwerkt (een dierenarts voegt na een consult bijvoorbeeld een chronische aandoening toe), pingt het onze server, en het volgende kliniekapparaat dat dat dossier opent, krijgt via de stale-while-revalidate-route de bijgewerkte versie.

De medicatiecheck in vier seconden

Dit is de hoofdfunctie. Voordat preoperatieve medicatie wordt bevestigd, stuurt de app het signalement van de patiënt (soort, ras, gewicht, leeftijd, signaalwaarden voor orgaanfunctie) en de voorgestelde medicatielijst naar een kennisbank-agent. De agent draait op een eigen corpus: het huisformularium van de keten, de anesthesieprotocollen van de keten, een gelicentieerd standaardwerk, en een door de medisch verantwoordelijke samengestelde selectie van peer-reviewed interactiepapers.

In minder dan vier seconden komt er één van drie statussen terug:

  • Groen: geen gemarkeerde interacties.
  • Oranje: een bekende voorzichtigheidsinteractie, met bronvermelding en een samenvatting van één zin.
  • Rood: een harde contra-indicatie, met bronvermelding, mechanisme en een voorgesteld alternatief uit het huisformularium.

De retrieval-architectuur is met opzet geen chat. De dierenarts typt nooit een prompt. De invoer is de gestructureerde formulierdata. De uitvoer is een deterministisch blok flags, gerenderd boven de "Bevestigen"-knop. De dierenarts tekent nog altijd af. De agent is een tweede paar ogen, geen beslisser. Dat onderscheid was belangrijk voor de medisch verantwoordelijke van de keten, die het ontwerp pas accordeerde nadat we hadden afgesproken het vrije-tekstveld uit het prototype te halen.

Kern

De agent neemt de beslissing niet. De agent zet een bronvermelding voor het oog van de dierenarts in de vier seconden waarin de beslissing valt.

Waarom vier seconden? Alles wat trager is, wordt spiergeheugen: "doorklik die waarschuwing." We zagen in het eerste prototype omzeilgedrag sterk oplopen voorbij ongeveer acht seconden, en bijna naar nul zakken zodra de round trip paste binnen de natuurlijke pauze tussen het kiezen van een middel en het tikken op bevestigen. De latency-budget was een gedragsbeperking, geen technische. Het technische werk was zorgen dat we het haalden in de slechtste kliniek, op de slechtste verbinding, op de slechtste zaterdag.

Het offline-first contract

Dit deel staat nooit in de demovideo. Een Leidse dierenkliniek heeft dikke muren, een röntgenruimte die 2,4 GHz verstoort, en een glasvezellijn die zo nu en dan negentig seconden uitvalt terwijl de ISP heronderhandelt. De PWA moet blijven werken als het netwerk dat niet doet. De Background Synchronization API regelt de happy path: elk formulier wordt eerst lokaal weggeschreven, geeft succes terug aan de UI, en wordt richting de server afgespeeld zodra de verbinding terug is.

De medicatiecheck heeft een eigen contract. Als de agent de kennisbank niet binnen 4,5 seconden bereikt, toont het formulier een gele "alleen offline check"-indicator en valt het terug op een lokaal gecachte subset van het formularium met de top 60 chirurgische middelen die de keten daadwerkelijk gebruikt. De dierenarts ziet meteen dat de check gedeeltelijk was. We hebben die degradatie zichtbaar gemaakt in plaats van verstopt. Een stille fallback is erger dan een ontbrekende functie: als de agent op verouderde data draait en de dierenarts weet het niet, dan heb je een aansprakelijkheid gebouwd, geen tool. Die zichtbaarheid is wat de medisch verantwoordelijke comfortabel hield met het überhaupt opleveren van het offline-pad.

De uitrol, kliniek voor kliniek

We rolden één kliniek per twee weken uit. De eerste kliniek vroeg vijf weken aan kleine fixes voordat de assistenten niet meer naar het klembord grepen. De zesde kostte vier dagen. Het verschil zat niet in de software. De praktijkmanager leerde hoe ze de tool moest introduceren. Ze stopte met zeggen "dit vervangt je intakeformulier" en begon te zeggen "dit is je intakeformulier, op een tablet, dat zichzelf invult." De tweede framing hield stand.

Rond de derde kliniek leerden we ook dat we de verkeerde undo-flow hadden gebouwd. Als een assistente op verzenden tikte en de eigenaar meteen iets corrigeerde (meestal het gewicht van de hond), kon je het dossier wel aanpassen, maar de agent had al gedraaid op de pre-correctiedata en de flag was verouderd. We hebben een venster van 90 seconden toegevoegd waarin het record nog muteerbaar is, met automatische hercheck. Dat had er vanaf dag één in moeten zitten. Het zat er niet in, omdat het ontwerp aannam dat het formulier één atomaire actie was. Het formulier is geen atomaire actie. Het is een gesprek van vijf minuten dat eindigt in een save.

De cijfers na tien weken

Dit zijn de cijfers van de keten zelf, gemeten door hun praktijkmanager op een steekproef die ze met de stopwatch hebben opgenomen. Geen branchegemiddelden, geen extrapolaties, niet het soort getal dat je opschaalt met een gok. Eén klant, tien weken, met een stopwatch.

  • Gemiddelde intakeduur: van 6,5 minuten per patiënt naar 2,1 minuten, gemeten op 80 consulten voor en na.
  • Preoperatieve interactieflags die de agent oppikte en die de dierenarts nog niet had genoteerd: 14 in tien weken. Elf oranje (overleggen en doorgaan), drie rood (vervangmiddel).
  • Overtyptijd op de receptie: in de praktijk nul. Intakedata landt via de server-side webhook in het PMS op het moment dat het formulier synchroniseert.
  • Bespaarde dierenartstijd per week, ketenbreed: ongeveer 18 uur. Bespaarde receptietijd: ongeveer 26 uur.

Er is niemand ontslagen. Twee receptiefuncties verschoven naar klantcommunicatie, waar de keten een echte achterstand had (terugbelacties na operatie, vaccinatieherinneringen, het soort werk dat tussen wal en schip viel op een papieren dag). Die herverdeling is, eerlijk gezegd, het cijfer waar de praktijkmanager het meest om geeft.

Wat we anders zouden doen

Drie dingen, kort. We zouden het muteerbare verzendvenster vanaf dag één bouwen in plaats van het naleveren als patch. We zouden de offline-indicator in de header van het formulier zetten in plaats van naast de medicatiecheck, omdat assistenten verbinding associëren met het hele dossier, niet met één veld. En we zouden er eerder in investeren dat de medisch verantwoordelijke van de keten het gezicht van de uitrol is, in plaats van wij. Software-adoptie binnen een klinisch team is een kwestie van wie het vraagt, niet van wat er gevraagd wordt.

Toen we dit voor de Leidse keten bouwden, was het ongelikte offline-gedrag het deel van het project dat ons het vertrouwen opleverde om de agent te mogen uitrollen. Assistenten adopteren geen tool die de eerste keer dat de wifi knippert al faalt. Wil je zien hoe wij dit soort werk aanpakken: onze pagina over AI-agents beschrijft de architectuur uitgebreider. Het kleinste wat je deze week kunt doen: tik tien consulten lang je eigen intaketijd af met een stopwatch en schrijf het getal op een geeltje. Zit je boven de vier minuten, dan heb je een project dat het scopen waard is.

Kern

De agent neemt de beslissing niet. De agent zet een bronvermelding voor het oog van de dierenarts in de vier seconden waarin de beslissing valt.

FAQ

Waarom een PWA en geen native iPad-app?

Eén codebase, geen App Store-review bij elke fix, volledige offline-ondersteuning via een service worker, en directe installatie vanuit de browser. De eerdere native app van de keten deed er twaalf seconden over om een dossier te laden en assistenten haakten af.

Hoe blijft de kennisbank-agent actueel bij nieuwe geneesmiddelen?

Het corpus is geversioneerd. De medisch verantwoordelijke van de keten beoordeelt toevoegingen maandelijks. Nieuwe middelen en protocollen worden aan het eigen formularium toegevoegd, waarna de cache bij de volgende laadbeurt invalideert.

Wat als de agent een flag verkeerd zet?

De dierenarts tekent het recept, niet de agent. Bij elke flag hoort een bronvermelding die in één tik opent. Het systeem logt zowel de flag als de beslissing van de dierenarts voor klinische audit.

Schaalt deze architectuur naar een grotere keten?

De technische architectuur wel. Het lastiger schaalbare deel is het uitrolplaybook: de framing van de praktijkmanager was net zo belangrijk als de code. Plan daarop, niet alleen op engineering.

mobile appsai agentsragknowledge basecase studyworkflow

Iets bouwen?

Start een project