Files
triqura-ecd/docs/architecture/intent-system-architectuur-nl.md
2026-07-09 23:16:28 +02:00

67 KiB
Raw Permalink Blame History

Intent-Driven Architectuur voor Zorgtoepassingen

Type: Architectuur Referentiedocument Versie: 1.0 Datum: 2026-02-14 Doelgroep: LLM-agents, developers, architecten die op dit systeem bouwen Taal: Nederlands (tekst), Engels (technische termen en code)


Leeswijzer

Dit document beschrijft een intent-driven architectuur voor zorgtoepassingen. Het is geschreven om zowel door mensen als door LLM's geconsumeerd te worden. Elke sectie volgt een vast patroon: wat het onderdeel doet, waarom het bestaat, hoe het werkt, en welke trade-offs er zijn gemaakt. Codevoorbeelden illustreren concepten — het zijn geen implementatiespecificaties.

De architectuur is in de kern domein-agnostisch (elke toepassing die natuurlijke taal vertaalt naar gestructureerde acties), maar maakt specifieke keuzes voor de zorgcontext waar veiligheid, traceerbaarheid en klinische relevantie niet-onderhandelbaar zijn.


1. Probleemstelling

1.1 De kernspanning

Zorgverleners denken en communiceren in ongestructureerde taal: "Jan heeft vanochtend zijn medicatie geweigerd." Zorgsystemen vereisen gestructureerde input: het juiste formulier selecteren, de juiste categorie, de juiste patiënt, op het juiste moment.

De kloof tussen deze twee modi is waar productiviteit verloren gaat. Elke formulierinvulling, elke menunavigatie, elke contextwisseling van klinisch denken naar systeembediening is frictie die tijd wegneemt van patiëntenzorg.

1.2 Wat deze architectuur oplost

Deze architectuur biedt een patroon voor het bouwen van een command interface die tussen natuurlijke taalinput en gestructureerde systeemacties zit. Het is geen chatbot (conversatie is niet het doel), geen zoekmachine (zoeken is één actie van vele), en geen autonoom agent (de mens beslist altijd).

Het doet drie dingen:

  1. Begrijpen — Classificeer wat de gebruiker wil (intent) en extraheer de relevante parameters (entities)
  2. Voorbereiden — Open het juiste formulier met de juiste data vooringevuld, klaar voor menselijke bevestiging
  3. Voorstellen — Stel na afronding van een actie klinisch relevante vervolgacties voor op basis van protocollen

1.3 Ontwerpbeperkingen vanuit de zorg

Deze beperkingen zijn niet-onderhandelbaar en vormen elke architectuurbeslissing:

Beperking Implicatie
Geen autonome schrijfacties Het systeem schrijft nooit naar het patiëntendossier zonder expliciete menselijke bevestiging
Traceerbaarheid Elke classificatie, elke actie, elke suggestie moet traceerbaar zijn
Graceful degradation Als AI niet beschikbaar is, moet het systeem blijven functioneren (verminderde capaciteit, geen uitval)
Snelheid voor routinetaken 70%+ van de interacties is routine en moet in milliseconden opgelost worden, niet seconden
Domeinvocabulaire is stabiel Medische termen, dienstpatronen, rapportagecategorieën veranderen langzaam — dit bevoordeelt rule-based aanpakken
Privacy by design Patiëntdata in AI-prompts moet geminimaliseerd worden; logs moeten gesanitiseerd worden

2. Architectuuroverzicht

2.1 De vijf bouwblokken

Het systeem bestaat uit vijf componenten, elk met een enkele verantwoordelijkheid. Ze worden beschreven in de volgorde waarin data erdoorheen stroomt.

Gebruikersinput (tekst of spraak)
        │
        ▼
┌─────────────────────────┐
│   1. INTENT REGISTRY    │  Declaratieve definities — de single source of truth
│      (statische config) │  voor alle intents, patronen, entities en metadata
└────────────┬────────────┘
             │ levert definities aan ↓
┌─────────────────────────┐
│   2. CLASSIFICATION     │  Tweefasen-pipeline: snelle lokale matching (Reflex)
│      PIPELINE           │  met LLM fallback (Orchestrator) voor complexe input
└────────────┬────────────┘
             │ produceert geclassificeerd intent + ruwe entities ↓
┌─────────────────────────┐
│   3. ENTITY RESOLUTION  │  Grondt ruwe entities tegen echte data:
│                         │  "Jan" → Patiënt #427, "morgen" → 2026-02-15
└────────────┬────────────┘
             │ produceert resolved intent klaar voor actie ↓
┌─────────────────────────┐
│   4. ACTION SYSTEM      │  Mapt intent → UI artifact (formulier, weergave, navigatie)
│                         │  Mens bevestigt → systeem voert uit → resultaat
└────────────┬────────────┘
             │ na succesvolle actie ↓
┌─────────────────────────┐
│   5. PROTOCOL ENGINE    │  Evalueert klinische regels tegen voltooide actie
│      (Nudge)            │  Stelt vervolgacties voor met protocolreferenties
└─────────────────────────┘

2.2 Waarom deze volgorde ertoe doet

De pipeline is strikt sequentieel, en daar is een reden voor. Elke stap produceert een meer verfijnde representatie:

  • Ruwe input → (Classification) → Intent + ruwe entities → (Resolution) → Gegrond intent → (Action) → Voorbereide UI → (Mens bevestigt) → Uitgevoerde actie → (Protocol) → Voorgestelde vervolgactie

Geen stap kan worden overgeslagen. Classificatie zonder resolution produceert ongegronde entities ("Jan" zonder te weten welke Jan). Resolution zonder classificatie heeft niets om te resolven. Acties zonder menselijke bevestiging schenden zorgveiligheidseisen. Protocol-evaluatie zonder voltooide acties heeft geen trigger.

2.3 Wat NIET in deze architectuur zit

Beslissingen over wat uit te sluiten zijn net zo belangrijk als wat erin zit:

Uitgesloten Reden
Custom ML-model voor classificatie Regex + LLM fallback presteert beter dan custom modellen voor <50 intents in een stabiel domeinvocabulaire. Geen onderhoud van trainingsdata nodig.
Event bus / pub-sub Overkill voor single-tenant applicaties. Directe functie-aanroepen en store subscriptions zijn eenvoudiger en even effectief.
Autonome agent-uitvoering Zorg vereist human-in-the-loop voor alle dossierwijzigingen. Het systeem bereidt voor; de mens beslist.
Ambient capture Passief meeluisteren tijdens patiëntgesprekken is een fundamenteel ander inputmodel. Deze architectuur behandelt expliciete commando's (getypt of gesproken). Ambient capture is een toekomstige uitbreiding, geen fundamentwijziging.
FHIR-server Een FHIR-geïnspireerd datamodel is voldoende. Een volledige FHIR-compliant server bouwen is een apart project dat de intent-architectuur niet raakt.
Knowledge graph voor entity resolution Simpele database-lookups (fuzzy naamzoekactie, datum-parsing) zijn voldoende bij <1000 patiënten. Knowledge graphs voegen waarde toe op schaal maar zijn premature complexiteit voor MVP.

3. Bouwblok 1: Intent Registry

3.1 Doel

De Intent Registry is de single source of truth voor alle intent-definities. Elk ander onderdeel in het systeem leest uit de registry — niets definieert intent-gedrag onafhankelijk.

3.2 Waarom dit ertoe doet

Zonder registry drijven intent-definities uit elkaar. In het huidige prototype wordt een intent op vier aparte plekken gedefinieerd: de TypeScript type union, de Reflex pattern map, de Orchestrator system prompt, en de Chat API system prompt. Een nieuw intent toevoegen vereist gesynchroniseerde wijzigingen op alle vier. Dit is de primaire bron van architecturale schuld.

