feat: add agenda intent scaffolding

This commit is contained in:
colinislit
2025-12-27 21:46:24 +01:00
parent 5b27a3b538
commit 6460b4361f
7 changed files with 751 additions and 51 deletions

View File

@@ -20,6 +20,9 @@ const ActionSchema = z.object({
category: z.enum(['medicatie', 'adl', 'gedrag', 'incident', 'observatie']).optional(),
content: z.string().optional(),
query: z.string().optional(), // For zoeken intent
date: z.string().optional(),
time: z.string().optional(),
identifier: z.string().optional(),
}),
confidence: z.number().min(0).max(1),
artifact: z

View File

@@ -11,12 +11,25 @@ import type { VerpleegkundigCategory } from '@/lib/types/report';
// Zod schema for AI response validation
const AIIntentResponseSchema = z.object({
intent: z.enum(['dagnotitie', 'zoeken', 'overdracht', 'unknown']),
intent: z.enum([
'dagnotitie',
'zoeken',
'overdracht',
'agenda_query',
'create_appointment',
'cancel_appointment',
'reschedule_appointment',
'unknown',
]),
confidence: z.number().min(0).max(1),
entities: z.object({
patientName: z.string().optional(),
category: z.enum(['medicatie', 'adl', 'gedrag', 'incident', 'observatie']).optional(),
content: z.string().optional(),
query: z.string().optional(),
date: z.string().optional(),
time: z.string().optional(),
identifier: z.string().optional(),
}).optional(),
reasoning: z.string().optional(),
});
@@ -45,7 +58,19 @@ Je taak is om de intentie van een zorgmedewerker te classificeren in één van d
3. **overdracht** - Gebruiker wil een overdracht/samenvatting van de dienst
Voorbeelden: "overdracht", "wat moet ik weten", "dienst afronden"
4. **unknown** - Intentie is onduidelijk of past niet in bovenstaande categorieën
4. **agenda_query** - Gebruiker wil afspraken opvragen of de agenda zien
Voorbeelden: "afspraken vandaag", "wat is mijn volgende afspraak", "agenda volgende week"
5. **create_appointment** - Gebruiker wil een afspraak maken of plannen
Voorbeelden: "maak afspraak met jan morgen 14:00", "plan intake volgende week"
6. **cancel_appointment** - Gebruiker wil een afspraak annuleren
Voorbeelden: "annuleer afspraak jan", "zeg afspraak af"
7. **reschedule_appointment** - Gebruiker wil een afspraak verzetten
Voorbeelden: "verzet 14:00 naar 15:00", "verplaats afspraak naar dinsdag"
8. **unknown** - Intentie is onduidelijk of past niet in bovenstaande categorieën
Voor dagnotitie, extraheer ook:
- patientName: de naam van de patiënt (indien genoemd)
@@ -55,14 +80,41 @@ Voor dagnotitie, extraheer ook:
Voor zoeken, extraheer:
- patientName: de naam die gezocht wordt
Voor agenda_query, extraheer:
- query: het relevante datum-/tijd-bereik of scope (bijv. "vandaag", "volgende week")
- patientName: de patiëntnaam als die expliciet genoemd is
- date: een expliciete datum als losse waarde (bijv. "2025-01-05")
- time: een expliciete tijd (24-uurs, bijv. "14:00")
Voor create_appointment, extraheer:
- patientName: de patiëntnaam als die expliciet genoemd is
- query: datum/tijd/type/locatie details in vrije tekst (bijv. "morgen 14:00 intake")
- date: een expliciete datum als losse waarde (bijv. "2025-01-05")
- time: een expliciete tijd (24-uurs, bijv. "14:00")
Voor cancel_appointment, extraheer:
- patientName: de patiëntnaam als die expliciet genoemd is
- query: afspraakdetails in vrije tekst (bijv. "afspraak om 14:00", "afspraak van vrijdag")
- identifier: een expliciete afspraak-id indien genoemd
Voor reschedule_appointment, extraheer:
- patientName: de patiëntnaam als die expliciet genoemd is
- query: huidige + nieuwe datum/tijd in vrije tekst (bijv. "14:00 naar 15:00")
- date: de nieuwe expliciete datum indien genoemd
- time: de nieuwe expliciete tijd indien genoemd
Antwoord ALLEEN met een JSON object in dit formaat:
{
"intent": "dagnotitie" | "zoeken" | "overdracht" | "unknown",
"intent": "dagnotitie" | "zoeken" | "overdracht" | "agenda_query" | "create_appointment" | "cancel_appointment" | "reschedule_appointment" | "unknown",
"confidence": 0.0-1.0,
"entities": {
"patientName": "naam" (optioneel),
"category": "medicatie" | "adl" | "gedrag" | "incident" | "observatie" (optioneel),
"content": "inhoud" (optioneel)
"content": "inhoud" (optioneel),
"query": "vrije tekst voor planning" (optioneel),
"date": "YYYY-MM-DD" (optioneel),
"time": "HH:MM" (optioneel),
"identifier": "id" (optioneel)
},
"reasoning": "korte uitleg" (optioneel)
}`;
@@ -138,6 +190,10 @@ export async function classifyIntentWithAI(input: string): Promise<AIClassificat
patientName: validated.entities?.patientName,
category: validated.entities?.category as VerpleegkundigCategory | undefined,
content: validated.entities?.content,
query: validated.entities?.query,
date: validated.entities?.date,
time: validated.entities?.time,
identifier: validated.entities?.identifier,
},
source: 'ai',
processingTimeMs,

View File

@@ -93,6 +93,65 @@ const INTENT_PATTERNS: Record<Exclude<SwiftIntent, 'unknown'>, PatternConfig[]>
{ pattern: /^begin\s+dienst\b/i, weight: 0.9 },
{ pattern: /^nieuwe?\s+dienst\b/i, weight: 0.85 },
],
// Agenda intents
agenda_query: [
// Exact commands
{ pattern: /^agenda\b/i, weight: 1.0 },
{ pattern: /^mijn\s+agenda\b/i, weight: 0.95 },
{ pattern: /^volgende\s+afspraak\b/i, weight: 0.95 },
// Question patterns
{ pattern: /^wat\s+zijn\s+(mijn\s+)?afspraken\b/i, weight: 1.0 },
{ pattern: /^(wat|wanneer)\s+is\s+(mijn\s+)?volgende\s+afspraak\b/i, weight: 1.0 },
// Date scoped queries
{ pattern: /^afspraken\s+(vandaag|morgen|deze\s+week|volgende\s+week)\b/i, weight: 0.95 },
{ pattern: /^(vandaag|morgen|deze\s+week|volgende\s+week)\s+afspraken\b/i, weight: 0.9 },
{ pattern: /^agenda\s+(vandaag|morgen|deze\s+week|volgende\s+week)\b/i, weight: 0.9 },
{ pattern: /^planning\s+(vandaag|morgen|deze\s+week|volgende\s+week)\b/i, weight: 0.85 },
{ pattern: /^afspraken\b(?!\s+(maken|plannen|inplannen|annuleren|verzetten|verplaatsen|verschuiven))\b/i, weight: 0.85 },
// Verb patterns
{ pattern: /^toon\s+agenda\b/i, weight: 0.9 },
{ pattern: /^laat\s+(mijn\s+)?agenda\s+zien\b/i, weight: 0.85 },
],
create_appointment: [
// Exact commands
{ pattern: /^maak\s+afspraak\b/i, weight: 1.0 },
{ pattern: /^plan\s+(een\s+)?(afspraak|intake|gesprek)\b/i, weight: 1.0 },
{ pattern: /^afspraak\s+(maken|plannen|inplannen)\b/i, weight: 0.95 },
{ pattern: /^afspraken\s+(maken|plannen|inplannen)\b/i, weight: 0.95 },
{ pattern: /^nieuwe?\s+afspraak\b/i, weight: 0.95 },
// Type-first patterns
{ pattern: /^maak\s+(een\s+)?(intake|behandeling|gesprek)\b/i, weight: 0.9 },
{ pattern: /^(intake|behandeling|gesprek)\s+(met\s+)?\w+/i, weight: 0.85 },
{ pattern: /^afspraak\s+met\s+\w+/i, weight: 0.9 },
{ pattern: /^plan\s+afspraak\s+met\s+\w+/i, weight: 0.9 },
],
cancel_appointment: [
// Exact commands
{ pattern: /^annuleer\s+(de\s+)?afspraak\b/i, weight: 1.0 },
{ pattern: /^cancel\s+(de\s+)?afspraak\b/i, weight: 1.0 },
{ pattern: /^verwijder\s+afspraak\b/i, weight: 0.95 },
{ pattern: /^afspraak\s+annuleren\b/i, weight: 0.95 },
{ pattern: /^zeg\s+afspraak\s+af\b/i, weight: 0.95 },
// Short forms
{ pattern: /^annuleer\s+\w+/i, weight: 0.7 }, // "annuleer jan"
],
reschedule_appointment: [
// Exact commands
{ pattern: /^verzet\s+(de\s+)?afspraak\b/i, weight: 1.0 },
{ pattern: /^verplaats\s+(de\s+)?afspraak\b/i, weight: 1.0 },
{ pattern: /^verschuif\s+afspraak\b/i, weight: 0.95 },
{ pattern: /^afspraak\s+verzetten\b/i, weight: 0.95 },
// Time shifts
{ pattern: /^\d{1,2}[:.]\d{2}\s+naar\s+\d{1,2}[:.]\d{2}\b/i, weight: 0.9 },
{ pattern: /^(verzet|verplaats)\s+\d{1,2}[:.]\d{2}\b/i, weight: 0.9 },
{ pattern: /^verzet\s+\w+\s+naar\b/i, weight: 0.85 }, // "verzet jan naar dinsdag"
],
};
// Help patterns (separate, always check)

View File

@@ -7,7 +7,15 @@
import type { VerpleegkundigCategory } from '@/lib/types/report';
// Intent types
export type SwiftIntent = 'dagnotitie' | 'zoeken' | 'overdracht' | 'unknown';
export type SwiftIntent =
| 'dagnotitie'
| 'zoeken'
| 'overdracht'
| 'agenda_query'
| 'create_appointment'
| 'cancel_appointment'
| 'reschedule_appointment'
| 'unknown';
export type BlockType = Exclude<SwiftIntent, 'unknown'> | 'patient-dashboard';
@@ -29,6 +37,9 @@ export interface ExtractedEntities {
category?: VerpleegkundigCategory;
content?: string;
query?: string;
date?: string;
time?: string;
identifier?: string;
}
// Block sizes
@@ -62,6 +73,30 @@ export const BLOCK_CONFIGS: Record<BlockType, BlockConfig> = {
size: 'lg',
icon: 'ArrowRightLeft',
},
agenda_query: {
type: 'agenda_query',
title: 'Agenda',
size: 'lg',
icon: 'Calendar',
},
create_appointment: {
type: 'create_appointment',
title: 'Nieuwe afspraak',
size: 'lg',
icon: 'Plus',
},
cancel_appointment: {
type: 'cancel_appointment',
title: 'Afspraak annuleren',
size: 'lg',
icon: 'X',
},
reschedule_appointment: {
type: 'reschedule_appointment',
title: 'Afspraak verzetten',
size: 'lg',
icon: 'Clock',
},
'patient-dashboard': {
type: 'patient-dashboard',
title: 'Patiëntoverzicht',