docs: add architecture and intake intent documentation
- Add architecture overview, implementation plan, and intent overview - Add intake intent process specs (gap analyse, bouwplan, testplan) - Add swift architecture specs and visualization prompts - Remove obsolete aispeedrun-manifesto template Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
296
docs/architectuur/architectuur-overzicht.md
Normal file
296
docs/architectuur/architectuur-overzicht.md
Normal file
@@ -0,0 +1,296 @@
|
||||
# Architectuur Overzicht — Mini-EPD
|
||||
|
||||
**Versie:** v1.0
|
||||
**Datum:** 4 februari 2026
|
||||
**Doelgroep:** Product owners, IT consultants, data scientists
|
||||
|
||||
---
|
||||
|
||||
## 1. Introductie
|
||||
|
||||
**Mini-EPD** is een elektronisch patiëntendossier voor de geestelijke gezondheidszorg. Het systeem combineert klassieke dossiervoering met een AI-gestuurde spraakinterface genaamd **Cortex**.
|
||||
|
||||
> **Elevator pitch:** Een EPD waarin zorgmedewerkers niet hoeven te klikken, maar gewoon zeggen wat ze willen doen. "Maak notitie voor Jan, medicatie gegeven" — en het gebeurt.
|
||||
|
||||
---
|
||||
|
||||
## 2. Technische Stack
|
||||
|
||||
| Laag | Technologie | Rol |
|
||||
|------|-------------|-----|
|
||||
| **Frontend** | Next.js 14, React, Tailwind CSS | Gebruikersinterface |
|
||||
| **Backend** | Next.js API Routes | Server-logica en API's |
|
||||
| **Database** | Supabase (PostgreSQL) | Opslag patiëntgegevens |
|
||||
| **Authenticatie** | Supabase Auth | Inloggen en toegangsbeheer |
|
||||
| **AI - Taal** | Claude (Anthropic) | Classificatie en samenvatting |
|
||||
| **AI - Spraak** | Deepgram | Spraak-naar-tekst |
|
||||
| **Hosting** | Vercel | Deployment en hosting |
|
||||
|
||||
---
|
||||
|
||||
## 3. Module-overzicht
|
||||
|
||||
Het systeem bestaat uit zes hoofdmodules:
|
||||
|
||||
| Module | Doel | Primaire gebruiker |
|
||||
|--------|------|-------------------|
|
||||
| **Cortex** | AI spraak/tekst commando's | Iedereen |
|
||||
| **Dashboard** | Overzicht werkzaamheden | Behandelaar |
|
||||
| **Patiënten** | Dossiers en intake | Iedereen |
|
||||
| **Verpleegrapportage** | Dagnotities en overdracht | Verpleegkundige |
|
||||
| **Agenda** | Afspraken en planning | Iedereen |
|
||||
| **Clients** | (Legacy, doorverwijzing) | — |
|
||||
|
||||
### Module-beschrijvingen
|
||||
|
||||
#### Cortex — AI Command Center
|
||||
Het "brein" van het systeem. Medewerkers geven spraak- of tekstcommando's, Cortex begrijpt de intentie en voert de actie uit. Bijvoorbeeld: "zoek marie" opent direct het zoekscherm met resultaten.
|
||||
|
||||
#### Dashboard
|
||||
Startpagina met overzicht van caseload, aandachtspunten en aankomende afspraken. Geeft behandelaars in één oogopslag zicht op hun werk.
|
||||
|
||||
#### Patiënten
|
||||
Beheer van alle patiënten en hun dossiers. Elk dossier bevat:
|
||||
- Basisgegevens (NAW, verzekering)
|
||||
- Screening (hulpvraag, documenten)
|
||||
- Intake (anamnese, diagnoses, risico's)
|
||||
- Behandelplan (doelen, interventies)
|
||||
- Rapportage (dagnotities)
|
||||
|
||||
#### Verpleegrapportage
|
||||
Twee functies:
|
||||
1. **Rapportage** — Invoer van dagelijkse observaties per patiënt
|
||||
2. **Overdracht** — AI-samenvatting van alle notities voor shift-wissel
|
||||
|
||||
#### Agenda
|
||||
Kalenderweergave van alle afspraken. Filtert per patiënt, dag of week.
|
||||
|
||||
---
|
||||
|
||||
## 4. Cortex — Diepere Uitwerking
|
||||
|
||||
Cortex is het onderscheidende element van dit EPD. Het werkt met drie lagen:
|
||||
|
||||
### Laag 1: Reflex Arc (Snelle herkenning)
|
||||
- **Wat:** Lokale patroonherkenning zonder AI
|
||||
- **Snelheid:** <20 milliseconden
|
||||
- **Wanneer:** Eenvoudige, veelvoorkomende commando's
|
||||
- **Voorbeeld:** "notitie jan" → herkent direct als "dagnotitie maken"
|
||||
|
||||
### Laag 2: Orchestrator (AI-classificatie)
|
||||
- **Wat:** Claude AI analyseert complexe invoer
|
||||
- **Snelheid:** 200-800 milliseconden
|
||||
- **Wanneer:** Meerdere acties, context nodig, onduidelijke input
|
||||
- **Voorbeeld:** "Zeg jan af en maak notitie griep" → herkent twee acties
|
||||
|
||||
### Laag 3: Nudge (Proactieve suggesties)
|
||||
- **Wat:** Suggesties na voltooide acties
|
||||
- **Wanneer:** Na opslaan van bepaalde notities
|
||||
- **Voorbeeld:** Notitie met "wond" → suggestie: "Wondcontrole inplannen?"
|
||||
|
||||
### Ondersteunde commando's (intents)
|
||||
|
||||
| Intent | Wat het doet | Voorbeeld |
|
||||
|--------|-------------|----------|
|
||||
| `dagnotitie` | Verpleegkundige notitie | "medicatie jan gegeven" |
|
||||
| `zoeken` | Patiënt zoeken | "zoek marie" |
|
||||
| `overdracht` | Overdracht openen | "overdracht" |
|
||||
| `agenda_query` | Afspraken bekijken | "afspraken vandaag" |
|
||||
| `create_appointment` | Afspraak maken | "plan intake jan morgen 14:00" |
|
||||
| `cancel_appointment` | Afspraak annuleren | "annuleer afspraak jan" |
|
||||
| `intake_status` | Intake voortgang | "wat moet ik nog doen?" |
|
||||
|
||||
### Cortex UI-opbouw
|
||||
|
||||
Het scherm is verticaal gesplitst:
|
||||
- **Links (40%):** Chat — conversatie met Cortex
|
||||
- **Rechts (60%):** Werkgebied — formulieren en lijsten
|
||||
|
||||
Sneltoetsen:
|
||||
- `Cmd/Ctrl + K` — Focus op invoerveld
|
||||
- `Cmd/Ctrl + Enter` — Verstuur commando
|
||||
- `Esc` — Sluit werkgebied
|
||||
|
||||
---
|
||||
|
||||
## 5. Data-architectuur
|
||||
|
||||
### Kernentiteiten
|
||||
|
||||
De database is georganiseerd rond de **patiënt** als centrale entiteit:
|
||||
|
||||
| Entiteit | Beschrijving |
|
||||
|----------|--------------|
|
||||
| **patients** | Basisgegevens patiënten |
|
||||
| **encounters** | Contactmomenten (afspraken, bezoeken) |
|
||||
| **observations** | Meetgegevens (vitals, symptomen) |
|
||||
| **conditions** | Diagnoses en aandoeningen |
|
||||
| **reports** | Alle notities en rapportages |
|
||||
| **intakes** | Intake-trajecten |
|
||||
| **care_plans** | Behandelplannen |
|
||||
| **risk_assessments** | Risico-evaluaties |
|
||||
| **practitioners** | Zorgverleners |
|
||||
|
||||
### Rapportage-types
|
||||
|
||||
Alle notities zitten in één tabel (`reports`) met een type-aanduiding:
|
||||
|
||||
| Type | Gebruik |
|
||||
|------|---------|
|
||||
| `verpleegkundig` | Dagelijkse zorgnotities |
|
||||
| `observatie` | Klinische waarnemingen |
|
||||
| `incident` | Incidenten/crises |
|
||||
| `voortgang` | Voortgangsnota's |
|
||||
| `medicatie` | Medicijnbeheer |
|
||||
| `contact` | Contactlogboek |
|
||||
|
||||
### API-groepen
|
||||
|
||||
De backend API's zijn logisch gegroepeerd:
|
||||
|
||||
| Groep | Functie |
|
||||
|-------|---------|
|
||||
| `/api/patients/*` | Patiëntgegevens |
|
||||
| `/api/reports/*` | Rapportages CRUD |
|
||||
| `/api/overdracht/*` | Shift-overdracht + AI-samenvatting |
|
||||
| `/api/intakes/*` | Intake-beheer |
|
||||
| `/api/cortex/*` | AI command center |
|
||||
| `/api/deepgram/*` | Spraakherkenning |
|
||||
|
||||
---
|
||||
|
||||
## 6. Diagrambeschrijvingen
|
||||
|
||||
Onderstaande beschrijvingen kun je gebruiken om visuele diagrammen te maken.
|
||||
|
||||
### Diagram A: Systeemoverzicht (Container)
|
||||
|
||||
**Componenten:**
|
||||
1. **Gebruiker** (persoon) — Zorgmedewerker met browser
|
||||
2. **Frontend** (container) — Next.js React applicatie
|
||||
3. **API Layer** (container) — Next.js API Routes
|
||||
4. **Database** (container) — Supabase PostgreSQL
|
||||
5. **Claude AI** (externe service) — Anthropic API
|
||||
6. **Deepgram** (externe service) — Spraak-naar-tekst API
|
||||
|
||||
**Verbindingen:**
|
||||
- Gebruiker → Frontend (HTTPS)
|
||||
- Frontend → API Layer (REST/SSE)
|
||||
- API Layer → Database (SQL via Supabase client)
|
||||
- API Layer → Claude AI (HTTPS, voor classificatie en samenvatting)
|
||||
- Frontend → Deepgram (WebSocket, voor live spraak)
|
||||
|
||||
---
|
||||
|
||||
### Diagram B: Cortex Flow (Sequence)
|
||||
|
||||
**Actoren:** Gebruiker, Frontend, Reflex Arc, Orchestrator (Claude), Database
|
||||
|
||||
**Flow:**
|
||||
1. Gebruiker spreekt/typt commando
|
||||
2. Frontend stuurt tekst naar API
|
||||
3. Reflex Arc probeert lokaal te classificeren
|
||||
4. **Als succesvol:** Retourneer intent + entiteiten
|
||||
5. **Als niet succesvol:** Escaleer naar Orchestrator
|
||||
6. Orchestrator (Claude) analyseert en retourneert intent chain
|
||||
7. Frontend toont juiste werkgebied (formulier/lijst)
|
||||
8. Gebruiker voltooit actie
|
||||
9. Data wordt opgeslagen in Database
|
||||
10. (Optioneel) Nudge evalueert en toont suggestie
|
||||
|
||||
---
|
||||
|
||||
### Diagram C: Module-relaties (Component)
|
||||
|
||||
**Modules en hun connecties:**
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────┐
|
||||
│ CORTEX │
|
||||
│ (kan alle andere modules aansturen via commando's) │
|
||||
└───────────────────────┬─────────────────────────────┘
|
||||
│
|
||||
┌───────────────┼───────────────┐
|
||||
▼ ▼ ▼
|
||||
┌───────────┐ ┌───────────────┐ ┌────────┐
|
||||
│ PATIËNTEN │◄──│ VERPLEEG- │ │ AGENDA │
|
||||
│ │ │ RAPPORTAGE │ │ │
|
||||
└─────┬─────┘ └───────────────┘ └────────┘
|
||||
│
|
||||
▼
|
||||
┌─────────────┐
|
||||
│ DASHBOARD │
|
||||
│ (overzicht) │
|
||||
└─────────────┘
|
||||
```
|
||||
|
||||
**Beschrijving:**
|
||||
- Cortex fungeert als "universele afstandsbediening"
|
||||
- Patiënten-module is de kern voor dossierdata
|
||||
- Verpleegrapportage leest en schrijft naar patiëntendossiers
|
||||
- Agenda toont afspraken gekoppeld aan patiënten
|
||||
- Dashboard aggregeert data uit alle modules
|
||||
|
||||
---
|
||||
|
||||
### Diagram D: Data-relaties (ERD vereenvoudigd)
|
||||
|
||||
**Entiteiten en relaties:**
|
||||
|
||||
```
|
||||
PATIENT (1) ──────< (N) ENCOUNTER
|
||||
│
|
||||
├──────< (N) OBSERVATION
|
||||
│
|
||||
├──────< (N) CONDITION
|
||||
│
|
||||
├──────< (N) REPORT
|
||||
│
|
||||
└──────< (N) INTAKE
|
||||
│
|
||||
├──── ANAMNESE
|
||||
├──── EXAMINATION
|
||||
├──── RISK_ASSESSMENT
|
||||
└──── CARE_PLAN
|
||||
```
|
||||
|
||||
**Leeswijzer:**
|
||||
- Eén patiënt heeft meerdere contactmomenten (encounters)
|
||||
- Eén patiënt heeft meerdere observaties, diagnoses en rapportages
|
||||
- Eén patiënt kan meerdere intake-trajecten doorlopen
|
||||
- Elk intake-traject bevat anamnese, onderzoek, risico's en behandelplan
|
||||
|
||||
---
|
||||
|
||||
## 7. Glossary
|
||||
|
||||
| Term | Betekenis |
|
||||
|------|-----------|
|
||||
| **EPD** | Elektronisch Patiënten Dossier |
|
||||
| **Cortex** | AI command center voor spraak/tekst commando's |
|
||||
| **Intent** | Gedetecteerde bedoeling achter een commando |
|
||||
| **Reflex Arc** | Snelle, lokale patroonherkenning (geen AI) |
|
||||
| **Orchestrator** | AI-laag voor complexe classificatie |
|
||||
| **Nudge** | Proactieve suggestie na een actie |
|
||||
| **Overdracht** | Shift-wissel met samenvatting van notities |
|
||||
| **RLS** | Row Level Security — database-beveiliging per gebruiker |
|
||||
| **Intake** | Opnameproces nieuwe patiënt |
|
||||
| **Anamnese** | Medische voorgeschiedenis |
|
||||
| **ROM** | Routine Outcome Monitoring — effectmeting behandeling |
|
||||
| **FHIR** | Internationale standaard voor zorgdata-uitwisseling |
|
||||
|
||||
---
|
||||
|
||||
## 8. Contactpunten voor verdieping
|
||||
|
||||
| Onderwerp | Waar te vinden |
|
||||
|-----------|----------------|
|
||||
| Functioneel ontwerp Cortex | `docs/swift/` |
|
||||
| API-documentatie | `app/api/` (code + comments) |
|
||||
| Database schema | `supabase/migrations/` |
|
||||
| UI componenten | `components/` |
|
||||
| Release notes | `docs/releasenotes/` |
|
||||
|
||||
---
|
||||
|
||||
*Dit document geeft een high-level overzicht. Voor technische implementatiedetails, raadpleeg de broncode of vraag het development team.*
|
||||
728
docs/architectuur/implementatieplan-nieuwe-intents.md
Normal file
728
docs/architectuur/implementatieplan-nieuwe-intents.md
Normal file
@@ -0,0 +1,728 @@
|
||||
# Implementatieplan — Nieuwe Intents Toevoegen
|
||||
|
||||
**Versie:** v1.0
|
||||
**Datum:** 4 februari 2026
|
||||
**Doelgroep:** Product owners, IT consultants, data scientists
|
||||
|
||||
---
|
||||
|
||||
## 1. Overzicht
|
||||
|
||||
Dit document beschrijft het stappenplan voor het toevoegen van een nieuwe intent aan Cortex. Een intent doorloopt **8 aanraakpunten** in de codebase — elk punt moet correct geconfigureerd zijn.
|
||||
|
||||
### Tijdsindicatie per Intent Type
|
||||
|
||||
| Type | Complexiteit | Bestanden |
|
||||
|------|--------------|-----------|
|
||||
| **Query Block** (data tonen) | Laag | 6-7 bestanden |
|
||||
| **Action Block** (data invoeren) | Middel | 7-8 bestanden |
|
||||
| **Navigatie Intent** (geen block) | Laag | 4-5 bestanden |
|
||||
|
||||
---
|
||||
|
||||
## 2. Beslisboom: Welk Type Intent?
|
||||
|
||||
```
|
||||
┌─────────────────────────┐
|
||||
│ Wat moet de intent doen?│
|
||||
└───────────┬─────────────┘
|
||||
│
|
||||
┌───────────────────────┼───────────────────────┐
|
||||
│ │ │
|
||||
▼ ▼ ▼
|
||||
┌───────────────┐ ┌───────────────┐ ┌───────────────┐
|
||||
│ Data TONEN │ │ Data INVOEREN │ │ NAVIGEREN │
|
||||
│ (read-only) │ │ (formulier) │ │ (route) │
|
||||
└───────┬───────┘ └───────┬───────┘ └───────┬───────┘
|
||||
│ │ │
|
||||
▼ ▼ ▼
|
||||
Query Block Action Block Navigation Intent
|
||||
|
||||
Voorbeelden: Voorbeelden: Voorbeelden:
|
||||
- risico_query - dagnotitie - intake_navigeer
|
||||
- diagnose_query - create_appointment
|
||||
- intake_status
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3. Stappenplan
|
||||
|
||||
### Stap 1: Types Definiëren
|
||||
|
||||
**Bestand:** `lib/cortex/types.ts`
|
||||
|
||||
**Wat te doen:**
|
||||
|
||||
1. Voeg intent toe aan `CortexIntent` type:
|
||||
```typescript
|
||||
export type CortexIntent =
|
||||
| 'dagnotitie'
|
||||
| 'zoeken'
|
||||
// ... bestaande intents
|
||||
| 'nieuwe_intent' // ← NIEUW
|
||||
| 'unknown';
|
||||
```
|
||||
|
||||
2. Voeg block config toe aan `BLOCK_CONFIGS` (alleen als er een block is):
|
||||
```typescript
|
||||
export const BLOCK_CONFIGS: Record<BlockType, BlockConfig> = {
|
||||
// ... bestaande configs
|
||||
nieuwe_intent: {
|
||||
type: 'nieuwe_intent',
|
||||
title: 'Nieuwe Intent Titel',
|
||||
size: 'md', // 'sm' | 'md' | 'lg' | 'full'
|
||||
icon: 'IconName', // Lucide icon naam
|
||||
},
|
||||
};
|
||||
```
|
||||
|
||||
3. Voeg eventuele nieuwe entities toe aan `ExtractedEntities`:
|
||||
```typescript
|
||||
export interface ExtractedEntities {
|
||||
// ... bestaande entities
|
||||
nieuwVeld?: string;
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Stap 2: Patterns Toevoegen (Reflex Arc)
|
||||
|
||||
**Bestand:** `lib/cortex/reflex-classifier.ts`
|
||||
|
||||
**Wat te doen:**
|
||||
|
||||
Voeg patterns toe aan `INTENT_PATTERNS`:
|
||||
|
||||
```typescript
|
||||
const INTENT_PATTERNS: Record<...> = {
|
||||
// ... bestaande patterns
|
||||
|
||||
nieuwe_intent: [
|
||||
// Exacte commando's (weight 1.0)
|
||||
{ pattern: /^trigger woord/i, weight: 1.0 },
|
||||
{ pattern: /^alternatief commando/i, weight: 1.0 },
|
||||
|
||||
// Sterke matches (weight 0.9)
|
||||
{ pattern: /^toon\s+(de\s+)?nieuwe/i, weight: 0.9 },
|
||||
|
||||
// Partiele matches (weight 0.7-0.8)
|
||||
{ pattern: /^nieuwe\b/i, weight: 0.7 },
|
||||
],
|
||||
};
|
||||
```
|
||||
|
||||
**Pattern weight richtlijnen:**
|
||||
|
||||
| Weight | Wanneer | Voorbeeld |
|
||||
|--------|---------|-----------|
|
||||
| `1.0` | Exacte, unieke trigger | `"risicotaxatie"` |
|
||||
| `0.9-0.95` | Sterke indicator | `"toon risico's"` |
|
||||
| `0.8-0.85` | Goede match | `"bekijk risico"` |
|
||||
| `0.7` | Partiele match | `"risico"` (kan ook andere dingen zijn) |
|
||||
| `< 0.7` | Vermijd | Leidt tot escalatie naar AI |
|
||||
|
||||
---
|
||||
|
||||
### Stap 3: AI Chat Integratie
|
||||
|
||||
**Bestand:** `app/api/cortex/chat/route.ts`
|
||||
|
||||
**Wat te doen:**
|
||||
|
||||
1. Voeg intent toe aan de system prompt (in `buildSystemPrompt()` functie):
|
||||
|
||||
```typescript
|
||||
// In de intents lijst:
|
||||
- **nieuwe_intent** — Korte beschrijving wat het doet
|
||||
- Triggers: "trigger 1", "trigger 2", "trigger 3"
|
||||
- Entities: veldNaam (type)
|
||||
- Actie: Beschrijf wat er gebeurt
|
||||
```
|
||||
|
||||
2. Voeg een voorbeeld toe:
|
||||
|
||||
```typescript
|
||||
### Voorbeeld N: Nieuwe Intent
|
||||
|
||||
**User:**
|
||||
"trigger zin"
|
||||
|
||||
**AI Response:**
|
||||
"Korte bevestiging van wat je gaat doen.
|
||||
|
||||
\`\`\`json
|
||||
{
|
||||
"type": "action",
|
||||
"intent": "nieuwe_intent",
|
||||
"entities": {
|
||||
"veldNaam": "waarde"
|
||||
},
|
||||
"confidence": 0.95,
|
||||
"artifact": {
|
||||
"type": "nieuwe_intent",
|
||||
"prefill": {
|
||||
"veldNaam": "waarde"
|
||||
}
|
||||
}
|
||||
}
|
||||
\`\`\`"
|
||||
```
|
||||
|
||||
**Let op:** De AI leert van voorbeelden. Zorg dat:
|
||||
- Het JSON format exact klopt
|
||||
- De confidence realistisch is (0.85-0.98)
|
||||
- De entities overeenkomen met wat je in types.ts hebt gedefinieerd
|
||||
|
||||
---
|
||||
|
||||
### Stap 4: Validatie Schema
|
||||
|
||||
**Bestand:** `lib/cortex/action-parser.ts`
|
||||
|
||||
**Wat te doen:**
|
||||
|
||||
1. Voeg intent toe aan `ActionSchema`:
|
||||
|
||||
```typescript
|
||||
const ActionSchema = z.object({
|
||||
type: z.literal('action'),
|
||||
intent: z.enum([
|
||||
'dagnotitie',
|
||||
'zoeken',
|
||||
// ... bestaande intents
|
||||
'nieuwe_intent', // ← NIEUW
|
||||
'unknown',
|
||||
]),
|
||||
// ... rest van schema
|
||||
});
|
||||
```
|
||||
|
||||
2. Voeg artifact type toe (als er een block is):
|
||||
|
||||
```typescript
|
||||
artifact: z.object({
|
||||
type: z.enum([
|
||||
'dagnotitie',
|
||||
'zoeken',
|
||||
// ... bestaande types
|
||||
'nieuwe_intent', // ← NIEUW
|
||||
'fallback',
|
||||
]),
|
||||
prefill: z.record(z.string(), z.any()),
|
||||
}).optional(),
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Stap 5: Routing Configureren
|
||||
|
||||
**Bestand:** `lib/cortex/action-parser.ts`
|
||||
|
||||
**Wat te doen:**
|
||||
|
||||
Voeg case toe aan `routeIntentToArtifact()`:
|
||||
|
||||
```typescript
|
||||
export function routeIntentToArtifact(
|
||||
intent: CortexIntent,
|
||||
entities: Record<string, any>,
|
||||
confidence: number
|
||||
): { type: BlockType; prefill: Record<string, any>; title: string } | null {
|
||||
|
||||
// ... bestaande cases
|
||||
|
||||
case 'nieuwe_intent':
|
||||
// Optioneel: check of vereiste entities aanwezig zijn
|
||||
if (!entities.vereistVeld) {
|
||||
return null; // Triggert clarification vraag
|
||||
}
|
||||
return {
|
||||
type: 'nieuwe_intent',
|
||||
title: 'Nieuwe Intent Titel',
|
||||
prefill: {
|
||||
veldNaam: entities.veldNaam,
|
||||
// ... andere prefill data
|
||||
},
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
**Voor navigatie intents:**
|
||||
```typescript
|
||||
case 'nieuwe_navigeer':
|
||||
// Return null → wordt afgehandeld in CommandCenter
|
||||
return null;
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Stap 6: API Route (Indien Nodig)
|
||||
|
||||
**Bestand:** `app/api/cortex/[domain]/route.ts`
|
||||
|
||||
**Wanneer nodig:** Als de block data moet ophalen van de server.
|
||||
|
||||
**Wat te doen:**
|
||||
|
||||
```typescript
|
||||
// app/api/cortex/nieuwe/route.ts
|
||||
|
||||
import { NextRequest, NextResponse } from 'next/server';
|
||||
import { z } from 'zod';
|
||||
import { createClient } from '@/lib/auth/server';
|
||||
|
||||
const QuerySchema = z.object({
|
||||
patientId: z.string().uuid(),
|
||||
optionalParam: z.string().optional(), // Let op: NIET .nullable()
|
||||
});
|
||||
|
||||
export async function GET(request: NextRequest) {
|
||||
const supabase = await createClient();
|
||||
const { data: { user } } = await supabase.auth.getUser();
|
||||
|
||||
if (!user) {
|
||||
return NextResponse.json({ error: 'Niet ingelogd' }, { status: 401 });
|
||||
}
|
||||
|
||||
const searchParams = request.nextUrl.searchParams;
|
||||
|
||||
// BELANGRIJK: Convert null naar undefined voor Zod
|
||||
const params = QuerySchema.safeParse({
|
||||
patientId: searchParams.get('patientId'),
|
||||
optionalParam: searchParams.get('optionalParam') || undefined, // ← NIET null!
|
||||
});
|
||||
|
||||
if (!params.success) {
|
||||
return NextResponse.json({ error: 'Ongeldige parameters' }, { status: 400 });
|
||||
}
|
||||
|
||||
// Data ophalen
|
||||
const { data, error } = await supabase
|
||||
.from('tabel_naam')
|
||||
.select('*')
|
||||
.eq('patient_id', params.data.patientId);
|
||||
|
||||
if (error) {
|
||||
return NextResponse.json({ error: error.message }, { status: 500 });
|
||||
}
|
||||
|
||||
return NextResponse.json({ data });
|
||||
}
|
||||
```
|
||||
|
||||
**Let op (Lesson Learned):**
|
||||
- `searchParams.get()` retourneert `null`, niet `undefined`
|
||||
- Zod's `.optional()` verwacht `undefined`
|
||||
- Gebruik altijd `|| undefined` bij optionele params
|
||||
|
||||
---
|
||||
|
||||
### Stap 7: Block Component
|
||||
|
||||
**Bestand:** `components/cortex/blocks/nieuwe-intent-block.tsx`
|
||||
|
||||
**Wat te doen:**
|
||||
|
||||
```typescript
|
||||
'use client';
|
||||
|
||||
/**
|
||||
* Nieuwe Intent Block
|
||||
*
|
||||
* Block voor [beschrijving].
|
||||
* Intent: nieuwe_intent
|
||||
*/
|
||||
|
||||
import { useCortexStore, type BlockPrefillData } from '@/stores/cortex-store';
|
||||
import { BlockContainer } from './block-container';
|
||||
import { BlockLoading, BlockError, BlockEmpty } from '../shared/block-states';
|
||||
import { useBlockData } from '@/lib/cortex/hooks/use-block-data';
|
||||
import { BLOCK_CONFIGS } from '@/lib/cortex/types';
|
||||
import { IconName } from 'lucide-react';
|
||||
|
||||
interface NieuweIntentBlockProps {
|
||||
prefill?: BlockPrefillData;
|
||||
}
|
||||
|
||||
interface NieuweIntentData {
|
||||
items: Array<{
|
||||
id: string;
|
||||
// ... velden
|
||||
}>;
|
||||
}
|
||||
|
||||
export function NieuweIntentBlock({ prefill }: NieuweIntentBlockProps) {
|
||||
const config = BLOCK_CONFIGS['nieuwe_intent'];
|
||||
const { activePatient } = useCortexStore();
|
||||
|
||||
const patientId = prefill?.patientId || activePatient?.id;
|
||||
|
||||
// Data ophalen
|
||||
const { data, isLoading, error, refetch } = useBlockData<NieuweIntentData>({
|
||||
endpoint: '/api/cortex/nieuwe',
|
||||
params: { patientId: patientId || '' },
|
||||
enabled: Boolean(patientId),
|
||||
});
|
||||
|
||||
// State: Geen context
|
||||
if (!patientId) {
|
||||
return (
|
||||
<BlockContainer title={config.title} size={config.size}>
|
||||
<BlockEmpty
|
||||
icon={IconName}
|
||||
message="Selecteer eerst een patiënt"
|
||||
/>
|
||||
</BlockContainer>
|
||||
);
|
||||
}
|
||||
|
||||
// State: Loading
|
||||
if (isLoading) {
|
||||
return (
|
||||
<BlockContainer title={config.title} size={config.size}>
|
||||
<BlockLoading message="Data laden..." />
|
||||
</BlockContainer>
|
||||
);
|
||||
}
|
||||
|
||||
// State: Error
|
||||
if (error) {
|
||||
return (
|
||||
<BlockContainer title={config.title} size={config.size}>
|
||||
<BlockError message={error} onRetry={refetch} />
|
||||
</BlockContainer>
|
||||
);
|
||||
}
|
||||
|
||||
// State: Data
|
||||
return (
|
||||
<BlockContainer title={config.title} size={config.size}>
|
||||
<div className="space-y-4">
|
||||
{/* Render je data hier */}
|
||||
{data?.items.map((item) => (
|
||||
<div key={item.id}>
|
||||
{/* Item content */}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</BlockContainer>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
**Shared components beschikbaar:**
|
||||
- `BlockContainer` — Wrapper met header en close button
|
||||
- `BlockLoading` — Spinner met message
|
||||
- `BlockError` — Foutmelding met retry button
|
||||
- `BlockEmpty` — Lege state met icon en actie
|
||||
- `BlockSection` — Sectie met header
|
||||
- `BlockItem` — Lijst-item met badge
|
||||
- `BlockFooter` — Footer met acties
|
||||
|
||||
---
|
||||
|
||||
### Stap 8: Artifact Container Updaten
|
||||
|
||||
**Bestand:** `components/cortex/artifacts/artifact-container.tsx`
|
||||
|
||||
**Wat te doen (3 plekken):**
|
||||
|
||||
1. **Import toevoegen:**
|
||||
```typescript
|
||||
import { NieuweIntentBlock } from '../blocks/nieuwe-intent-block';
|
||||
```
|
||||
|
||||
2. **Render case toevoegen:**
|
||||
```typescript
|
||||
function renderArtifactBlock(artifact: Artifact) {
|
||||
switch (artifact.type) {
|
||||
// ... bestaande cases
|
||||
|
||||
case 'nieuwe_intent':
|
||||
return <NieuweIntentBlock key={artifact.id} prefill={artifact.prefill} />;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
3. **Titel toevoegen:**
|
||||
```typescript
|
||||
function getArtifactTitle(type: string): string {
|
||||
switch (type) {
|
||||
// ... bestaande cases
|
||||
|
||||
case 'nieuwe_intent':
|
||||
return 'Nieuwe Intent Titel';
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Stap 9 (Optioneel): Navigatie Handler
|
||||
|
||||
**Alleen voor navigatie intents (zonder block)**
|
||||
|
||||
**Bestand:** `components/cortex/command-center/command-center.tsx`
|
||||
|
||||
**Wat te doen:**
|
||||
|
||||
In de `useEffect` die `pendingAction` afhandelt:
|
||||
|
||||
```typescript
|
||||
useEffect(() => {
|
||||
if (!pendingAction) return;
|
||||
|
||||
// ... bestaande handlers
|
||||
|
||||
if (pendingAction.intent === 'nieuwe_navigeer') {
|
||||
const target = pendingAction.entities.navigationTarget;
|
||||
|
||||
// Navigeer naar juiste pagina
|
||||
router.push(`/epd/patients/${patientId}/path/${target}`);
|
||||
|
||||
// Feedback tonen
|
||||
toast({
|
||||
title: 'Navigeren...',
|
||||
description: `Naar ${target}`,
|
||||
});
|
||||
|
||||
// Cleanup
|
||||
setPendingAction(null);
|
||||
return;
|
||||
}
|
||||
}, [pendingAction]);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. Checklist
|
||||
|
||||
Gebruik deze checklist bij het toevoegen van een nieuwe intent:
|
||||
|
||||
### Voorbereiding
|
||||
- [ ] Intent type bepaald (Query/Action/Navigation)
|
||||
- [ ] Intent naam gekozen (lowercase, underscore)
|
||||
- [ ] Entities gedefinieerd
|
||||
- [ ] Trigger woorden verzameld
|
||||
|
||||
### Types & Patterns
|
||||
- [ ] `lib/cortex/types.ts` — CortexIntent type
|
||||
- [ ] `lib/cortex/types.ts` — BLOCK_CONFIGS (als block nodig)
|
||||
- [ ] `lib/cortex/types.ts` — ExtractedEntities (als nieuwe entities)
|
||||
- [ ] `lib/cortex/reflex-classifier.ts` — INTENT_PATTERNS
|
||||
|
||||
### AI Integratie
|
||||
- [ ] `app/api/cortex/chat/route.ts` — Intent in system prompt
|
||||
- [ ] `app/api/cortex/chat/route.ts` — Voorbeeld met JSON
|
||||
|
||||
### Validatie & Routing
|
||||
- [ ] `lib/cortex/action-parser.ts` — ActionSchema intent enum
|
||||
- [ ] `lib/cortex/action-parser.ts` — ActionSchema artifact type (als block)
|
||||
- [ ] `lib/cortex/action-parser.ts` — routeIntentToArtifact() case
|
||||
|
||||
### API (indien nodig)
|
||||
- [ ] `app/api/cortex/[domain]/route.ts` — Nieuwe route
|
||||
- [ ] Query params: `|| undefined` voor optionele params!
|
||||
|
||||
### UI Component
|
||||
- [ ] `components/cortex/blocks/[intent]-block.tsx` — Block component
|
||||
- [ ] `components/cortex/artifacts/artifact-container.tsx` — Import
|
||||
- [ ] `components/cortex/artifacts/artifact-container.tsx` — Render case
|
||||
- [ ] `components/cortex/artifacts/artifact-container.tsx` — Titel
|
||||
|
||||
### Navigatie (indien van toepassing)
|
||||
- [ ] `components/cortex/command-center/command-center.tsx` — Handler
|
||||
|
||||
### Testen
|
||||
- [ ] Lokale classificatie testen (Reflex Arc)
|
||||
- [ ] AI classificatie testen (Chat)
|
||||
- [ ] Block rendering testen
|
||||
- [ ] Error states testen
|
||||
- [ ] Voice input testen
|
||||
|
||||
---
|
||||
|
||||
## 5. Veelgemaakte Fouten
|
||||
|
||||
### Fout 1: Zod + null vs undefined
|
||||
|
||||
**Probleem:**
|
||||
```typescript
|
||||
// Dit faalt!
|
||||
const param = searchParams.get('optionalParam'); // Returns null
|
||||
```
|
||||
|
||||
**Oplossing:**
|
||||
```typescript
|
||||
const param = searchParams.get('optionalParam') || undefined;
|
||||
```
|
||||
|
||||
### Fout 2: Intent niet in AI prompt
|
||||
|
||||
**Symptoom:** Chat AI vraagt "Wil je het dossier opzoeken?" ipv de juiste actie.
|
||||
|
||||
**Oorzaak:** Intent ontbreekt in system prompt.
|
||||
|
||||
**Oplossing:** Voeg intent + voorbeeld toe aan `buildSystemPrompt()`.
|
||||
|
||||
### Fout 3: Block wordt niet gerenderd
|
||||
|
||||
**Symptoom:** Console log toont "Opening artifact: intent" maar niets verschijnt.
|
||||
|
||||
**Oorzaak:** Drie plekken in artifact-container.tsx niet bijgewerkt.
|
||||
|
||||
**Oplossing:** Check import, render case, én titel.
|
||||
|
||||
### Fout 4: Escalatie bij elke invoer
|
||||
|
||||
**Symptoom:** Alles gaat naar AI, zelfs simpele commando's.
|
||||
|
||||
**Oorzaak:** Pattern weight te laag (< 0.7).
|
||||
|
||||
**Oplossing:** Verhoog weights of voeg sterkere patterns toe.
|
||||
|
||||
---
|
||||
|
||||
## 6. Diagram: Bestandenflow
|
||||
|
||||
```
|
||||
┌────────────────────────────────────────────────────────────────────────┐
|
||||
│ BESTANDEN PER STAP │
|
||||
├────────────────────────────────────────────────────────────────────────┤
|
||||
│ │
|
||||
│ lib/cortex/ │
|
||||
│ ├── types.ts ────────────────────────┬─── Stap 1: Types │
|
||||
│ ├── reflex-classifier.ts ────────────┼─── Stap 2: Patterns │
|
||||
│ └── action-parser.ts ────────────────┼─── Stap 4-5: Validatie+Route │
|
||||
│ │ │
|
||||
│ app/api/cortex/ │ │
|
||||
│ ├── chat/route.ts ───────────────────┼─── Stap 3: AI Prompt │
|
||||
│ └── [domain]/route.ts ───────────────┼─── Stap 6: API (optioneel) │
|
||||
│ │ │
|
||||
│ components/cortex/ │ │
|
||||
│ ├── blocks/[intent]-block.tsx ───────┼─── Stap 7: Block Component │
|
||||
│ ├── artifacts/artifact-container.tsx ┼─── Stap 8: Rendering │
|
||||
│ └── command-center/command-center.tsx┴─── Stap 9: Navigatie │
|
||||
│ │
|
||||
└────────────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 7. Voorbeeld: Nieuwe Intent "kindcheck_query"
|
||||
|
||||
Stel we willen een intent toevoegen voor "Toon kindcheck status".
|
||||
|
||||
### Stap 1: types.ts
|
||||
```typescript
|
||||
// CortexIntent
|
||||
| 'kindcheck_query'
|
||||
|
||||
// BLOCK_CONFIGS
|
||||
kindcheck_query: {
|
||||
type: 'kindcheck_query',
|
||||
title: 'Kindcheck',
|
||||
size: 'md',
|
||||
icon: 'Baby',
|
||||
},
|
||||
```
|
||||
|
||||
### Stap 2: reflex-classifier.ts
|
||||
```typescript
|
||||
kindcheck_query: [
|
||||
{ pattern: /^kindcheck/i, weight: 1.0 },
|
||||
{ pattern: /^(toon|bekijk)\s+(de\s+)?kindcheck/i, weight: 0.95 },
|
||||
{ pattern: /^zijn\s+er\s+kinderen/i, weight: 0.9 },
|
||||
{ pattern: /^kinderen\s+in\s+beeld/i, weight: 0.85 },
|
||||
],
|
||||
```
|
||||
|
||||
### Stap 3: chat/route.ts
|
||||
```typescript
|
||||
// In prompt:
|
||||
- **kindcheck_query** — Kindcheck status opvragen
|
||||
- Triggers: "kindcheck", "zijn er kinderen?", "kinderen in beeld?"
|
||||
- Entities: geen
|
||||
- Actie: Toont kindcheck status block
|
||||
|
||||
// Voorbeeld:
|
||||
### Voorbeeld 13: Kindcheck
|
||||
|
||||
**User:**
|
||||
"kindcheck"
|
||||
|
||||
**AI Response:**
|
||||
"Ik toon de kindcheck status.
|
||||
|
||||
\`\`\`json
|
||||
{
|
||||
"type": "action",
|
||||
"intent": "kindcheck_query",
|
||||
"entities": {},
|
||||
"confidence": 0.98,
|
||||
"artifact": {
|
||||
"type": "kindcheck_query",
|
||||
"prefill": {}
|
||||
}
|
||||
}
|
||||
\`\`\`"
|
||||
```
|
||||
|
||||
### Stap 4-5: action-parser.ts
|
||||
```typescript
|
||||
// ActionSchema intent enum
|
||||
'kindcheck_query',
|
||||
|
||||
// ActionSchema artifact type
|
||||
'kindcheck_query',
|
||||
|
||||
// routeIntentToArtifact
|
||||
case 'kindcheck_query':
|
||||
return {
|
||||
type: 'kindcheck_query',
|
||||
title: 'Kindcheck',
|
||||
prefill: entities,
|
||||
};
|
||||
```
|
||||
|
||||
### Stap 6: API route
|
||||
```typescript
|
||||
// app/api/cortex/intake/kindcheck/route.ts
|
||||
// ... (vergelijkbaar met risico/route.ts)
|
||||
```
|
||||
|
||||
### Stap 7: Block component
|
||||
```typescript
|
||||
// components/cortex/blocks/kindcheck-block.tsx
|
||||
// ... (vergelijkbaar met risico-block.tsx)
|
||||
```
|
||||
|
||||
### Stap 8: artifact-container.tsx
|
||||
```typescript
|
||||
import { KindcheckBlock } from '../blocks/kindcheck-block';
|
||||
|
||||
// render case
|
||||
case 'kindcheck_query':
|
||||
return <KindcheckBlock key={artifact.id} prefill={artifact.prefill} />;
|
||||
|
||||
// titel
|
||||
case 'kindcheck_query':
|
||||
return 'Kindcheck';
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 8. Gerelateerde Documentatie
|
||||
|
||||
| Document | Locatie |
|
||||
|----------|---------|
|
||||
| Intent Overzicht | `docs/architectuur/intent-overzicht.md` |
|
||||
| Block Template Pattern | `docs/intent/intake-intent-proces/block-template-pattern.md` |
|
||||
| Session Log (Lessons Learned) | `docs/intent/intake-intent-proces/session-log-2026-02-03.md` |
|
||||
| Architectuur Overzicht | `docs/architectuur/architectuur-overzicht.md` |
|
||||
|
||||
---
|
||||
|
||||
*Bij vragen of problemen, raadpleeg de session logs voor bekende issues en oplossingen.*
|
||||
511
docs/architectuur/intent-overzicht.md
Normal file
511
docs/architectuur/intent-overzicht.md
Normal file
@@ -0,0 +1,511 @@
|
||||
# Intent Overzicht — Cortex
|
||||
|
||||
**Versie:** v1.0
|
||||
**Datum:** 4 februari 2026
|
||||
**Doelgroep:** Product owners, IT consultants, data scientists
|
||||
|
||||
---
|
||||
|
||||
## 1. Wat is een Intent?
|
||||
|
||||
Een **intent** is de gedetecteerde bedoeling achter een gebruikerscommando. Wanneer een zorgmedewerker zegt "zoek jan", herkent Cortex de intent `zoeken` met de entity `patientName: "jan"`.
|
||||
|
||||
**Voorbeeld flow:**
|
||||
|
||||
```
|
||||
Gebruikersinvoer → Intent → Actie
|
||||
────────────────────────────────────────────────────────
|
||||
"notitie jan" → dagnotitie → Open notitie-formulier
|
||||
"wat zijn risico's" → risico_query → Toon risico-overzicht
|
||||
"agenda vandaag" → agenda_query → Toon afspraken
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 2. Alle Intents in Kaart
|
||||
|
||||
### 2.1 Basis Intents (Productie)
|
||||
|
||||
| Intent | Trigger voorbeelden | Wat het doet | Block |
|
||||
|--------|---------------------|--------------|-------|
|
||||
| `dagnotitie` | "notitie jan", "medicatie gegeven" | Verpleegkundige notitie maken | DagnotitieBlock |
|
||||
| `zoeken` | "zoek marie", "wie is jan" | Patiënt opzoeken | ZoekenBlock |
|
||||
| `overdracht` | "overdracht", "einde dienst" | Shift-overdracht bekijken | OverdrachtBlock |
|
||||
|
||||
### 2.2 Agenda Intents (Productie)
|
||||
|
||||
| Intent | Trigger voorbeelden | Wat het doet | Block |
|
||||
|--------|---------------------|--------------|-------|
|
||||
| `agenda_query` | "agenda", "afspraken vandaag" | Afspraken bekijken | AgendaBlock |
|
||||
| `create_appointment` | "plan afspraak jan morgen" | Nieuwe afspraak maken | CreateAppointmentBlock |
|
||||
| `cancel_appointment` | "annuleer afspraak jan" | Afspraak annuleren | CancelAppointmentBlock |
|
||||
| `reschedule_appointment` | "verzet 14:00 naar 15:00" | Afspraak verzetten | RescheduleAppointmentBlock |
|
||||
|
||||
### 2.3 Intake Intents (MVP)
|
||||
|
||||
| Intent | Trigger voorbeelden | Wat het doet | Block |
|
||||
|--------|---------------------|--------------|-------|
|
||||
| `intake_status` | "wat moet ik nog doen?", "intake checklist" | Intake voortgang tonen | IntakeStatusBlock |
|
||||
| `risico_query` | "wat zijn de risico's?", "risicotaxatie" | Risico's weergeven | RisicoBlock |
|
||||
| `diagnose_query` | "welke diagnoses?", "toon diagnose" | Diagnoses weergeven | DiagnoseBlock |
|
||||
| `intake_navigeer` | "ga naar risico", "open anamnese" | Naar intake-sectie navigeren | *(geen block, directe navigatie)* |
|
||||
|
||||
### 2.4 Speciale Intents
|
||||
|
||||
| Intent | Wanneer | Wat het doet |
|
||||
|--------|---------|--------------|
|
||||
| `unknown` | Niet herkend | Toon fallback-keuzemenu |
|
||||
|
||||
---
|
||||
|
||||
## 3. Anatomie van een Intent
|
||||
|
||||
Elke intent heeft meerdere "aanraakpunten" in de codebase:
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────────────┐
|
||||
│ INTENT LEVENSCYCLUS │
|
||||
├─────────────────────────────────────────────────────────────────────┤
|
||||
│ │
|
||||
│ 1. TYPES lib/cortex/types.ts │
|
||||
│ └── Intent naam in CortexIntent type │
|
||||
│ └── Block config in BLOCK_CONFIGS │
|
||||
│ └── Entities in ExtractedEntities │
|
||||
│ │
|
||||
│ 2. HERKENNING lib/cortex/reflex-classifier.ts │
|
||||
│ └── Regex patterns voor lokale classificatie │
|
||||
│ │
|
||||
│ 3. AI PROMPT app/api/cortex/chat/route.ts │
|
||||
│ └── Intent beschrijving in system prompt │
|
||||
│ └── Voorbeelden met JSON output │
|
||||
│ │
|
||||
│ 4. VALIDATIE lib/cortex/action-parser.ts │
|
||||
│ └── Zod schema voor intent validatie │
|
||||
│ └── Artifact type mapping │
|
||||
│ │
|
||||
│ 5. ROUTING lib/cortex/action-parser.ts │
|
||||
│ └── routeIntentToArtifact() switch case │
|
||||
│ │
|
||||
│ 6. UI BLOCK components/cortex/blocks/[intent]-block.tsx │
|
||||
│ └── React component voor weergave │
|
||||
│ │
|
||||
│ 7. RENDERING components/cortex/artifacts/artifact- │
|
||||
│ container.tsx │
|
||||
│ └── Import statement │
|
||||
│ └── Switch case in renderArtifactBlock() │
|
||||
│ └── Titel in getArtifactTitle() │
|
||||
│ │
|
||||
│ 8. API (optioneel) app/api/cortex/[domain]/route.ts │
|
||||
│ └── Endpoint voor data ophalen │
|
||||
│ │
|
||||
└─────────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. Bestandsoverzicht per Intent
|
||||
|
||||
### 4.1 `dagnotitie`
|
||||
|
||||
| Aspect | Bestand | Regel/Sectie |
|
||||
|--------|---------|--------------|
|
||||
| Type definitie | `lib/cortex/types.ts` | `CortexIntent` type |
|
||||
| Block config | `lib/cortex/types.ts` | `BLOCK_CONFIGS.dagnotitie` |
|
||||
| Patterns | `lib/cortex/reflex-classifier.ts` | `INTENT_PATTERNS.dagnotitie` |
|
||||
| AI prompt | `app/api/cortex/chat/route.ts` | System prompt sectie |
|
||||
| Validatie | `lib/cortex/action-parser.ts` | `ActionSchema` |
|
||||
| Routing | `lib/cortex/action-parser.ts` | `routeIntentToArtifact()` case |
|
||||
| UI Block | `components/cortex/blocks/dagnotitie-block.tsx` | Hele bestand |
|
||||
| Rendering | `components/cortex/artifacts/artifact-container.tsx` | Import + switch |
|
||||
| API | `app/api/reports/route.ts` | POST voor opslaan |
|
||||
|
||||
**Entities:**
|
||||
```typescript
|
||||
{
|
||||
patientName?: string; // "jan"
|
||||
patientId?: string; // UUID
|
||||
category?: 'medicatie' | 'adl' | 'gedrag' | 'incident' | 'observatie';
|
||||
content?: string; // "medicatie gegeven"
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 4.2 `zoeken`
|
||||
|
||||
| Aspect | Bestand |
|
||||
|--------|---------|
|
||||
| Type definitie | `lib/cortex/types.ts` |
|
||||
| Block config | `lib/cortex/types.ts` |
|
||||
| Patterns | `lib/cortex/reflex-classifier.ts` |
|
||||
| UI Block | `components/cortex/blocks/zoeken-block.tsx` |
|
||||
| API | `app/api/cortex/patients/search/route.ts` |
|
||||
|
||||
**Entities:**
|
||||
```typescript
|
||||
{
|
||||
query?: string; // Zoekterm
|
||||
patientName?: string; // Directe naam
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 4.3 `overdracht`
|
||||
|
||||
| Aspect | Bestand |
|
||||
|--------|---------|
|
||||
| Type definitie | `lib/cortex/types.ts` |
|
||||
| Block config | `lib/cortex/types.ts` |
|
||||
| Patterns | `lib/cortex/reflex-classifier.ts` |
|
||||
| UI Block | `components/cortex/blocks/overdracht-block.tsx` |
|
||||
| API | `app/api/overdracht/route.ts` |
|
||||
|
||||
**Entities:**
|
||||
```typescript
|
||||
{
|
||||
// Geen specifieke entities
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 4.4 `agenda_query`
|
||||
|
||||
| Aspect | Bestand |
|
||||
|--------|---------|
|
||||
| Type definitie | `lib/cortex/types.ts` |
|
||||
| Block config | `lib/cortex/types.ts` |
|
||||
| Patterns | `lib/cortex/reflex-classifier.ts` |
|
||||
| UI Block | `components/cortex/blocks/agenda-block.tsx` |
|
||||
| API | `app/api/cortex/agenda/route.ts` |
|
||||
|
||||
**Entities:**
|
||||
```typescript
|
||||
{
|
||||
dateRange?: {
|
||||
start: Date;
|
||||
end: Date;
|
||||
label: 'vandaag' | 'morgen' | 'deze week' | 'volgende week' | 'custom';
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 4.5 `create_appointment`
|
||||
|
||||
| Aspect | Bestand |
|
||||
|--------|---------|
|
||||
| Type definitie | `lib/cortex/types.ts` |
|
||||
| Block config | `lib/cortex/types.ts` |
|
||||
| Patterns | `lib/cortex/reflex-classifier.ts` |
|
||||
| UI Block | `components/cortex/blocks/agenda-block.tsx` (create mode) |
|
||||
| API | `app/api/cortex/agenda/create/route.ts` |
|
||||
|
||||
**Entities:**
|
||||
```typescript
|
||||
{
|
||||
patientName?: string;
|
||||
patientId?: string;
|
||||
datetime?: {
|
||||
date: Date;
|
||||
time: string; // "HH:mm"
|
||||
};
|
||||
appointmentType?: 'intake' | 'behandeling' | 'follow-up' | 'telefonisch' | 'huisbezoek' | 'online' | 'crisis' | 'overig';
|
||||
location?: 'praktijk' | 'online' | 'thuis';
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 4.6 `intake_status` (MVP)
|
||||
|
||||
| Aspect | Bestand |
|
||||
|--------|---------|
|
||||
| Type definitie | `lib/cortex/types.ts` |
|
||||
| Block config | `lib/cortex/types.ts` |
|
||||
| Patterns | `lib/cortex/reflex-classifier.ts` |
|
||||
| UI Block | `components/cortex/blocks/intake-status-block.tsx` |
|
||||
| API | `app/api/cortex/intake/status/route.ts` |
|
||||
|
||||
**Entities:**
|
||||
```typescript
|
||||
{
|
||||
patientId?: string;
|
||||
intakeId?: string;
|
||||
}
|
||||
```
|
||||
|
||||
**Trigger patterns:**
|
||||
- "wat moet ik nog doen?"
|
||||
- "is de intake compleet?"
|
||||
- "intake checklist"
|
||||
- "intake status"
|
||||
|
||||
---
|
||||
|
||||
### 4.7 `risico_query` (MVP)
|
||||
|
||||
| Aspect | Bestand |
|
||||
|--------|---------|
|
||||
| Type definitie | `lib/cortex/types.ts` |
|
||||
| Block config | `lib/cortex/types.ts` |
|
||||
| Patterns | `lib/cortex/reflex-classifier.ts` |
|
||||
| UI Block | `components/cortex/blocks/risico-block.tsx` |
|
||||
| API | `app/api/cortex/intake/risico/route.ts` |
|
||||
|
||||
**Entities:**
|
||||
```typescript
|
||||
{
|
||||
patientId?: string;
|
||||
intakeId?: string;
|
||||
}
|
||||
```
|
||||
|
||||
**Trigger patterns:**
|
||||
- "wat zijn de risico's?"
|
||||
- "risicotaxatie"
|
||||
- "toon risico's"
|
||||
|
||||
---
|
||||
|
||||
### 4.8 `diagnose_query` (MVP)
|
||||
|
||||
| Aspect | Bestand |
|
||||
|--------|---------|
|
||||
| Type definitie | `lib/cortex/types.ts` |
|
||||
| Block config | `lib/cortex/types.ts` |
|
||||
| Patterns | `lib/cortex/reflex-classifier.ts` |
|
||||
| UI Block | `components/cortex/blocks/diagnose-block.tsx` |
|
||||
| API | `app/api/cortex/intake/diagnose/route.ts` |
|
||||
|
||||
**Entities:**
|
||||
```typescript
|
||||
{
|
||||
patientId?: string;
|
||||
intakeId?: string;
|
||||
}
|
||||
```
|
||||
|
||||
**Trigger patterns:**
|
||||
- "welke diagnoses?"
|
||||
- "toon diagnose"
|
||||
- "wat is de diagnose?"
|
||||
|
||||
---
|
||||
|
||||
### 4.9 `intake_navigeer` (MVP)
|
||||
|
||||
| Aspect | Bestand |
|
||||
|--------|---------|
|
||||
| Type definitie | `lib/cortex/types.ts` |
|
||||
| Patterns | `lib/cortex/reflex-classifier.ts` |
|
||||
| Handler | `components/cortex/command-center/command-center.tsx` |
|
||||
|
||||
**Let op:** Deze intent heeft geen block — het navigeert direct naar een EPD-pagina.
|
||||
|
||||
**Entities:**
|
||||
```typescript
|
||||
{
|
||||
navigationTarget?: 'contacts' | 'kindcheck' | 'risk' | 'anamnese' |
|
||||
'examination' | 'rom' | 'diagnosis' | 'behandeladvies';
|
||||
}
|
||||
```
|
||||
|
||||
**Trigger patterns:**
|
||||
- "ga naar risico"
|
||||
- "open diagnose"
|
||||
- "naar anamnese"
|
||||
|
||||
---
|
||||
|
||||
## 5. Data Flow Diagram
|
||||
|
||||
### 5.1 Van Invoer naar Actie
|
||||
|
||||
```
|
||||
┌──────────────┐
|
||||
│ GEBRUIKER │
|
||||
│ spreekt/ │
|
||||
│ typt │
|
||||
└──────┬───────┘
|
||||
│ "notitie jan medicatie"
|
||||
▼
|
||||
┌──────────────────────────────────────────────────────────────┐
|
||||
│ LAAG 1: REFLEX ARC │
|
||||
│ lib/cortex/reflex-classifier.ts │
|
||||
│ │
|
||||
│ • Pattern matching: /^notitie\s+\w+/i │
|
||||
│ • Confidence: 0.95 │
|
||||
│ • Escalatie check: geen multi-intent, geen context nodig │
|
||||
│ │
|
||||
│ Output: { intent: "dagnotitie", confidence: 0.95 } │
|
||||
└──────────────────────────┬───────────────────────────────────┘
|
||||
│
|
||||
┌────────────────┴────────────────┐
|
||||
│ Confidence >= 0.7? │
|
||||
└────────────────┬────────────────┘
|
||||
│ Ja
|
||||
▼
|
||||
┌──────────────────────────────────────────────────────────────┐
|
||||
│ ROUTING │
|
||||
│ lib/cortex/action-parser.ts → routeIntentToArtifact() │
|
||||
│ │
|
||||
│ Input: intent="dagnotitie", entities={patientName:"jan"} │
|
||||
│ Output: { type: "dagnotitie", prefill: {...}, title: "..." } │
|
||||
└──────────────────────────┬───────────────────────────────────┘
|
||||
│
|
||||
▼
|
||||
┌──────────────────────────────────────────────────────────────┐
|
||||
│ UI RENDERING │
|
||||
│ components/cortex/artifacts/artifact-container.tsx │
|
||||
│ │
|
||||
│ • Switch op artifact.type │
|
||||
│ • Rendert <DagnotitieBlock prefill={...} /> │
|
||||
└──────────────────────────┬───────────────────────────────────┘
|
||||
│
|
||||
▼
|
||||
┌──────────────────────────────────────────────────────────────┐
|
||||
│ BLOCK │
|
||||
│ components/cortex/blocks/dagnotitie-block.tsx │
|
||||
│ │
|
||||
│ • Toont formulier met prefilled data │
|
||||
│ • Gebruiker vult aan en klikt "Opslaan" │
|
||||
│ • POST naar /api/reports │
|
||||
└──────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### 5.2 Escalatie naar AI (Laag 2)
|
||||
|
||||
Wanneer escaleert de Reflex Arc naar de AI Orchestrator?
|
||||
|
||||
| Trigger | Voorbeeld | Reden |
|
||||
|---------|-----------|-------|
|
||||
| **Lage confidence** | "blah blah" | Geen pattern match |
|
||||
| **Ambiguïteit** | "jan" | Kan zoeken of notitie zijn |
|
||||
| **Multi-intent** | "zeg jan af en maak notitie" | Twee acties in één zin |
|
||||
| **Context nodig** | "maak notitie voor hem" | Wie is "hem"? |
|
||||
| **Relatieve tijd** | "morgen om 14:00" | Datum moet berekend worden |
|
||||
|
||||
```
|
||||
┌──────────────────────────────────────────────────────────────┐
|
||||
│ LAAG 2: ORCHESTRATOR (bij escalatie) │
|
||||
│ app/api/cortex/chat/route.ts │
|
||||
│ │
|
||||
│ • Stuurt context + input naar Claude AI │
|
||||
│ • AI retourneert IntentChain met 1+ actions │
|
||||
│ • Kan clarification vragen ("Met welke patiënt?") │
|
||||
└──────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 6. Entity Extractie
|
||||
|
||||
### 6.1 Hoe worden entities geëxtraheerd?
|
||||
|
||||
**Lokaal (Reflex Arc):**
|
||||
- Eenvoudige regex voor bekende patronen
|
||||
- Voorbeeld: `/^notitie\s+(\w+)/` → extraheert patiëntnaam
|
||||
|
||||
**AI (Orchestrator):**
|
||||
- Claude analyseert volledige zin
|
||||
- Extraheert alle relevante entities
|
||||
- Kan context gebruiken (actieve patiënt, agenda)
|
||||
|
||||
### 6.2 Entity Types
|
||||
|
||||
| Entity | Type | Voorbeeld | Gebruikt door |
|
||||
|--------|------|-----------|---------------|
|
||||
| `patientName` | string | "jan de vries" | Alle intents |
|
||||
| `patientId` | UUID | "abc-123..." | Alle intents |
|
||||
| `category` | enum | "medicatie" | dagnotitie |
|
||||
| `content` | string | "medicatie gegeven" | dagnotitie |
|
||||
| `query` | string | "jan" | zoeken |
|
||||
| `dateRange` | object | { start, end, label } | agenda_query |
|
||||
| `datetime` | object | { date, time } | create_appointment |
|
||||
| `appointmentType` | enum | "intake" | create_appointment |
|
||||
| `location` | enum | "praktijk" | create_appointment |
|
||||
| `navigationTarget` | enum | "risk" | intake_navigeer |
|
||||
|
||||
---
|
||||
|
||||
## 7. Block Types
|
||||
|
||||
### 7.1 Block Categorieën
|
||||
|
||||
| Type | Doel | Voorbeeld |
|
||||
|------|------|-----------|
|
||||
| **Query Block** | Data tonen (read-only) | RisicoBlock, DiagnoseBlock |
|
||||
| **Action Block** | Data invoeren/wijzigen | DagnotitieBlock |
|
||||
| **Status Block** | Voortgang/checklist tonen | IntakeStatusBlock |
|
||||
| **Navigation Block** | Direct navigeren | *(intake_navigeer)* |
|
||||
|
||||
### 7.2 Block Structuur
|
||||
|
||||
Alle blocks volgen hetzelfde patroon:
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────┐
|
||||
│ [Icon] Titel [Sluiten] │
|
||||
├─────────────────────────────────────────────────────┤
|
||||
│ │
|
||||
│ Loading state → Error state → Data state │
|
||||
│ (spinner) (foutmelding) (content) │
|
||||
│ │
|
||||
│ ─────────────────────────────────────────────── │
|
||||
│ [Secundaire actie] [Primaire actie] │
|
||||
└─────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 8. Confidence & Escalatie
|
||||
|
||||
### 8.1 Drempelwaarden
|
||||
|
||||
| Waarde | Betekenis | Actie |
|
||||
|--------|-----------|-------|
|
||||
| `>= 0.9` | Zeer zeker | Direct uitvoeren |
|
||||
| `0.7 - 0.9` | Redelijk zeker | Uitvoeren met bevestiging |
|
||||
| `< 0.7` | Onzeker | Escaleer naar AI of vraag verduidelijking |
|
||||
|
||||
### 8.2 Escalatie Redenen
|
||||
|
||||
```typescript
|
||||
type EscalationReason =
|
||||
| 'low_confidence' // Confidence < 0.7
|
||||
| 'ambiguous' // Top-2 intents liggen dicht bij elkaar
|
||||
| 'multi_intent_detected' // Meerdere acties gedetecteerd
|
||||
| 'needs_context' // Voornaamwoorden zoals "hij", "haar"
|
||||
| 'relative_time'; // "morgen", "volgende week"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 9. Gerelateerde Documentatie
|
||||
|
||||
| Document | Locatie | Inhoud |
|
||||
|----------|---------|--------|
|
||||
| Block Template Pattern | `docs/intent/intake-intent-proces/block-template-pattern.md` | Technisch patroon voor blocks |
|
||||
| Session Log | `docs/intent/intake-intent-proces/session-log-2026-02-03.md` | Bug fixes en lessons learned |
|
||||
| Implementatieplan | `docs/architectuur/implementatieplan-nieuwe-intents.md` | Stappenplan nieuwe intents |
|
||||
|
||||
---
|
||||
|
||||
## 10. Glossary
|
||||
|
||||
| Term | Betekenis |
|
||||
|------|-----------|
|
||||
| **Intent** | Gedetecteerde bedoeling achter een commando |
|
||||
| **Entity** | Geëxtraheerd gegeven uit de invoer (naam, datum, etc.) |
|
||||
| **Block** | UI component dat een intent visueel afhandelt |
|
||||
| **Artifact** | Container voor blocks in het werkgebied |
|
||||
| **Confidence** | Zekerheidsgraad van classificatie (0-1) |
|
||||
| **Escalatie** | Doorverwijzing naar AI voor complexe invoer |
|
||||
| **Prefill** | Vooringevulde data in een formulier |
|
||||
|
||||
---
|
||||
|
||||
*Voor het toevoegen van nieuwe intents, zie: `implementatieplan-nieuwe-intents.md`*
|
||||
Reference in New Issue
Block a user