De registry elimineert dit door de definitie te centraliseren en afgeleide artifacts (pattern maps, prompts, validatieschema's) te genereren vanuit één enkele declaratie.

3.3 Anatomie van een Intent Definition

interface IntentDefinition {
  // === Identiteit ===
  id: string;                    // Unieke identifier, bijv. "dagnotitie"
  label: string;                 // Leesbare naam (Nederlands), bijv. "Dagnotitie"
  description: string;           // Wat dit intent doet (gebruikt in AI-prompts)
  priority: 'P1' | 'P2' | 'P3'; // Classificatieprioriteit

  // === Lokale classificatie (Reflex) ===
  reflexPatterns: Array<{
    pattern: RegExp;
    weight: number;              // 0.5 (zwak signaal) tot 1.0 (exacte match)
  }>;

  // === Entity Schema ===
  entities: {
    required: string[];          // Moet aanwezig zijn voor actie-uitvoering
    optional: string[];          // Verrijkt de actie als aanwezig
    extractionRules: Array<{     // Regex met named capture groups
      pattern: RegExp;
      mapping: Record<string, string>; // capture group → entity-veld
    }>;
  };

  // === AI-classificatie ===
  aiExamples: Array<{
    input: string;               // Voorbeeld gebruikersinput
    expectedEntities: Record<string, string>;
  }>;

  // === Action Binding ===
  artifactType: string | null;   // Welk UI artifact te openen (null = alleen navigatie)
  requiresConfirmation: boolean; // Moet gebruiker bevestigen voor uitvoering?

  // === Toegangscontrole (voorbereid, niet afgedwongen in MVP) ===
  allowedRoles?: string[];       // Leeg = iedereen

  // === Feature Management ===
  featureFlag?: string;          // Optionele vlag om aan/uit te zetten
  enabled: boolean;              // Hoofdschakelaar
}

3.4 Wat de Registry genereert

Andere componenten benaderen de registry niet direct voor hun kernlogica. In plaats daarvan biedt de registry generatorfuncties:

Functie Afnemer Wat het produceert
getReflexPatterns() Classification Pipeline (Reflex-fase) Map van intent → pattern/weight-paren
getExtractionRules() Entity Resolution Map van intent → regex-extractieregels
buildClassificationPrompt() Classification Pipeline (Orchestrator-fase) System prompt-sectie met alle intents, beschrijvingen en voorbeelden
buildChatPrompt() Chat API System prompt-sectie voor conversationele context
getEntitySchema(intentId) Action System Zod-validatieschema voor entity-validatie
getArtifactMapping() Action System Map van intent → artifact type

3.5 Trade-off: Declaratief vs. Gedistribueerd

Overwogen alternatief: Elk intent als zelfstandige module (bestand per intent, co-located patronen + prompt + component). Dit is het "feature folder" patroon.

Waarom we voor gecentraliseerde registry kozen: In een zorgcontext doen de relaties tussen intents ertoe. Ambiguïteitdetectie vereist het vergelijken van confidence scores over intents heen. RBAC-filtering vereist iteratie over alle intents. Promptgeneratie heeft de volledige catalogus nodig. Een gecentraliseerde registry maakt deze cross-cutting concerns natuurlijk. De kosten zijn dat het toevoegen van een intent één groot bestand raakt in plaats van een nieuw bestand aan te maken — maar dat ene bestand is het enige bestand dat je hoeft aan te raken.

Wanneer te heroverwegen: Als het systeem groeit voorbij ~50 intents wordt het registry-bestand onhandelbaar. Op dat punt: opsplitsen in categorie-gebaseerde sub-registries (klinisch, administratief, navigatie) die samenvoegen tot één runtime registry.


4. Bouwblok 2: Classification Pipeline

4.1 Doel

De Classification Pipeline neemt ruwe gebruikersinput en produceert een geclassificeerd intent met confidence score en ruwe entities. Het is het enkele toegangspunt voor alle classificatie — zowel directe API-calls als chatinteracties gebruiken dezelfde pipeline.

4.2 Tweefasen-ontwerp

Input + Context
      │
      ▼
┌──────────────────────────────────────────────────────┐
│  Fase 1: REFLEX (lokaal, <20ms)                      │
│                                                      │
│  1. Controleer escalatie-triggers                    │
│     - Multi-intent signalen ("en", "daarna", "dan")  │
│     - Voornaamwoorden ("hij", "zij", "hem", "haar")  │
│     - Relatieve tijd ("morgen", "volgende week")     │
│     → Bij trigger: overslaan naar Fase 2 met gok    │
│                                                      │
│  2. Patroonmatching tegen registry-patronen          │
│     - Score elk intent op hoogste matchend gewicht   │
│     - Track top-2 voor ambiguïteitdetectie           │
│     → Als confidence ≥ 0.7 EN verschil > 0.1: klaar │
│     → Anders: escaleer naar Fase 2                   │
│                                                      │
│  3. Entity-extractie via registry extractieregels    │
│     → Koppel ruwe entities aan resultaat             │
└──────────────────────────────────────────────────────┘
      │
      │ (alleen als escalatie nodig)
      ▼
┌──────────────────────────────────────────────────────┐
│  Fase 2: ORCHESTRATOR (AI, ~250ms-3s)                │
│                                                      │
│  1. Bouw prompt vanuit registry (intent-beschrij-    │
│     vingen + voorbeelden + entity schemas)           │
│  2. Voeg applicatiecontext toe:                      │
│     - Actieve patiënt (naam, recente notities)       │
│     - Huidige dienst (nacht/ochtend/middag/avond)    │
│     - Afspraken vandaag                              │
│     - Recente intents (voor voornaamwoordresolutie)  │
│  3. Stuur naar LLM met gestructureerd outputformaat  │
│  4. Valideer response met Zod-schema                 │
│  5. Bouw IntentChain (enkele of meervoudige actie)   │
│                                                      │
│  Bij falen: val terug op Reflex-resultaat,           │
│  cap confidence op 0.6                               │
└──────────────────────────────────────────────────────┘
      │
      ▼
ClassificationResult {
  intent, confidence, rawEntities,
  chain (bij multi-action),
  source ('reflex' | 'orchestrator' | 'fallback'),
  processingTimeMs
}

4.3 Waarom twee fasen in plaats van één

Kosten: Een LLM API-call kost geld en tijd. Bij 100 classificaties per uur over een organisatie wordt pure-LLM classificatie duur. Reflex handelt 70%+ lokaal af tegen nul marginale kosten.

Latency: Reflex reageert in <20ms. De LLM doet er 250ms-3s over. Voor routinecommando's ("notitie jan medicatie") hoeft de gebruiker niet op AI te wachten.

Veerkracht: Als de LLM-provider plat ligt, werkt Reflex nog steeds. Het systeem degradeert van "intelligent" naar "snel en betrouwbaar" in plaats van volledig te falen.

Debugbaarheid: Regex-patronen zijn deterministisch en inspecteerbaar. Als een classificatie fout is, kun je precies traceren welk patroon matchte en waarom. LLM-reasoning is probabilistisch en lastiger te debuggen.

4.4 Escalatie-triggers

De Reflex-fase controleert op escalatie-triggers vóór de patroonmatching. Dit is een bewuste keuze: als de input signalen bevat die AI-reasoning vereisen (meerdere acties, voornaamwoorden, relatieve tijd), heeft het geen zin een lokale hypothese te bouwen die onvolledig zal zijn.

Trigger Detectie Waarom AI nodig is
Multi-intent Voegwoorden: "en", "daarna", "dan", "ook" Reflex kan samengestelde zinnen niet splitsen
Voornaamwoorden "hij", "zij", "hem", "haar", "die", "deze" Vereist context (actieve patiënt, recente acties) om te resolven
Relatieve tijd "morgen", "overmorgen", "volgende week", dagnamen Vereist huidige datum + agenda-awareness

4.5 Het Unified Pipeline Principe

Zowel de Classify API (directe classificatie) als de Chat API (conversationele interactie) gebruiken dezelfde pipeline. Het verschil:

  • Classify API: Retourneert het pipeline-resultaat direct als gestructureerde data
  • Chat API: Geeft het pipeline-resultaat als context aan een conversationeel LLM, dat een natuurlijke taalrespons genereert naast de gestructureerde actie

Dit betekent dat het Chat-LLM niet hoeft te classificeren — het ontvangt de classificatie als input en hoeft alleen een antwoord te formuleren. Dit elimineert het probleem van twee aparte classificatiesystemen met divergerende intent-definities.

                    ┌─────────────────────┐
                    │ Classification      │
                    │ Pipeline            │
                    │ (enkele impl.)      │
                    └──────┬──────────────┘
                           │
              ┌────────────┴────────────┐
              ▼                         ▼
    ┌──────────────────┐     ┌──────────────────┐
    │ Classify API     │     │ Chat API         │
    │ Retourneert      │     │ Geeft resultaat  │
    │ resultaat als    │     │ als context aan  │
    │ JSON             │     │ LLM voor         │
    └──────────────────┘     │ conversatie      │
                             └──────────────────┘

4.6 Circuit Breaker voor AI-veerkracht

De Orchestrator wikkelt AI-calls in een simpele circuit breaker:

State Machine:
  CLOSED (normaal) ──[3 opeenvolgende fouten]──▶ OPEN (bypass AI)
  OPEN ──[30 seconden verstreken]──▶ HALF-OPEN (probeer één request)
  HALF-OPEN ──[succes]──▶ CLOSED
  HALF-OPEN ──[fout]──▶ OPEN

Wanneer het circuit open is, worden alle classificaties alleen door Reflex afgehandeld (met confidence gecapped op 0.6). Dit voorkomt cascade-fouten wanneer de AI-provider problemen heeft — in plaats van elk request 5 seconden te laten wachten op timeout, valt het systeem direct terug.

4.7 Trade-off: Regex vs. Embeddings voor Fase 1

Huidige keuze: Regex-patronen met gewichten.

Alternatief: Sentence embeddings (bijv. via een lokaal model of API) die input matchen tegen intent-beschrijvingen op basis van semantische gelijkenis.

Waarom regex voor nu: Het domeinvocabulaire in de zorg (met name GGZ) is stabiel en beperkt. "Notitie", "overdracht", "afspraak" zijn geen ambigue woorden. Regex vangt deze patronen met bijna perfecte nauwkeurigheid en nul latency. Embeddings voegen infrastructuurcomplexiteit toe (model hosting of API-calls) zonder proportionele nauwkeurigheidsverbetering bij <50 intents.

Wanneer te heroverwegen: Als de Reflex hit rate onder de 60% zakt — wat betekent dat meer dan 40% van de input AI-escalatie vereist — dekken de patronen het vocabulaire niet meer. Op dat punt wordt een embedding-based eerste pass (top-10 kandidaat-intents selecteren, dan LLM voor definitieve classificatie) waardevol. Dit is de architectuur die Voiceflow succesvol in productie heeft gebenchmarkt.


5. Bouwblok 3: Entity Resolution

5.1 Doel

Entity Resolution neemt de ruwe entities uit classificatie (strings als "jan", "morgen", "medicatie") en grondt ze tegen echte systeemdata (Patiënt #427, 2026-02-15, categorie "medicatie").

5.2 Waarom dit een aparte stap is

In het huidige prototype zijn entity-extractie en resolution vermengd — een deel gebeurt in de Reflex, een deel in de Orchestrator, een deel in de UI-componenten. Door het een expliciete pipeline-stap te maken, winnen we:

  1. Testbaarheid: Resolution-logica kan unit-getest worden met mock data
  2. Herbruikbaarheid: Dezelfde resolution-logica bedient zowel Classify als Chat paden
  3. Uitbreidbaarheid: Een naamzoekactie vervangen door een FHIR Patient search vereist alleen aanpassing van deze laag
  4. Separation of concerns: Classificatie bepaalt wat de gebruiker wil; resolution grondt wie/wanneer/wat ze bedoelen

5.3 Resolution-typen

Entity-type Ruwe waarde Resolution-methode Resolved waarde
Patiëntnaam "jan" Fuzzy search tegen patiëntdatabase { id: "427", name: "Jan de Vries" }
Datum/tijd "morgen 14:00" Datum-parsing met Nederlandse locale + huidige datum { date: "2026-02-15", time: "14:00" }
Categorie "medicatie", "med", "medicijn" Alias-mapping naar canonieke waarden "medicatie"
Navigatiedoel "risico", "diagnose" Enum matching "risicotaxatie"
Dienst "ochtend" Directe mapping "ochtend" (07:00-12:00)

5.4 Disambiguatie

Wanneer resolution ambigu is (meerdere patiënten genaamd "Jan"), produceert het systeem een ClarificationRequest:

interface ClarificationRequest {
  type: 'patient_disambiguation' | 'appointment_disambiguation' | 'time_disambiguation';
  question: string;       // "Welke Jan bedoel je?"
  options: Array<{
    label: string;        // "Jan de Vries (kamer 12)"
    value: string;        // patiënt-ID
  }>;
  originalIntent: string; // Bewaard voor retry na disambiguatie
  originalEntities: Record<string, unknown>; // Bewaarde ruwe entities
}

De UI presenteert de opties. Nadat de gebruiker selecteert, hervat de pipeline met de resolved entity. Het oorspronkelijke intent en de entities worden bewaard zodat de gebruiker zijn commando niet hoeft te herhalen.

5.5 Contextbewuste resolution

Sommige entities resolven anders afhankelijk van applicatiecontext:

  • "Hij"/"zij" → resolvet naar activePatient uit de context store
  • "De afspraak" → resolvet naar de meest recente of volgende afspraak in context
  • "Vandaag" → resolvet naar huidige datum, maar rapportages vóór 07:00 horen bij de dienst van de vorige dag

Deze contextbewustheid is wat de Orchestrator (AI) noodzakelijk maakt voor sommige input — de Reflex-fase kan contextafhankelijke verwijzingen niet resolven.

5.6 Trade-off: Inline vs. Expliciete Resolution

Overwogen alternatief: Elke UI-component entities laten resolven bij mount (huidige aanpak — componenten halen patiëntdata op wanneer ze een naamstring ontvangen).

Waarom expliciete resolution: In een multi-action chain ("notitie jan en verzet zijn afspraak") moet de patiënt die in actie 1 resolved is dezelfde patiënt zijn die in actie 2 gebruikt wordt. Inline resolution per component kan inconsistenties produceren. Expliciete resolution op pipeline-niveau garandeert één resolution-pass voor de hele chain.


6. Bouwblok 4: Action System

6.1 Doel

Het Action System mapt resolved intents naar UI artifacts (formulieren, weergaven, navigatiedoelen) en beheert de execution lifecycle. Het is de brug tussen "het systeem begrijpt wat je wilt" en "het systeem toont je het juiste formulier."

6.2 De scheiding Intent-Action

Een intent is wat de gebruiker bedoelt. Een action is wat het systeem doet. Deze scheiding is belangrijk omdat:

  • Eén intent kan mappen naar verschillende acties afhankelijk van context (bijv. "overdracht" opent een samenvattingsweergave bij dienststart maar genereert een nieuwe samenvatting bij diensteinde)
  • Eén gebruikersuiting kan meerdere acties produceren (IntentChain)
  • Actions hebben lifecycle-states die intents niet hebben (pending → confirming → executing → success/failed)

6.3 Action-typen

Er zijn drie typen acties, elk met ander UI-gedrag:

Type Wat het doet UI-gedrag Voorbeeld
Artifact Action Opent een formulier of dataweergave Opent artifact-panel met vooringevulde data dagnotitie, create_appointment
Query Action Haalt data op en toont deze Opent read-only artifact risico_query, diagnose_query, agenda_query
Navigation Action Navigeert naar een pagina Client-side navigatie, geen artifact intake_navigeer

6.4 Het bevestigingsprincipe

Geen actie die patiëntdata wijzigt wordt uitgevoerd zonder menselijke bevestiging. Dit is het fundamentele veiligheidsprincipe.

In de praktijk betekent dit:

  • Hoge confidence (≥0.9), niet-destructief: Open formulier met vooringevulde data. Gebruiker controleert en verzendt.
  • Gemiddelde confidence (0.7-0.9): Open formulier met vooringevulde data + expliciete bevestigingsbanner: "Ik begrijp: dagnotitie voor Jan, categorie medicatie. Klopt dit?"
  • Lage confidence (0.5-0.7): Stel verduidelijkingsvraag vóór het openen van wat dan ook.
  • Zeer lage confidence (<0.5): "Ik begrijp het niet. Kun je het anders zeggen?"
  • Destructieve acties (annuleren, verwijderen): Vereisen altijd expliciete bevestiging, ongeacht confidence.

Het systeem vult voor maar verzendt nooit automatisch. De mens ziet wat het systeem begrepen heeft en beslist of het correct is.

6.5 IntentChain: Multi-Action Execution

Wanneer de Orchestrator meerdere intents detecteert in één uiting ("Annuleer de afspraak van Jan en maak een notitie over zijn medicatie"), produceert het een IntentChain — een geordende reeks acties.

interface IntentChain {
  id: string;
  originalInput: string;
  actions: IntentAction[];       // Geordende reeks
  status: 'pending' | 'executing' | 'completed' | 'partial' | 'failed';
  meta: {
    source: 'reflex' | 'orchestrator' | 'fallback';
    processingTimeMs: number;
    aiReasoning?: string;        // Uitleg van de Orchestrator (voor debugging)
  };
}

Uitvoering is strikt sequentieel: actie 2 start pas nadat actie 1 voltooid is (of de gebruiker actie 1 bevestigd heeft). Parallelle uitvoering wordt bewust niet ondersteund — de gebruiker verwacht een logische volgorde, en in de zorg kan de volgorde van acties klinisch relevant zijn (bijv. annuleren vóór opnieuw inplannen).

6.6 Trade-off: Pre-fill vs. Auto-Execute

Overwogen alternatief: Bij zeer hoge confidence classificaties (≥0.95) het formulier overslaan en direct uitvoeren (de notitie wegschrijven, de afspraak aanmaken).

Waarom we kozen voor alleen pre-fill: In de zorg is de kost van een foute schrijfactie hoog (onjuist patiëntendossier) terwijl de kost van een extra bevestigingsklik laag is (fractie van een seconde). De risico-rendementsverhouding bevoordeelt sterk het vereisen van bevestiging. Bovendien dient het tonen van het vooringevulde formulier als transparantiemechanisme — de gebruiker ziet precies wat het systeem begrepen heeft, wat vertrouwen opbouwt over tijd.

Uitzondering om te overwegen voor de toekomst: Read-only queries (agenda_query, risico_query) zouden zonder bevestiging uitgevoerd kunnen worden omdat ze geen data wijzigen. Dit is een redelijke optimalisatie maar geen prioriteit.


7. Bouwblok 5: Protocol Engine (Nudge)

7.1 Doel

De Protocol Engine evalueert klinische regels na een succesvolle actie en stelt relevante vervolgacties voor. Het is de proactieve intelligentie van het systeem — dingen voorstellen waar de gebruiker niet om gevraagd heeft maar die klinisch relevant zijn op basis van protocollen.

7.2 Waarom dit de differentiator is

Elke chatbot kan intents classificeren. Elk formuliersysteem kan data voorinvullen. Wat een zorg-command-interface daadwerkelijk nuttig maakt is domeinintelligentie: weten dat na wondzorgdocumentatie een vervolgwondcontrole ingepland moet worden over 3 dagen volgens V&VN-richtlijnen. Dit is waar het systeem overgaat van "snellere data-invoer" naar "clinical decision support."

7.3 Regelstructuur

interface ProtocolRule {
  id: string;
  name: string;                    // Leesbare regelnaam

  // Wanneer te triggeren
  trigger: {
    intent: string;                // Welk voltooid intent deze regel triggert
    conditions: Condition[];       // ALLE moeten matchen (AND-logica)
  };

  // Wat voor te stellen
  suggestion: {
    intent: string;                // Het voorgestelde vervolg-intent
    message: string;               // Gebruikersgerichte suggestietekst (Nederlands)
    prefillEntities: (completedAction: Action) => Record<string, unknown>;
  };

  // Klinische onderbouwing
  protocol?: {
    name: string;                  // "V&VN Richtlijn Wondzorg"
    reference: string;             // "§4.2 Controlebeleid"
    rationale: string;             // Waarom deze suggestie ertoe doet
  };

  priority: 'low' | 'medium' | 'high';
  expiresAfterMs: number;         // Suggestie verdwijnt na deze tijd
  enabled: boolean;
}

interface Condition {
  field: string;                   // Entity-veld om te controleren
  operator: 'equals' | 'contains' | 'matches' | 'exists';
  value?: string;                  // Verwachte waarde (niet nodig voor 'exists')
}

7.4 Engine-ontwerp: Pure Function

De engine is een pure function: (completedAction, rules) → suggestions[]. Het heeft geen side effects, geen state, en geen kennis van waar regels vandaan komen. Dit maakt het:

  • Testbaar: Geef mock-acties en -regels mee, assert op suggesties
  • Portable: Regels kunnen komen uit hardcoded TypeScript-objecten (MVP), een database-tabel (v2), of FHIR PlanDefinitions (enterprise)
  • Voorspelbaar: Dezelfde input produceert altijd dezelfde output

7.5 Integratiepunt

De Protocol Engine draait na actievoltooiing, getriggerd door het Action System:

Actie succesvol voltooid
        │
        ▼
evaluateNudge(completedAction, protocolRules)
        │
        ▼
NudgeSuggestion[] (gesorteerd op prioriteit)
        │
        ▼
UI toont suggestie-toast met:
  - Bericht ("Wondcontrole inplannen over 3 dagen?")
  - Protocolreferentie ("V&VN Wondzorg §4.2")
  - Accepteren-knop → opent nieuw artifact met vooringevulde entities
  - Afwijzen-knop → logt afwijzing voor analytics

7.6 Voorbeeldregels (Zorg / GGZ)

Regel Trigger Suggestie Protocol
Wondzorg-opvolging dagnotitie + inhoud bevat "wond" Wondcontrole inplannen over 3 dagen V&VN Wondzorg §4.2
Medicatiewijziging-review dagnotitie + inhoud bevat "medicatie" + "gewijzigd" Medicatie-evaluatie inplannen over 1 week FMS Polyfarmacie §3.1
Incident-opvolging dagnotitie + categorie = "incident" Incidentrapport aanmaken IGJ Meldcode §2
Risico-herbeoordeling risico_query bekeken Risicoherbeoordeling inplannen als >30 dagen oud GGZ Standaarden §5.4
Intake-voltooiing intake_status bekeken + ontbrekende secties Navigeer naar volgende onvolledige sectie Intakeprotocol

7.7 Trade-off: Rules Engine vs. AI-gegenereerde suggesties

Overwogen alternatief: In plaats van regelgebaseerde suggesties, het LLM contextuele suggesties laten genereren op basis van de voltooide actie en patiëntgeschiedenis.

Waarom we kozen voor regels: Klinische suggesties moeten traceerbaar zijn naar specifieke protocollen. Wanneer een verpleegkundige ziet "Wondcontrole inplannen over 3 dagen," moet ze weten waarom — en het antwoord moet een protocolreferentie zijn, niet "de AI vond het een goed idee." Regelgebaseerde suggesties zijn deterministisch, auditbaar en klinisch onderbouwd. AI-gegenereerde suggesties zijn creatief maar klinisch niet verifieerbaar.

Hybride toekomst: De rules engine kan aangevuld worden met een AI-laag die nieuwe regels voorstelt op basis van patronen in actielogs. Deze voorgestelde regels worden dan beoordeeld door klinisch personeel en toegevoegd aan de regelset als ze geldig zijn. De engine zelf blijft regelgebaseerd; AI draagt bij aan regeldiscovery, niet regeluitvoering.

7.8 Pad naar standaarden: CDS Hooks

De Protocol Engine is conceptueel aligned met de HL7 CDS Hooks specificatie — een standaard voor Clinical Decision Support in de zorg. De mapping:

Cortex-concept CDS Hooks equivalent
ProtocolRule CDS Service
Trigger (intent + conditions) CDS Hook (workflow trigger)
NudgeSuggestion CDS Card (suggestion type)
Protocol metadata Card.source

Migratie naar CDS Hooks-compatibiliteit is niet nodig voor MVP maar architectureel mogelijk: regels uitdrukken als FHIR PlanDefinitions, triggers mappen naar CDS hook-types, output formatteren als CDS Cards. Dit maakt integratie mogelijk met ziekenhuis-CDS-systemen die al HL7 spreken.


8. Cross-Cutting Concerns

8.1 State Management

Het systeem vereist state management voor vijf aparte domeinen. Dit moeten aparte stores zijn (geen monolithische store) om testbaarheid en single responsibility te behouden:

Store Verantwoordelijkheid Belangrijkste state
Context Store Applicatiecontext Actieve patiënt, dienst, recente patiënten
Chat Store Conversationele state Berichten, streaming status, pending action
Artifact Store UI artifact-beheer Open artifacts (max 3), actief artifact
Action Store Intent chain-uitvoering Actieve chain, chain-geschiedenis
Nudge Store Protocolsuggesties Actieve suggesties, accept/dismiss tracking

Stores communiceren via subscriptions, niet via directe imports. Voorbeeld: wanneer de Action Store een chain voltooit, triggert de subscription van de Nudge Store de protocol-evaluatie.

8.2 Observability

Elke classificatie produceert een gestructureerd log-event:

interface ClassificationEvent {
  timestamp: string;
  userId: string;
  input: string;              // Gesanitiseerd (PII verwijderd of gehasht)
  intent: string;
  confidence: number;
  source: 'reflex' | 'orchestrator' | 'fallback';
  escalationReason?: string;
  processingTimeMs: number;
  entities: Record<string, unknown>;  // Gesanitiseerd
}

Belangrijke metrics om te tracken:

Metric Waarom het ertoe doet
Reflex hit rate Als <60%, moeten patronen uitgebreid worden of embeddings overwogen
Orchestrator fallback rate Hoog percentage = AI is onbetrouwbaar, circuit breaker-effectiviteit
Classificatie-latency (P95) Gebruikerservaring-indicator
Intent-distributie Laat zien welke features daadwerkelijk gebruikt worden
Nudge-acceptatieratio per regel Geeft aan of suggesties klinisch nuttig zijn

8.3 Audit Trail

In de zorg moet elke systeeminteractie met patiëntcontext auditbaar zijn:

interface AuditEvent {
  userId: string;
  action: 'classify' | 'resolve' | 'execute' | 'nudge_shown' | 'nudge_accepted' | 'nudge_dismissed';
  intent?: string;
  patientId?: string;         // Welke patiënt betrokken was
  input?: string;             // Gesanitiseerd
  result?: string;            // Samenvatting, niet volledige response
  timestamp: string;
  sessionId: string;          // Voor correlatie binnen één gebruikerssessie
  durationMs?: number;
}

Opslagvereisten: append-only tabel, geen UPDATE of DELETE, minimaal 5 jaar bewaring (NEN 7510-compliance voor Nederlandse zorg).

8.4 Privacy: PII in AI-prompts

Wanneer de Orchestrator context naar het LLM stuurt, bevat dit patiëntnamen en mogelijk klinische informatie. Mitigaties:

  1. Minimaliseer context: Stuur alleen wat nodig is voor classificatie (patiëntnaam, niet volledige medische geschiedenis)
  2. Data processing agreement: Zorg dat de DPA van de AI-provider garandeert dat API-data niet gebruikt wordt voor training
  3. Log-sanitisatie: Alle log-events moeten patiëntnamen en identifiers strippen of hashen vóór opslag
  4. Pseudonimisatie-optie: Voor hoog-beveiligde omgevingen, vervang patiëntnamen door tokens vóór verzending naar AI, resolv tokens na classificatie

8.5 Role-Based Access (Voorbereid, niet afgedwongen)

De Intent Registry bevat een allowedRoles-veld per intent. Dit maakt toekomstige RBAC-filtering mogelijk zonder architectuurwijzigingen:

Input → Classification Pipeline → [RBAC Filter] → Action System
                                        │
                                        └─ Als intent niet toegestaan voor gebruikersrol:
                                           → "Je hebt geen toegang tot deze functie"

Het filter zit tussen classificatie en actie: het systeem classificeert het intent (zodat het weet wat geprobeerd werd) maar blokkeert uitvoering als de rol van de gebruiker het niet toestaat. De geblokkeerde poging wordt gelogd voor auditdoeleinden.


9. Schaalbaarheidsoverwegingen

9.1 Intents schalen: van 11 naar 50+

Het huidige systeem heeft 11 intents. De architectuur ondersteunt groei naar 50+ met deze overwegingen:

Dimensie Bij 11 intents Bij 50 intents Mitigatie
Reflex matching ~88 regex-evaluaties (<20ms) ~400 evaluaties (~50ms) Keyword pre-filter: match eerste woord tegen index, evalueer alleen kandidaat-intents
AI-prompt grootte ~2000 tokens ~8000 tokens Categorie-gebaseerde prompt-secties; neem alleen relevante categorieën op basis van Reflex pre-classificatie
Registry-bestandsgrootte ~300 regels ~1500 regels Splits in categorie-sub-registries die samenvoegen bij runtime
Ambiguïteit Laag (intents zijn onderscheidend) Hoger (meer overlappende patronen) Vergroot patroonspecificiteit; voeg disambiguatieregels toe

9.2 Gebruikers schalen

Component Bottleneck Mitigatie
Reflex Geen (stateless, in-process) Schaalt met applicatie-instanties
Orchestrator AI-provider rate limits Circuit breaker voorkomt cascade; overweeg provider load balancing
Rate limiting Momenteel in-memory (verloren bij herstart) Verplaats naar database-backed (Supabase-tabel of Redis)
Audit logging Schrijfvolume Batch writes; async logging pipeline

9.3 AI Provider Independence

Het systeem is momenteel gekoppeld aan Anthropic Claude. Om vendor lock-in te verminderen:

interface LLMProvider {
  classify(input: string, systemPrompt: string, context: string): Promise<string>;
  stream(messages: Message[], systemPrompt: string): AsyncIterable<string>;
}

Twee implementaties (Anthropic, OpenAI) achter één interface. Let op: prompts zijn NIET volledig portable tussen providers — de system prompt is geoptimaliseerd voor het gedrag van Claude. Provider-wisseling vereist prompt-hertuning. Dit is inherent aan LLM-gebruik en kan niet volledig geabstraheerd worden.


10. Relatie met zorgstandaarden

10.1 FHIR Resource Mapping

Geëxtraheerde entities mappen uiteindelijk naar FHIR-geïnspireerde resources:

Entity FHIR Resource Gebruik
patientName / patientId Patient Patiëntidentificatie
category + content Observation of DocumentReference Klinische notities
datetime + appointmentType Encounter / Appointment Planning
diagnosis / risk Condition Klinische data-queries

Het systeem hoeft geen FHIR-server te zijn. Het heeft een duidelijke mapping-laag nodig zodat wanneer FHIR-compliance vereist wordt, entity resolution FHIR-compliant referenties kan produceren.

10.2 NEN 7510 (Informatiebeveiliging in de zorg)

Eis Hoe deze architectuur het adresseert
Toegangscontrole RBAC voorbereid in Intent Registry; Supabase Auth + RLS op data
Audit logging Append-only audit events voor alle interacties
Geen PII in logs Sanitisatiestap in logging pipeline
Encryptie in transit HTTPS (standaard voor moderne webapps)
Encryptie at rest Database-niveau (Supabase managed)
Incidentregistratie Classificatiefouten gelogd als potentiële beveiligingsincidenten

11. Migratiepad vanuit huidig prototype

Fase 1: Fundament (Laag risico)

Doel: Intent Registry als single source of truth + entity-extractie in Reflex

  1. Maak intent-registry.ts met alle 11 huidige intents als declaratieve objecten
  2. Refactor reflex-classifier.ts om patronen uit registry te laden in plaats van hardcoded map
  3. Voeg entity-extractieregels toe aan registry; implementeer extractie in Reflex-fase
  4. Genereer Orchestrator-prompt vanuit registry (vervang hardcoded prompt)
  5. Verifieer: alle bestaande classificaties produceren dezelfde resultaten (regressietest)

Risico: Laag — refactoring zonder gedragswijziging (behalve toegevoegde entity-extractie).

Fase 2: Unificatie (Gemiddeld risico)

Doel: Enkele classificatie-pipeline die zowel Classify als Chat API's bedient

  1. Extraheer classifyInput() als standalone pipeline-functie
  2. Pas Chat API aan om pipeline te gebruiken voor classificatie (verwijder dubbele intent-logica uit chat-prompt)
  3. Vereenvoudig Chat system prompt tot alleen conversationele rol (geen classificatieverantwoordelijkheid)
  4. Fix AbortController signal propagation (momenteel losgekoppeld van daadwerkelijke fetch)
  5. Voeg circuit breaker toe aan Orchestrator

Risico: Gemiddeld — Chat-gedrag kan subtiel veranderen. Vereist grondig testen met bestaande scenario's.

Fase 3: Activatie (Laag risico)

Doel: Nudge-systeem daadwerkelijk actief; state management opgeschoond

  1. Splits monolithische Zustand store in 5 domein-stores
  2. Koppel evaluateNudge() om te vuren na actievoltooiing (via store subscription)
  3. Voeg 3-5 klinische protocolregels toe
  4. Implementeer nudge tracking (getoond/geaccepteerd/afgewezen)

Risico: Laag — nieuwe functionaliteit, geen breaking changes aan bestaande features.

Fase 4: Productiegereedheid (Laag risico)

Doel: Observeerbaar, auditbaar, veerkrachtig

  1. Gestructureerde logging (vervang console.log)
  2. Classificatie-metrics tracking
  3. Audit events naar database
  4. Database-backed rate limiting
  5. PII-sanitisatie in logs

Risico: Laag — cross-cutting infrastructuur, geen functionaliteitswijzigingen.


12. Decision Log

Deze sectie documenteert de belangrijkste architectuurbeslissingen met hun rationale, voor toekomstige referentie.

D1: Drielagenmodel (Reflex → Orchestrator → Nudge)

Beslissing: Gebruik een tiered classificatiemodel met lokaal snel pad en AI fallback, plus protocol-gedreven post-action suggesties.

Rationale: Optimaliseert voor het veelvoorkomende geval (70%+ routinecommando's lokaal afgehandeld in <20ms) terwijl AI-capaciteit behouden blijft voor complexe input. Nudge-laag voegt domeinwaarde toe voorbij classificatie. Dit patroon is gevalideerd door industriebenchmarks (Voiceflow 2025) die laten zien dat hybride NLU+LLM zowel pure NLU als pure LLM overtreft op kosten én nauwkeurigheid.

Afgewezen alternatieven: Pure LLM (te traag en duur voor routinecommando's), pure rule-based (kan multi-intent, voornaamwoorden, relatieve tijd niet aan), custom ML-model (trainingsdata-overhead zonder proportionele nauwkeurigheidswinst in stabiel domein).

D2: Human-in-the-Loop voor alle schrijfacties

Beslissing: Het systeem schrijft nooit naar patiëntendossiers zonder expliciete menselijke bevestiging. Vul formulieren voor, verzend nooit automatisch.

Rationale: Zorg vereist verantwoording. De kost van een foute schrijfactie (onjuist patiëntendossier) overtreft verre de kost van een extra bevestigingsklik. Dit bouwt ook gebruikersvertrouwen op — het systeem is transparant over wat het begrepen heeft.

Afgewezen alternatieven: Auto-uitvoering bij hoge-confidence classificaties (>0.95). Afgewezen omdat zelfs bij 99% nauwkeurigheid, 1 foute schrijfactie per 100 interacties onacceptabel is in een klinische context.

D3: Gecentraliseerde Intent Registry boven Feature Folders

Beslissing: Eén registry-bestand boven een bestand-per-intent modulestructuur.

Rationale: Cross-cutting concerns (ambiguïteitdetectie, RBAC-filtering, promptgeneratie) hebben de volledige intent-catalogus nodig. Een gecentraliseerde registry maakt dit natuurlijk. De kosten (één groot bestand) zijn beheersbaar bij <50 intents en kunnen later opgesplitst worden in sub-registries.

D4: Regex boven Embeddings voor Fase 1 Classificatie

Beslissing: Gebruik regex-patronen met gewichten voor de Reflex-fase.

Rationale: Domeinvocabulaire is stabiel en beperkt (medische termen, vaste werkprocessen). Regex bereikt bijna perfecte nauwkeurigheid voor bekende patronen bij nul latency. Embeddings voegen infrastructuurcomplexiteit toe zonder proportionele nauwkeurigheidsverbetering bij de huidige schaal.

Heroverweging-trigger: Reflex hit rate zakt onder 60%.

D5: Regelgebaseerde Protocol Engine boven AI-gegenereerde suggesties

Beslissing: Nudge-suggesties zijn deterministisch, gebaseerd op expliciet gedefinieerde protocolregels.

Rationale: Klinische suggesties moeten traceerbaar zijn naar specifieke protocollen. Regelgebaseerde suggesties zijn auditbaar en verifieerbaar. AI-gegenereerde suggesties zijn creatief maar klinisch niet verifieerbaar.

Toekomstige hybride: AI stelt kandidaat-regels voor op basis van patronen in actielogs; klinisch personeel beoordeelt en keurt goed; engine blijft regelgebaseerd.

D6: Sequentiële Multi-Intent Execution boven Parallel

Beslissing: Acties in een IntentChain worden sequentieel uitgevoerd, nooit parallel.

Rationale: Gebruikers verwachten logische ordening ("annuleer afspraak EN maak notitie" impliceert eerst annuleren). In de zorg kan actievolgorde klinisch relevant zijn. Sequentiële uitvoering is voorspelbaar en debugbaar.


13. Platformmodel: Van Intent-Systeem naar Zorgtoepassing

13.1 De vier lagen

Het complete systeem bestaat uit vier lagen. Het intent-systeem (dit document) is laag 1. Daarbovenop komen drie lagen die samen een zorgtoepassing vormen.

┌─────────────────────────────────────────────────────────────────┐
│                                                                 │
│   LAAG 4: EPD-APPLICATIE                                        │
│   De toepassing die de zorgverlener ziet en gebruikt            │
│                                                                 │
│   Rapportage │ Dossier │ Agenda │ Overdracht │ Intake │ ...    │
│   UI-modules, block-componenten, API-routes, datamodel          │
│                                                                 │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│   LAAG 3: ZORGDOMEIN-CONFIGURATIE                               │
│   De kennis van het specifieke zorgtype                         │
│                                                                 │
│   Intent-definities (dagnotitie, overdracht, agenda, ...)      │
│   Entity-schemas (patiënt, categorie, dienst, ...)             │
│   Domeinvocabulaire (GGZ-termen, rapportagecategorieën)        │
│   Protocol-regels (nudges specifiek voor dit zorgtype)         │
│                                                                 │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│   LAAG 2: KENNISLAAG                                            │
│   Externe databronnen die de Protocol Engine voeden            │
│                                                                 │
│   ┌───────────┐ ┌──────────────┐ ┌────────────┐ ┌───────────┐ │
│   │ Wet- en   │ │ Behandel-    │ │ Kwaliteits-│ │ EPD-data  │ │
│   │ regel-    │ │ protocollen  │ │ documenten │ │ (eigen    │ │
│   │ geving    │ │              │ │            │ │  patiënt- │ │
│   │           │ │              │ │            │ │  data)    │ │
│   └───────────┘ └──────────────┘ └────────────┘ └───────────┘ │
│                                                                 │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│   LAAG 1: INTENT-SYSTEEM (dit document)                         │
│   De generieke motor                                            │
│                                                                 │
│   Intent Registry │ Classification Pipeline │ Entity Resolution │
│   Action System │ Protocol Engine (Nudge)                       │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

13.2 Waarom de Kennislaag ertoe doet

De Protocol Engine (Bouwblok 5) genereert nudges op basis van regels. Maar die regels komen niet uit de lucht vallen — ze zijn afgeleid van externe kennisbronnen. De kwaliteit van de nudges is direct afhankelijk van de kwaliteit en toegankelijkheid van deze bronnen.

Zonder de Kennislaag is de Protocol Engine een lege motor: de mechanica werkt, maar er is geen brandstof. De Kennislaag is die brandstof.

13.3 De vier kennisbronnen

13.3.1 Wet- en regelgeving

Wetten en normen die het handelen in de zorg reguleren. Deze bepalen wat er moet gebeuren.

Bron Voorbeeld Type nudge dat eruit voortkomt
Wlz (Wet langdurige zorg) Zorgplanvereisten, evaluatietermijnen "Zorgplan evaluatie is over 2 weken verlopen"
Wvggz (Wet verplichte GGZ) Meldplicht, termijnen dwangmaatregelen "Dwangmaatregel verloopt over 3 dagen — verlenging nodig?"
Wkkgz (Wet kwaliteit, klachten en geschillen zorg) Incidentmeldplicht "Incident geregistreerd — IGJ-melding vereist binnen 3 werkdagen"
NEN 7510 / AVG Dossierbewaartermijnen, toegangslogging "Audit log: 3 medewerkers hebben dit dossier ingezien"
Wmcz (Wet medezeggenschap cliënten zorginstellingen) Cliëntenraad-informatieplicht (Niet direct nudge-relevant, maar compliance-check)

Kenmerken: Verandert langzaam (wetswijzigingen), hoge betrouwbaarheid, publiek beschikbaar, juridisch bindend.

Technische implicatie: Deze regels zijn stabiel genoeg om als hardcoded protocolregels in de engine te zitten. Ze veranderen misschien 1-2x per jaar. Een database-backed regelsysteem is handig maar niet strikt noodzakelijk.

13.3.2 Behandelprotocollen

Klinische richtlijnen en standaarden die beschrijven hoe zorg geleverd zou moeten worden.

Bron Voorbeeld Type nudge dat eruit voortkomt
GGZ Standaarden Zorgstandaard Depressie, Psychose, etc. "Patiënt met depressie — ROM-meting gepland over 6 weken?"
V&VN Richtlijnen Wondzorg, medicatiebeheer, valpreventie "Wondcontrole inplannen over 3 dagen (V&VN §4.2)"
FMS Richtlijnen Polyfarmacie, pijnbehandeling "Medicatie-evaluatie over 1 week (FMS Polyfarmacie §3.1)"
Multidisciplinaire richtlijnen MDO-frequentie, behandelplanbesprekingen "MDO gepland over 2 weken — voorbereiding starten?"
NHG Standaarden (Bij samenwerking met huisarts) "Verwijsbrief huisarts — terugkoppeling binnen 4 weken"
Zorginstituut Nederland Kwaliteitskaders langdurige zorg "Jaarlijkse kwaliteitsmeting — deadline over 6 weken"

Kenmerken: Verandert regelmatig (nieuwe richtlijnversies), hoge betrouwbaarheid, publiek beschikbaar (ggzstandaarden.nl, V&VN), vereist interpretatie door klinisch personeel.

Technische implicatie: Deze bronnen zijn te omvangrijk en te genuanceerd om handmatig als regels te coderen. Hier komt een RAG-component (Retrieval Augmented Generation) in beeld: de protocolteksten worden geïndexeerd, en bij relevante acties wordt de LLM gevraagd om op basis van de protocoltekst een specifieke nudge-suggestie te formuleren. Die suggestie wordt vervolgens door klinisch personeel gevalideerd en als regel opgenomen.

Dit is het pad naar het "hybride model" beschreven in sectie 7.7: AI stelt regels voor, mensen valideren, de engine voert uit.

13.3.3 Kwaliteitsdocumenten

Interne documenten van de zorginstelling die beschrijven hoe deze specifieke organisatie zorg levert.

Bron Voorbeeld Type nudge dat eruit voortkomt
Instellingsprotocollen Huisprotocol medicatiedistributie "Dubbele controle medicatie vereist (huisprotocol §2.3)"
Werkafspraken Overdrachtsmomenten, rapportagefrequentie "Overdrachtsrapportage niet ingevuld — dienst eindigt over 1 uur"
Kwaliteitshandboek Auditmomenten, verbeterplannen "Interne audit volgende maand — actieplan bijwerken"
Accreditatie-eisen HKZ, PREZO, Planetree "Cliënttevredenheidsonderzoek — 3 patiënten nog niet bevraagd"

Kenmerken: Organisatie-specifiek (niet publiek), verandert bij beleidsbeslissingen, vereist interne afstemming, vaak in Word/PDF formaat.

Technische implicatie: Dit is de meest uitdagende bron omdat het per instelling verschilt. Bij onboarding van een nieuwe GGZ-instelling moeten hun interne protocollen geïndexeerd worden. Opties:

  1. Handmatig: Klinisch personeel vertaalt hun protocollen naar nudge-regels in de admin-interface. Arbeidsintensief maar nauwkeurig.
  2. Semi-automatisch: Upload documenten → RAG-indexering → AI genereert kandidaat-regels → klinisch personeel valideert. Sneller, maar vereist validatieworkflow.
  3. Hybride: Standaard-regelset (op basis van landelijke richtlijnen) + organisatie-specifieke aanvullingen.

13.3.4 EPD-data (eigen patiëntdata)

De data die al in het systeem zit — patiëntendossiers, rapportages, afspraken, behandelplannen.

Databron Voorbeeld Type nudge dat eruit voortkomt
Rapportagegeschiedenis Geen rapportage in 48 uur "Geen dagnotitie voor Jan de Vries in 2 dagen"
Behandelplan Evaluatiedatum verstreken "Behandelplanevaluatie Jan — 1 week over deadline"
Medicatieoverzicht Medicatie-interacties, wijzigingen "Nieuwe medicatie voorgeschreven — interactiecheck nodig"
Risicotaxatie Verlopen risicobeoordeling "Risicotaxatie Marie — laatste beoordeling 45 dagen geleden"
Agenda Gemiste afspraken, no-shows "Jan 2x no-show deze maand — contact opnemen?"
Lab/metingen ROM-scores, vitale waarden "PHQ-9 score gestegen van 12 naar 18 — aanpassing behandelplan?"

Kenmerken: Realtime, patiënt-specifiek, altijd beschikbaar (het zit in je eigen database), privacy-gevoelig.

Technische implicatie: Dit is de enige bron die geen externe integratie vereist — het zit al in Supabase. De Protocol Engine kan direct queries draaien:

// Voorbeeld: check of er recente rapportage is
interface DataDrivenCondition {
  type: 'query';
  table: 'reports';
  filter: { patient_id: '$activePatient', created_at: { gt: 'now() - interval 48 hours' } };
  expect: 'exists';  // Als NIET exists → trigger nudge
}

Dit is de eenvoudigste en meest impactvolle bron om mee te beginnen: je hebt de data al, de queries zijn simpel, en de nudges zijn direct relevant.

13.4 Hoe de Kennislaag de Protocol Engine voedt

┌─────────────────────────────────────────────────────────────────┐
│                     KENNISLAAG                                   │
│                                                                  │
│  ┌─────────────────┐     ┌─────────────────────────────────┐   │
│  │ Statische regels │     │ Dynamische regels                │   │
│  │                  │     │                                  │   │
│  │ Wet- en regel-   │     │ EPD-data queries                │   │
│  │ geving           │     │ (rapportage-gaps, verlopen      │   │
│  │ (hardcoded,      │     │  beoordelingen, no-shows)       │   │
│  │  wijzigt zelden) │     │                                  │   │
│  │                  │     │ Behandelprotocollen via RAG     │   │
│  │ Landelijke       │     │ (geïndexeerde richtlijnteksten  │   │
│  │ richtlijnen      │     │  → AI-gegenereerde kandidaat-   │   │
│  │ (V&VN, GGZ Std)  │     │  regels → menselijke validatie) │   │
│  │                  │     │                                  │   │
│  │ Instellings-     │     │ Kwaliteitsdocumenten            │   │
│  │ protocollen      │     │ (geüpload per instelling,       │   │
│  │ (bij onboarding  │     │  geïndexeerd, AI-voorgesteld)   │   │
│  │  geconfigureerd)  │     │                                  │   │
│  └────────┬─────────┘     └────────────┬────────────────────┘   │
│           │                             │                        │
│           ▼                             ▼                        │
│  ┌──────────────────────────────────────────────────────────┐   │
│  │              PROTOCOL RULES REGISTRY                      │   │
│  │                                                          │   │
│  │  Alle regels — ongeacht bron — in hetzelfde formaat:     │   │
│  │  trigger + conditions + suggestion + protocol metadata   │   │
│  │                                                          │   │
│  │  Elke regel bevat:                                       │   │
│  │  - source: 'wetgeving' | 'richtlijn' | 'instelling'    │   │
│  │           | 'epd_data' | 'ai_suggested'                 │   │
│  │  - validatedBy: userId (wie heeft de regel goedgekeurd) │   │
│  │  - validatedAt: timestamp                                │   │
│  │  - version: regelversie (voor audittrail)               │   │
│  └──────────────────────────┬───────────────────────────────┘   │
│                              │                                   │
└──────────────────────────────┼───────────────────────────────────┘
                               │
                               ▼
                    ┌──────────────────────┐
                    │   PROTOCOL ENGINE    │
                    │   (Bouwblok 5)       │
                    │                      │
                    │   evaluateNudge()    │
                    └──────────────────────┘

13.5 Implementatievolgorde voor de Kennislaag

De vier bronnen hebben zeer verschillende implementatiecomplexiteit. De volgorde:

Stap 1: EPD-data (eigen database) — Start hier. Geen externe integratie nodig. Supabase queries op je eigen tabellen. Voorbeelden: rapportage-gaps, verlopen beoordelingen, gemiste afspraken. Directe waarde, lage complexiteit. Dit zijn "data-driven nudges" die je kunt bouwen met wat je al hebt.

Stap 2: Wet- en regelgeving + landelijke richtlijnen — Hardcoded regels. Stabiele bronnen, publiek beschikbaar. Vertaal de belangrijkste wettelijke termijnen en richtlijn-aanbevelingen naar protocolregels. 20-30 regels die voor elke GGZ-instelling gelden. Eenmalig werk dat je voor alle klanten hergebruikt.

Stap 3: Instellingsprotocollen — Per klant configureerbaar. Bij onboarding van een nieuwe instelling: hun protocollen uploaden, vertalen naar regels (handmatig of semi-automatisch). Dit is het stuk dat de toepassing organisatie-specifiek maakt.

Stap 4: RAG op behandelprotocollen — AI-ondersteund. Indexeer de volledige tekst van GGZ Standaarden, V&VN richtlijnen, etc. Gebruik RAG om bij relevante acties protocolteksten op te halen en kandidaat-nudges te genereren. Klinisch personeel valideert. Dit is het meest geavanceerde stuk — maar ook het meest schaalbare, omdat je hiermee honderden regels kunt genereren die je handmatig nooit zou uitschrijven.

13.6 Trade-off: Regels vs. RAG

Aspect Hardcoded regels RAG-gegenereerde suggesties
Nauwkeurigheid 100% (menselijk gevalideerd) ~80-90% (AI-gegenereerd, moet gevalideerd)
Schaalbaarheid Laag (handmatig per regel) Hoog (automatisch uit documenten)
Traceerbaarheid Perfect (regel → protocol §) Goed (RAG-bron + AI-reasoning)
Opstartkosten Laag per regel, hoog voor volledigheid Hoog voor setup, laag per regel daarna
Onderhoud Handmatig bij richtlijnwijzigingen Semi-automatisch (herindexeren + hervalideren)

Aanbeveling: Begin met hardcoded regels (stap 1-3). Bouw RAG als de regelset groeit voorbij wat handmatig beheersbaar is (~50+ regels per instelling). Het regelformaat is hetzelfde — de bron verandert, de engine niet.

13.7 De Kennislaag als product-differentiator

Dit is waar het commercieel interessant wordt. De Kennislaag is wat het verschil maakt tussen:

  • Generiek intent-systeem: "Ik begrijp je commando en open het juiste formulier" — dat kan iedereen bouwen.
  • Zorg-specifiek platform: "Ik weet dat na wondzorg een controle nodig is over 3 dagen volgens V&VN §4.2, en dat de Wlz vereist dat het zorgplan binnen 6 weken geëvalueerd wordt" — dat vereist domeinkennis.
  • Instelling-specifiek systeem: "Ik weet dat jullie huisprotocol vereist dat medicatie-uitgifte met twee personen gecontroleerd wordt, en dat de volgende interne audit over 3 weken is" — dat vereist organisatiekennis.

Elke laag voegt waarde toe. Het intent-systeem (laag 1) is de motor die iedereen kan bouwen. De Kennislaag (laag 2) is het vliegwiel dat moeilijk te kopiëren is.

13.8 Laag-scheiding en herbruikbaarheid

De vier lagen zijn bewust ontkoppeld:

Vervang je... Dan verandert... En blijft ongewijzigd...
Laag 4 (EPD-applicatie) UI, API-routes, datamodel Intent-systeem, Kennislaag, Domeinconfig
Laag 3 (Zorgdomein) Intents, entities, vocabulaire Intent-systeem, Kennislaag, EPD-applicatie
Laag 2 (Kennislaag) Protocolregels, databronnen Intent-systeem, Domeinconfig, EPD-applicatie
Laag 1 (Intent-systeem) Classificatie, resolution, actions Kennislaag, Domeinconfig, EPD-applicatie

Concreet voorbeeld: wil je van GGZ naar thuiszorg? Dan:

  • Laag 1 blijft identiek (dezelfde motor)
  • Laag 2 krijgt andere bronnen (thuiszorgprotocollen i.p.v. GGZ Standaarden, Wlz i.p.v. Wvggz)
  • Laag 3 krijgt andere intents (zorgmoment_registratie i.p.v. dagnotitie, route_planning i.p.v. agenda)
  • Laag 4 krijgt een andere UI (thuiszorg-app i.p.v. EPD-dashboard)

Appendix A: Begrippenlijst

Term Definitie
Intent Een geclassificeerde gebruikersintentie: wat de gebruiker wil doen (bijv. dagnotitie, create_appointment)
Entity Een parameter geëxtraheerd uit gebruikersinput (bijv. patiëntnaam, datum, categorie)
Confidence Een score (0.01.0) die aangeeft hoe zeker de classificatie is
Reflex De snelle, lokale classificatiefase met regex-patronen
Orchestrator De AI-gestuurde classificatiefase met een LLM
IntentChain Een geordende reeks acties afgeleid uit één gebruikersuiting
Artifact Een UI-paneel (formulier, dataweergave) dat geopend wordt als reactie op een intent
Block Een React-component dat een specifiek artifact-type rendert
Nudge Een proactieve suggestie gegenereerd door de Protocol Engine na actievoltooiing
Protocol Rule Een declaratieve regel die een nudge triggert op basis van voltooide actie + condities
Entity Resolution Het proces van het gronden van ruwe entity-strings tegen echte systeemdata
Escalation De beslissing om input van Reflex naar Orchestrator te sturen voor AI-classificatie
Circuit Breaker Een veerkrachtigheidspatroon dat AI-calls omzeilt na opeenvolgende fouten

Appendix B: Huidige Intent Catalogus

Intent Prioriteit Categorie Artifact Bevestiging
dagnotitie P1 Klinisch Formulier (notitie-aanmaak) Nee (alleen pre-fill)
zoeken P1 Navigatie Zoekresultaten-weergave Nee
overdracht P1 Klinisch Overdrachtssamenvatting Nee
agenda_query P2 Administratief Kalenderweergave Nee
create_appointment P2 Administratief Afspraakformulier Nee (pre-fill)
cancel_appointment P2 Administratief Bevestigingsdialoog Ja (destructief)
reschedule_appointment P2 Administratief Afspraakformulier Ja (wijzigend)
intake_status P3 Klinisch Intake-voortgangsweergave Nee
intake_navigeer P3 Navigatie Paginanavigatie Nee (geen artifact)
risico_query P3 Klinisch Risicobeoordeling-weergave Nee
diagnose_query P3 Klinisch Diagnoselijst-weergave Nee

Appendix C: Referenties