# 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

```typescript
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`:

```typescript
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.

```typescript
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

```typescript
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](https://cds-hooks.hl7.org/) 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:

```typescript
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:

```typescript
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:

```typescript
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:

```typescript
// 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.0–1.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

- [Voiceflow: Benchmarking Hybrid LLM Classification Systems](https://www.voiceflow.com/pathways/benchmarking-hybrid-llm-classification-systems) — Productiebenchmarks die laten zien dat hybride NLU+LLM pure aanpakken overtreft
- [HL7 CDS Hooks Specificatie](https://cds-hooks.hl7.org/) — Standaard voor Clinical Decision Support integratie
- [Microsoft Dragon Copilot](https://www.microsoft.com/en-us/health-solutions/clinical-workflow/dragon-copilot) — Grootste ambient AI klinisch documentatiesysteem (capture → classify → act → review patroon)
- [MediGRAF: Hybrid Graph RAG voor EPD](https://arxiv.org/abs/2602.00009) — Hybride gestructureerde + ongestructureerde bevraging van patiëntdata
- [NEN 7510](https://www.nen.nl/nen-7510-1-2017-nl-245399) — Nederlandse norm voor informatiebeveiliging in de zorg
