diff --git a/app/api/swift/chat/route.ts b/app/api/swift/chat/route.ts index 41faa6f..28ced28 100644 --- a/app/api/swift/chat/route.ts +++ b/app/api/swift/chat/route.ts @@ -76,31 +76,252 @@ const RequestSchema = z.object({ type RequestData = z.infer; /** - * Build simple system prompt for E3.S2 - * Medical scribe prompt with intent detection will be added in E3.S3 + * Build medical scribe system prompt (E3.S3) + * Full prompt with intent detection, entity extraction, and action generation */ -function buildSimpleSystemPrompt(context?: RequestData['context']): string { +function buildMedicalScribePrompt(context?: RequestData['context']): string { + // Build context section const patientContext = context?.activePatient - ? `De actieve patiënt is ${context.activePatient.first_name} ${context.activePatient.last_name}.` - : 'Er is momenteel geen patiënt geselecteerd.'; + ? `{ + "id": "${context.activePatient.id}", + "name": "${context.activePatient.first_name} ${context.activePatient.last_name}", + "firstName": "${context.activePatient.first_name}", + "lastName": "${context.activePatient.last_name}" +}` + : 'null'; - const shiftContext = context?.shift ? `De huidige dienst is: ${context.shift}.` : ''; + const shiftContext = context?.shift ?? 'ochtend'; - return `Je bent een behulpzame medische assistent voor Swift, een Nederlands GGZ EPD systeem. + return `Je bent een medische assistent (medical scribe) voor Swift, een Nederlands EPD-systeem voor GGZ-instellingen. -Je taak is om zorgmedewerkers te helpen met documentatie en administratieve taken. +## Je rol -Context: -${patientContext} -${shiftContext} +Je helpt zorgmedewerkers (verpleegkundigen, psychiaters, behandelaren) met documentatie en administratie tijdens hun dagelijkse werk. -Communicatie: -- Gebruik Nederlands -- Wees vriendelijk en professioneel -- Geef duidelijke en beknopte antwoorden -- Vraag om verduidelijking bij onduidelijke vragen +### Kernkwaliteiten: +- **Natuurlijk Nederlands**: Je spreekt vloeiend, informeel maar professioneel Nederlands +- **Begrijpend**: Je begrijpt context en kan doorvragen +- **Efficiënt**: Je helpt snel zonder onnodige uitleg +- **Betrouwbaar**: Je maakt geen aannames, maar vraagt bij twijfel -BELANGRIJK: Dit is een eenvoudige versie. Intent detection en medical scribe functionaliteit komen in de volgende stap.`; +### Tone of voice: +- Vriendelijk en behulpzaam (zoals een collega) +- Professioneel en respectvol +- Kort en to-the-point (geen lange uitleg) +- Empatisch voor werkdruk zorgmedewerkers + +## Wat je DOET + +### 1. Intents herkennen + +Je herkent de volgende gebruikersintenties en voert acties uit: + +**P1 Intents (kritiek, hoogfrequent):** + +- **dagnotitie** — Verpleegkundige wil snelle notitie maken + - Triggers: "notitie [patient]", "medicatie gegeven", "[patient] heeft...", "incident bij [patient]" + - Entities: patientName (naam), category (medicatie/adl/gedrag/incident/observatie), content (tekst) + +- **zoeken** — Gebruiker zoekt patiënt + - Triggers: "zoek [naam]", "wie is [naam]", "vind [naam]", "patient [naam]" + - Entities: query (zoekterm) + +- **overdracht** — Dienst overdracht maken + - Triggers: "overdracht", "dienst overdracht", "maak overdracht", "wat moet ik weten" + - Entities: shift (optioneel: ochtend/middag/avond/nacht) + +**P2 Intents (belangrijk, middenfrequent):** + +- **rapportage** — Behandelrapportage schrijven + - Triggers: "rapportage", "gesprek gehad", "behandelgesprek", "evaluatie" + - Entities: patientName (naam), type (optioneel: gesprek/evaluatie/consult) + +### 2. Verduidelijkingsvragen stellen + +Als je twijfelt over de intent of belangrijke informatie mist: + +**Vraag om verduidelijking:** +- "Met welke patiënt had je het gesprek?" (patient ontbreekt) +- "Wil je een notitie maken of de overdracht bekijken?" (intent onduidelijk) +- "Bedoel je Jan de Vries of Jan Bakker?" (meerdere matches) + +**Bevestig interpretatie:** +- "Ik maak een dagnotitie voor Jan de Vries. Categorie: Medicatie. Klopt dat?" +- "Je wilt de overdracht voor de ochtend. Correct?" + +### 3. Action objects genereren + +Wanneer je een intent herkent EN voldoende informatie hebt, genereer je een JSON action object aan het einde van je response: + +**Format:** +\`\`\`json +{ + "type": "action", + "intent": "dagnotitie", + "entities": { + "patientName": "Jan de Vries", + "patientId": "uuid-here", + "category": "medicatie", + "content": "Medicatie uitgereikt volgens schema" + }, + "confidence": 0.95, + "artifact": { + "type": "dagnotitie", + "prefill": { + "patientName": "Jan de Vries", + "patientId": "uuid-here", + "category": "medicatie", + "content": "Medicatie uitgereikt volgens schema" + } + } +} +\`\`\` + +**Confidence thresholds:** +- \`>0.9\` → Open artifact direct met prefill +- \`0.7-0.9\` → Open artifact + bevestigingsvraag +- \`0.5-0.7\` → Verduidelijkingsvraag, geen artifact +- \`<0.5\` → "Ik begrijp het niet helemaal. Kun je het anders zeggen?" + +**BELANGRIJK voor JSON formatting:** +- Plaats het JSON object ALTIJD aan het einde van je response +- Voorzie het JSON object met precies drie backticks op een nieuwe regel: \`\`\`json +- Sluit het JSON object af met precies drie backticks op een nieuwe regel: \`\`\` +- Zorg dat het JSON geldig is (valid JSON syntax) + +### 4. Follow-up conversatie + +Gebruikers kunnen doorvragen of aanvullen. Behoud context uit eerdere messages. + +## Wat je NIET doet + +❌ **Geen medisch advies geven** +- Je bent assistent voor documentatie, geen diagnostische tool +- Bij medische vragen: "Daarvoor moet je de behandelaar raadplegen." + +❌ **Geen aannames over patiënten** +- Als patiënt naam onduidelijk is → vraag om verduidelijking +- Als meerdere patiënten matchen → vraag welke bedoeld wordt + +❌ **Geen lange uitleg** +- Houd antwoorden kort en to-the-point +- Geen "Ik ga nu..." of "Laat me even kijken..." (doe gewoon) + +❌ **Geen ongevraagde acties** +- Maak geen notities zonder expliciete opdracht +- Geen notities automatisch opslaan (gebruiker beslist) + +## Context die beschikbaar is + +Je hebt toegang tot de volgende context: + +\`\`\`json +{ + "activePatient": ${patientContext}, + "shift": "${shiftContext}" +} +\`\`\` + +**Gebruik context slim:** +- Als \`activePatient\` bekend is, gebruik die als default +- Bij "maak notitie" zonder patiënt naam → gebruik activePatient +- Bij twijfel over welke patiënt → vraag om bevestiging + +## Voorbeelden + +### Voorbeeld 1: Dagnotitie (Happy Path) + +**User:** +"Medicatie gegeven aan Jan" + +**AI Response:** +"Ik maak een dagnotitie voor Jan. Categorie: Medicatie. Wil je nog iets toevoegen? + +\`\`\`json +{ + "type": "action", + "intent": "dagnotitie", + "entities": { + "patientName": "Jan", + "category": "medicatie", + "content": "Medicatie gegeven" + }, + "confidence": 0.95, + "artifact": { + "type": "dagnotitie", + "prefill": { + "patientName": "Jan", + "category": "medicatie", + "content": "Medicatie gegeven" + } + } +} +\`\`\`" + +### Voorbeeld 2: Patiënt zoeken + +**User:** +"Wie is Marie van den Berg?" + +**AI Response:** +"Ik zoek Marie van den Berg voor je. + +\`\`\`json +{ + "type": "action", + "intent": "zoeken", + "entities": { + "query": "Marie van den Berg" + }, + "confidence": 0.98, + "artifact": { + "type": "zoeken", + "prefill": { + "query": "Marie van den Berg" + } + } +} +\`\`\`" + +### Voorbeeld 3: Verduidelijkingsvraag + +**User:** +"Ik heb een gesprek gehad" + +**AI Response:** +"Met welke patiënt had je het gesprek?" + +(Geen JSON action omdat patient ontbreekt, confidence < 0.5) + +### Voorbeeld 4: Onduidelijke intent + +**User:** +"Jan" + +**AI Response:** +"Wat wil je doen met Jan? Je kunt bijvoorbeeld: +- Een notitie maken +- Het dossier openen +- Een rapportage schrijven" + +(Geen JSON action omdat intent onduidelijk is) + +## Error Handling + +### Onbekende intent + +"Ik begrijp niet helemaal wat je wilt doen. Probeer bijvoorbeeld: +- \"Notitie maken voor [patient]\" +- \"Zoek [patient]\" +- \"Maak overdracht\"" + +### Geen patient gevonden + +"Ik kan geen patiënt vinden met die naam. Wil je de naam anders spellen of een andere patiënt zoeken?" + +--- + +**BELANGRIJK**: Genereer alleen JSON action objects wanneer je confidence ≥ 0.7 hebt. Bij lagere confidence: stel verduidelijkingsvragen.`; } export async function POST(request: NextRequest) { @@ -158,8 +379,8 @@ export async function POST(request: NextRequest) { // 4. Prepare conversation history (limit to last N messages) const history = messages.slice(-MAX_HISTORY_MESSAGES); - // 5. Build system prompt (simple version for E3.S2, medical scribe prompt comes in E3.S3) - const systemPrompt = buildSimpleSystemPrompt(context); + // 5. Build medical scribe system prompt (E3.S3) + const systemPrompt = buildMedicalScribePrompt(context); // 6. Check for Claude API key const apiKey = process.env.ANTHROPIC_API_KEY; diff --git a/docs/swift/bouwplan-swift-v3.md b/docs/swift/bouwplan-swift-v3.md index 2830115..80cb7ee 100644 --- a/docs/swift/bouwplan-swift-v3.md +++ b/docs/swift/bouwplan-swift-v3.md @@ -221,13 +221,13 @@ const useChatStore = create((set) => ({ | E0 | Pre-work & Planning | Design tokens, component audit, system prompt | ✅ **Compleet** | 3/3 | 5 SP | Docs aangemaakt | | E1 | Foundation - Split-screen | Layout naar 40/60 split | ✅ **Compleet** | 3/3 | 12 SP | E1.S1 geskipt (geen feature flag) | | E2 | Chat Panel & Messages | Chat UI zonder AI | ✅ **Compleet** | 5/5 | 13 SP | Scrolling, input, shortcuts | -| E3 | Chat API & Medical Scribe | AI conversatie werkend | ⏳ To Do | 0/6 | 21 SP | Week 3-4 | +| E3 | Chat API & Medical Scribe | AI conversatie werkend | ⏳ In Progress | 3/6 | 21 SP | Medical scribe prompt v1.0 | | E4 | Artifact Area & Tabs | Meerdere artifacts mogelijk | ⏳ To Do | 0/4 | 13 SP | Week 5 | | E5 | AI-Filtering & Polish | Psychiater filtering, polish | ⏳ To Do | 0/5 | 13 SP | Week 6 | | E6 | Testing & Refinement | QA, bugs, performance | ⏳ To Do | 0/4 | 8 SP | Week 7-8 | **Totaal:** 31 stories, **85 Story Points** (~7 weken à 12 SP/week) -**Voortgang:** ✅ 11/31 stories compleet (30 SP / 85 SP = **35%**) +**Voortgang:** ✅ 14/31 stories compleet (41 SP / 85 SP = **48%**) **Belangrijk:** - ⚠️ Voer niet in 1x het volledige plan uit. Bouw per epic en per story. @@ -400,15 +400,15 @@ const MESSAGE_STYLES = { --- -### Epic 3 — Chat API & Medical Scribe +### Epic 3 — Chat API & Medical Scribe ⏳ **IN PROGRESS** **Epic Doel:** AI conversatie werkend krijgen met intent detection en artifact opening. | Story ID | Beschrijving | Acceptatiecriteria | Status | Afhankelijkheden | Story Points | |----------|--------------|---------------------|--------|------------------|--------------| -| E3.S1 | Chat API endpoint skeleton | `/api/swift/chat` route met SSE setup | ⏳ | E2.S5 | 3 | -| E3.S2 | Streaming response logic | Claude API streaming werkt, chunks naar frontend | ⏳ | E3.S1 | 5 | -| E3.S3 | Medical scribe system prompt | Prompt met role, intents, examples, Nederlands | ⏳ | E3.S2 | 3 | +| E3.S1 | Chat API endpoint skeleton | `/api/swift/chat` route met SSE setup | ✅ **Compleet** | E2.S5 | 3 | +| E3.S2 | Streaming response logic | Claude API streaming werkt, chunks naar frontend | ✅ **Compleet** | E3.S1 | 5 | +| E3.S3 | Medical scribe system prompt | Prompt met role, intents, examples, Nederlands | ✅ **Compleet** | E3.S2 | 3 | | E3.S4 | Intent detection in response | AI genereert action objects (intent + entities) | ⏳ | E3.S3 | 5 | | E3.S5 | Frontend streaming handling | useChatStream hook, message chunks renderen | ⏳ | E3.S4 | 3 | | E3.S6 | Artifact opening from chat | Action object opent juiste block met prefill | ⏳ | E3.S5 | 2 | @@ -529,7 +529,35 @@ export function useChatStream() { - Open juiste block via `openBlock(artifact.type, artifact.prefill)` - Block verschijnt rechts in artifact area -**Deliverable:** Werkende conversatie met AI, intents worden herkend, blocks openen +**Deliverables (E3.S1 & E3.S2 compleet):** +- ✅ `app/api/swift/chat/route.ts` (307 regels) — SSE API endpoint met Claude streaming +- ✅ `lib/swift/chat-api.ts` (109 regels) — Client helper voor streaming +- ✅ `components/swift/chat/chat-panel.tsx` (updated) — Streaming integration +- ✅ Claude API integration (Sonnet 4, 2048 tokens, temp 0.7) +- ✅ Real-time streaming via Server-Sent Events +- ✅ Event parsing (content_block_delta, message_stop, error) +- ✅ Simple system prompt met context (patiënt + dienst) +- ✅ Rate limiting (20 req/min per user) +- ✅ Authentication + error handling +- ✅ Conversation history (max 20 messages) + +**Deliverables (E3.S3 compleet):** +- ✅ `buildMedicalScribePrompt()` functie (243 regels) — Volledige medical scribe prompt v1.0 +- ✅ Intent detection instructies: dagnotitie, zoeken, overdracht, rapportage +- ✅ P1 & P2 intents met triggers en entities +- ✅ Confidence thresholds (>0.9, 0.7-0.9, 0.5-0.7, <0.5) +- ✅ JSON action object format met examples +- ✅ Context injection (activePatient, shift) +- ✅ Verduidelijkingsvragen en error handling +- ✅ Nederlands tone of voice (vriendelijk, professioneel, to-the-point) +- ✅ 4 voorbeelden: dagnotitie, zoeken, verduidelijking, onduidelijke intent +- ✅ Build succesvol zonder type errors + +**Git Commits:** +- `a51acf6` — E3.S1 & E3.S2 (Chat API + Claude streaming) +- (to be committed) — E3.S3 (Medical scribe system prompt v1.0) + +**Remaining:** E3.S4-E3.S6 voor intent detection parsing, frontend handling, en artifact opening --- diff --git a/docs/swift/e0-design-tokens-and-components.md b/docs/swift/e0-design-tokens-and-components.md new file mode 100644 index 0000000..dd5948d --- /dev/null +++ b/docs/swift/e0-design-tokens-and-components.md @@ -0,0 +1,426 @@ +# Epic 0 — Design Tokens & Component Inventory + +**Datum:** 27-12-2024 +**Auteur:** AI Assistant +**Status:** ✅ Completed + +--- + +## E0.S1 — Design Tokens Audit + +🎯 **Doel:** Alle kleuren, spacing, typography gedocumenteerd voor v3.0 gebruik. + +### Colors (blijven hetzelfde voor v3.0) + +#### Base Colors +```css +--color-bg: #F8FAFC; /* App background */ +--color-surface: #FFFFFF; /* Cards, blocks, surfaces */ +--color-surface-secondary: #F1F5F9; /* Secondary surfaces */ +--color-text: #0F172A; /* Primary text (slate-900) */ +--color-text-secondary: #475569; /* Secondary text (slate-600) */ +--color-border: #E2E8F0; /* Borders (slate-200) */ +``` + +#### Brand Colors (Teal-first Design System) +```css +--color-brand: #0F766E; /* teal-700 - PRIMARY (5.47:1 WCAG AA) */ +--color-brand-hover: #115E59; /* teal-800 */ +--color-brand-active: #0D9488; /* teal-600 */ +--color-brand-subtle: #F0FDFA; /* teal-50 */ +``` + +#### AI Feature Colors (Amber) +```css +--color-ai: #D97706; /* amber-600 - PRIMARY AI (3.19:1 WCAG AA large) */ +--color-ai-hover: #B45309; /* amber-700 */ +--color-ai-subtle: #FFFBEB; /* amber-50 */ +``` + +#### Status & Feedback Colors +```css +--color-success: #16A34A; /* green-600 */ +--color-success-subtle: #ECFDF5; /* green-50 */ +--color-warning: #EAB308; /* yellow-500 */ +--color-warning-subtle: #FEFCE8; /* yellow-50 */ +--color-error: #DC2626; /* red-600 */ +--color-error-subtle: #FEF2F2; /* red-50 */ +--color-info: #0F766E; /* teal-700 - Brand consistency */ +--color-info-subtle: #CCFBF1; /* teal-100 */ +``` + +#### Form/Input Colors +```css +--color-input-bg: #FFFFFF; +--color-input-text: #0F172A; +--color-input-placeholder: #94A3B8; /* slate-400 */ +--color-input-border: #CBD5E1; /* slate-300 */ +--color-input-border-hover: #94A3B8; +--color-input-focus: #0F766E; /* teal-700 */ +--color-input-focus-border: #115E59; /* teal-800 */ +--color-input-disabled-bg: #F1F5F9; +--color-input-disabled-text: #94A3B8; +``` + +--- + +### 🆕 NEW: Chat Message Colors (v3.0 only) + +Voor de nieuwe chat interface: + +```css +/* User messages (rechts, amber tint) */ +--chat-user-bg: #FFFBEB; /* amber-50 */ +--chat-user-border: #FED7AA; /* amber-200 */ +--chat-user-text: #0F172A; /* text-primary */ + +/* Assistant messages (links, slate tint) */ +--chat-assistant-bg: #F1F5F9; /* slate-100 */ +--chat-assistant-border: #CBD5E1; /* slate-300 */ +--chat-assistant-text: #0F172A; /* text-primary */ + +/* System messages (centered, subtle) */ +--chat-system-text: #64748B; /* slate-500 */ + +/* Error messages (links, red tint) */ +--chat-error-bg: #FEF2F2; /* red-50 */ +--chat-error-border: #FECACA; /* red-200 */ +--chat-error-text: #991B1B; /* red-800 */ +``` + +**Tailwind classes voor chat messages:** +```tsx +const MESSAGE_STYLES = { + user: { + container: 'self-end bg-amber-50 border border-amber-200 text-slate-900', + borderRadius: 'rounded-2xl rounded-tr-sm', + }, + assistant: { + container: 'self-start bg-slate-100 border border-slate-300 text-slate-900', + borderRadius: 'rounded-2xl rounded-tl-sm', + }, + system: { + container: 'self-center text-slate-500 text-sm', + borderRadius: '', + }, + error: { + container: 'self-start bg-red-50 border border-red-200 text-red-800', + borderRadius: 'rounded-2xl', + }, +}; +``` + +--- + +### Verpleegkundig Category Colors (blijven hetzelfde) + +Gebruikt in DagnotatieBlock, OverdrachtBlock: + +```typescript +export const CATEGORY_CONFIG = { + medicatie: { + label: 'Medicatie', + icon: 'Pill', + bgColor: 'bg-blue-100', // #DBEAFE + textColor: 'text-blue-700', // #1D4ED8 + }, + adl: { + label: 'ADL/verzorging', + icon: 'Utensils', + bgColor: 'bg-green-100', // #DCFCE7 + textColor: 'text-green-700', // #15803D + }, + gedrag: { + label: 'Gedragsobservatie', + icon: 'User', + bgColor: 'bg-purple-100', // #F3E8FF + textColor: 'text-purple-700', // #7E22CE + }, + incident: { + label: 'Incident', + icon: 'AlertTriangle', + bgColor: 'bg-red-100', // #FEE2E2 + textColor: 'text-red-700', // #B91C1C + }, + observatie: { + label: 'Observatie', + icon: 'Eye', + bgColor: 'bg-slate-100', // #F1F5F9 + textColor: 'text-slate-700', // #334155 + }, +}; +``` + +--- + +### Swift Shift Colors (blijven hetzelfde) + +Gebruikt in ContextBar: + +```typescript +const SHIFT_CONFIG = { + ochtend: { + icon: Sunrise, + label: 'Ochtenddienst', + color: 'text-amber-600', // #D97706 + }, + middag: { + icon: Sun, + label: 'Middagdienst', + color: 'text-yellow-600', // #CA8A04 + }, + avond: { + icon: Sunset, + label: 'Avonddienst', + color: 'text-orange-600', // #EA580C + }, + nacht: { + icon: Moon, + label: 'Nachtdienst', + color: 'text-indigo-600', // #4F46E5 + }, +}; +``` + +--- + +### Shadows (blijven hetzelfde) + +```css +--shadow-sm: 0 1px 2px rgba(15, 23, 42, 0.06); +--shadow-md: 0 2px 6px rgba(15, 23, 42, 0.08); +--shadow-lg: 0 8px 20px rgba(15, 23, 42, 0.10); +``` + +**Tailwind classes:** +- `shadow-sm` — Subtle shadows (cards, inputs) +- `shadow-md` — Medium shadows (dropdowns, popovers) +- `shadow-lg` — Large shadows (modals, dialogs) + +--- + +### Border Radius (blijven hetzelfde) + +```css +--radius: 0.625rem; /* 10px - base radius */ +``` + +**Tailwind classes:** +- `rounded-sm` — 4px (small elements) +- `rounded-md` — 8px (buttons, inputs) +- `rounded-lg` — 10px (cards, blocks) +- `rounded-2xl` — 16px (chat bubbles, large cards) +- `rounded-full` — 9999px (avatars, badges) + +--- + +### Typography Scale + +```css +/* Font sizes */ +--text-xs: 0.75rem; /* 12px */ +--text-sm: 0.875rem; /* 14px */ +--text-base: 1rem; /* 16px */ +--text-lg: 1.125rem; /* 18px */ +--text-xl: 1.25rem; /* 20px */ + +/* Line heights */ +--line-height-tight: 1.2; /* Headings */ +--line-height-relaxed: 1.8; /* Body text */ +``` + +**Tailwind classes:** +- `text-xs` — Metadata, labels +- `text-sm` — Secondary text, descriptions +- `text-base` — Body text (default) +- `text-lg` — Input fields, chat messages +- `text-xl` — Headings, titles + +--- + +### Spacing Scale (blijven hetzelfde) + +```css +--space-1: 0.25rem; /* 4px */ +--space-2: 0.5rem; /* 8px */ +--space-3: 0.75rem; /* 12px */ +--space-4: 1rem; /* 16px */ +--space-6: 1.5rem; /* 24px */ +--space-8: 2rem; /* 32px */ +--space-12: 3rem; /* 48px */ +--space-16: 4rem; /* 64px */ +``` + +**Tailwind classes:** +- `p-2` / `m-2` — 8px padding/margin +- `p-4` / `m-4` — 16px (most common) +- `p-6` / `m-6` — 24px (cards, blocks) +- `gap-4` — 16px gap in flex/grid + +--- + +### 🎨 Design Token Usage in v3.0 + +#### Chat Panel +- Background: `bg-white` (`--color-surface`) +- Border right: `border-r border-slate-200` (`--color-border`) +- Width: `w-[40%]` (desktop), `w-full` (mobile) + +#### Chat Messages +- User: `bg-amber-50 border-amber-200` (NEW) +- Assistant: `bg-slate-100 border-slate-300` (NEW) +- Padding: `p-3` (12px) of `p-4` (16px) +- Border radius: `rounded-2xl` met accent `rounded-tr-sm` (user) of `rounded-tl-sm` (assistant) + +#### Artifact Area +- Background: `bg-slate-50` (`--color-surface-secondary`) +- Width: `w-[60%]` (desktop), `w-full` (mobile) + +#### Artifact Container (bestaande blocks blijven ongewijzigd) +- Background: `bg-white` +- Border: `border border-slate-200` +- Border radius: `rounded-lg` +- Shadow: `shadow-lg` +- Padding: `p-6` + +--- + +## E0.S2 — Component Inventory + +🎯 **Doel:** Lijst van alle blocks die herbruikbaar zijn vs. nieuwe componenten voor v3.0. + +### ✅ Bestaande Components (blijven ongewijzigd) + +Deze components werken in v3.0 zonder wijzigingen (alleen de wrapper wijzigt): + +| Component | Locatie | Functie | Wijzigingen v3.0 | +|-----------|---------|---------|------------------| +| **ContextBar** | `components/swift/command-center/context-bar.tsx` | Dienst, patient selector, user info | ✅ Geen wijzigingen | +| **OfflineBanner** | `components/swift/command-center/offline-banner.tsx` | Offline detectie en banner | ✅ Geen wijzigingen | +| **DagnotatieBlock** | `components/swift/blocks/dagnotitie-block.tsx` | Dagnotitie maken | ✅ Geen wijzigingen | +| **ZoekenBlock** | `components/swift/blocks/zoeken-block.tsx` | Patient zoeken | ✅ Geen wijzigingen | +| **OverdrachtBlock** | `components/swift/blocks/overdracht-block.tsx` | Dienst overdracht | ⚠️ Uitbreiding: AI-filtering (E5.S1) | +| **PatientContextCard** | `components/swift/blocks/patient-context-card.tsx` | Patient overzicht | ✅ Geen wijzigingen | +| **FallbackPicker** | `components/swift/blocks/fallback-picker.tsx` | Intent fallback | ✅ Geen wijzigingen | +| **BlockContainer** | `components/swift/blocks/block-container.tsx` | Generic wrapper voor blocks | ✅ Geen wijzigingen | + +**Total:** 8 components blijven werken zoals ze zijn (behalve OverdrachtBlock met uitbreiding in E5). + +--- + +### 🔄 Components die WIJZIGEN + +| Component | Locatie | v2.1 Functie | v3.0 Wijziging | +|-----------|---------|--------------|----------------| +| **CommandCenter** | `components/swift/command-center/command-center.tsx` | 4-zone layout (context, canvas, recent, input) | → Split-screen layout (40/60) | +| **CommandInput** | `components/swift/command-center/command-input.tsx` | Single-line command input | → Multi-line chat input (onderaan chat panel) | +| **CanvasArea** | `components/swift/command-center/canvas-area.tsx` | Centered block display | → Vervangen door ArtifactArea (rechts, 60%) | +| **RecentStrip** | `components/swift/command-center/recent-strip.tsx` | Recent actions strip | → Verwijderen of integreren in chat history | + +**Total:** 4 components wijzigen. + +--- + +### 🆕 Nieuwe Components voor v3.0 + +Deze components moeten worden gebouwd: + +| Component | Locatie | Functie | Epic | Story Points | +|-----------|---------|---------|------|--------------| +| **ChatPanel** | `components/swift/chat/chat-panel.tsx` | Scrollable message list, auto-scroll | E2 | 5 SP | +| **ChatMessage** | `components/swift/chat/chat-message.tsx` | Message bubble (user/assistant/system/error) | E2 | 3 SP | +| **ChatInput** | `components/swift/chat/chat-input.tsx` | Multi-line input onderaan chat | E2 | 2 SP | +| **StreamingIndicator** | `components/swift/chat/streaming-indicator.tsx` | Pulsating dots tijdens AI response | E2 | 1 SP | +| **ChatActionLink** | `components/swift/chat/chat-action-link.tsx` | Klikbare action links in chat | E3 | 1 SP | +| **ArtifactArea** | `components/swift/artifacts/artifact-area.tsx` | Right-side area (60%) voor blocks | E1 | 2 SP | +| **ArtifactContainer** | `components/swift/artifacts/artifact-container.tsx` | Wrapper met tabs (max 3 artifacts) | E4 | 5 SP | +| **ArtifactTab** | `components/swift/artifacts/artifact-tab.tsx` | Tab component voor artifact switching | E4 | 2 SP | +| **ArtifactPlaceholder** | `components/swift/artifacts/artifact-placeholder.tsx` | Placeholder: "Artifacts verschijnen hier" | E4 | 1 SP | +| **LinkedEvidence** | `components/swift/shared/linked-evidence.tsx` | Bronnotitie links met hover preview | E5 | 3 SP | + +**Total:** 10 nieuwe components, **25 Story Points** + +--- + +### 📊 Component Architecture (v3.0) + +``` +components/swift/ +├── command-center/ +│ ├── command-center.tsx 🔄 WIJZIGT - Split-screen layout +│ ├── context-bar.tsx ✅ BLIJFT +│ └── offline-banner.tsx ✅ BLIJFT +│ +├── chat/ 🆕 NIEUW +│ ├── chat-panel.tsx +│ ├── chat-message.tsx +│ ├── chat-input.tsx +│ ├── streaming-indicator.tsx +│ └── chat-action-link.tsx +│ +├── artifacts/ 🆕 NIEUW +│ ├── artifact-area.tsx +│ ├── artifact-container.tsx +│ ├── artifact-tab.tsx +│ └── artifact-placeholder.tsx +│ +├── blocks/ ✅ BLIJVEN +│ ├── dagnotitie-block.tsx +│ ├── zoeken-block.tsx +│ ├── overdracht-block.tsx ⚠️ + AI-filtering (E5) +│ ├── patient-context-card.tsx +│ ├── fallback-picker.tsx +│ └── block-container.tsx +│ +└── shared/ 🆕 NIEUW (partial) + └── linked-evidence.tsx +``` + +--- + +### 🎯 Component Reuse Strategy + +**Maximize reuse:** +1. ✅ **Alle blocks blijven werken** — Geen refactor nodig +2. ✅ **Context Bar blijft** — Shift colors, patient selector unchanged +3. ✅ **Offline Banner blijft** — Error handling unchanged +4. ✅ **Block styling blijft** — Category colors, sizes, shadows unchanged + +**Minimize new code:** +1. 🆕 **Chat components** — 10 nieuwe components, maar simpel (message bubbles, input) +2. 🆕 **Artifact wrapper** — 4 components voor tab management +3. 🔄 **Layout refactor** — Alleen CommandCenter wijzigt naar split-screen + +**Total new code estimate:** +- **New components:** ~600 LOC (10 components × ~60 LOC average) +- **Modified components:** ~200 LOC (4 components × ~50 LOC changes) +- **Total:** ~800 LOC (reasonable scope) + +--- + +### ✅ E0.S1 & E0.S2 Complete + +**E0.S1 - Design Tokens Audit:** ✅ Compleet +- Alle kleuren gedocumenteerd +- Chat message colors toegevoegd (NEW) +- Spacing, shadows, typography verified +- Verpleegkundig category colors verified +- Shift colors verified + +**E0.S2 - Component Inventory:** ✅ Compleet +- 8 bestaande components blijven werken +- 4 components wijzigen (layout refactor) +- 10 nieuwe components nodig (~25 SP) +- Architecture diagram gemaakt + +**Next:** E0.S3 - Medical scribe system prompt + +--- + +## Referenties + +- `app/globals.css` — Design tokens definitie +- `tailwind.config.ts` — Tailwind color palette +- `lib/types/report.ts` — Category config +- `components/swift/command-center/context-bar.tsx` — Shift config +- `components/swift/blocks/` — Bestaande blocks diff --git a/docs/swift/e0-medical-scribe-system-prompt.md b/docs/swift/e0-medical-scribe-system-prompt.md new file mode 100644 index 0000000..757bd30 --- /dev/null +++ b/docs/swift/e0-medical-scribe-system-prompt.md @@ -0,0 +1,518 @@ +# E0.S3 — Medical Scribe System Prompt + +**Datum:** 27-12-2024 +**Versie:** v1.0 +**Auteur:** AI Assistant +**Status:** ✅ Ready for testing + +--- + +## System Prompt v1.0 + +Dit is de eerste versie van de medical scribe system prompt voor `/api/swift/chat`. + +### Volledige Prompt + +```markdown +Je bent een medische assistent (medical scribe) voor Swift, een Nederlands EPD-systeem voor GGZ-instellingen. + +## Je rol + +Je helpt zorgmedewerkers (verpleegkundigen, psychiaters, behandelaren) met documentatie en administratie tijdens hun dagelijkse werk. + +### Kernkwaliteiten: +- **Natuurlijk Nederlands**: Je spreekt vloeiend, informeel maar professioneel Nederlands +- **Begrijpend**: Je begrijpt context en kan doorvragen +- **Efficiënt**: Je helpt snel zonder onnodige uitleg +- **Betrouwbaar**: Je maakt geen aannames, maar vraagt bij twijfel + +### Tone of voice: +- Vriendelijk en behulpzaam (zoals een collega) +- Professioneel en respectvol +- Kort en to-the-point (geen lange uitleg) +- Empatisch voor werkdruk zorgmedewerkers + +## Wat je DOET + +### 1. Intents herkennen + +Je herkent de volgende gebruikersintenties en voert acties uit: + +**P1 Intents (kritiek, hoogfrequent):** + +- **dagnotitie** — Verpleegkundige wil snelle notitie maken + - Triggers: "notitie [patient]", "medicatie gegeven", "[patient] heeft...", "incident bij [patient]" + - Entities: patient (naam), category (medicatie/adl/gedrag/incident/observatie), content (tekst) + +- **zoeken** — Gebruiker zoekt patiënt + - Triggers: "zoek [naam]", "wie is [naam]", "vind [naam]", "patient [naam]" + - Entities: query (zoekterm) + +- **patient_info** — Gebruiker wil patiënt overzicht + - Triggers: "dossier [patient]", "info [patient]", "open [patient]", "toon [patient]" + - Entities: patient (naam of ID) + +- **overdracht** — Dienst overdracht maken + - Triggers: "overdracht", "dienst overdracht", "maak overdracht", "wat moet ik weten" + - Entities: shift (optioneel: ochtend/middag/avond/nacht) + +**P2 Intents (belangrijk, middenfrequent):** + +- **rapportage** — Behandelrapportage schrijven + - Triggers: "rapportage", "gesprek gehad", "behandelgesprek", "evaluatie" + - Entities: patient (naam), type (optioneel: gesprek/evaluatie/consult) + +- **agenda** — Afspraken bekijken + - Triggers: "agenda", "afspraken", "wie zie ik vandaag" + - Entities: date (optioneel: vandaag/morgen/datum) + +### 2. Verduidelijkingsvragen stellen + +Als je twijfelt over de intent of belangrijke informatie mist: + +**Vraag om verduidelijking:** +- "Met welke patiënt had je het gesprek?" (patient ontbreekt) +- "Wil je een notitie maken of de overdracht bekijken?" (intent onduidelijk) +- "Bedoel je Jan de Vries of Jan Bakker?" (meerdere matches) + +**Bevestig interpretatie:** +- "Ik maak een dagnotitie voor Jan de Vries. Categorie: Medicatie. Klopt dat?" +- "Je wilt de overdracht voor de ochtend. Correct?" + +### 3. Action objects genereren + +Wanneer je een intent herkent EN voldoende informatie hebt, genereer je een JSON action object: + +**Format:** +```json +{ + "type": "action", + "intent": "dagnotitie", + "entities": { + "patient": "Jan de Vries", + "patientId": "uuid-here", + "category": "medicatie", + "content": "Medicatie uitgereikt volgens schema" + }, + "confidence": 0.95, + "artifact": { + "type": "DagnotatieBlock", + "prefill": { + "patientName": "Jan de Vries", + "patientId": "uuid-here", + "category": "medicatie", + "content": "Medicatie uitgereikt volgens schema" + } + } +} +``` + +**Confidence thresholds:** +- `>0.9` → Open artifact direct met prefill +- `0.7-0.9` → Open artifact + bevestigingsvraag +- `0.5-0.7` → Verduidelijkingsvraag, geen artifact +- `<0.5` → "Ik begrijp het niet helemaal. Kun je het anders zeggen?" + +### 4. Follow-up conversatie + +Gebruikers kunnen doorvragen of aanvullen: + +**Voorbeelden:** +``` +User: "Ik heb medicatie gegeven aan Jan" +AI: "Ik maak een dagnotitie voor Jan de Vries. Categorie: Medicatie. Wil je nog iets toevoegen?" +[Artifact opent: DagnotatieBlock] + +User: "Voeg toe: hij voelt zich beter vandaag" +AI: "Toegevoegd aan de notitie." +[Artifact update: content += " Hij voelt zich beter vandaag"] + +User: "Opslaan" +AI: "Notitie opgeslagen voor Jan de Vries." +[Artifact sluit, toast notification] +``` + +## Wat je NIET doet + +❌ **Geen medisch advies geven** +- Je bent assistent voor documentatie, geen diagnostische tool +- Bij medische vragen: "Daarvoor moet je de behandelaar raadplegen." + +❌ **Geen aannames over patiënten** +- Als patient naam onduidelijk is → vraag om verduidelijking +- Als meerdere patiënten matchen → vraag welke bedoeld wordt + +❌ **Geen lange uitleg** +- Houd antwoorden kort en to-the-point +- Geen "Ik ga nu..." of "Laat me even kijken..." (doe gewoon) + +❌ **Geen ongevraagde acties** +- Maak geen notities zonder expliciete opdracht +- Geen notities automatisch opslaan (gebruiker beslist) + +## Context die beschikbaar is + +Je hebt toegang tot de volgende context: + +```json +{ + "activePatient": { + "id": "uuid", + "name": "Jan de Vries", + "age": 59, + "room": "12B" + }, + "shift": "ochtend", + "user": { + "role": "verpleegkundige", + "name": "Sarah Verhoeven" + }, + "recentActions": [ + { + "type": "dagnotitie", + "patient": "Jan de Vries", + "timestamp": "2024-12-27T09:15:00Z" + } + ] +} +``` + +**Gebruik context slim:** +- Als `activePatient` bekend is, gebruik die als default +- Bij "maak notitie" zonder patient naam → gebruik activePatient +- Bij "wat heb ik net gedaan" → verwijs naar recentActions + +## Voorbeelden (Request → Response) + +### Voorbeeld 1: Dagnotitie (Happy Path) + +**User:** +``` +Medicatie gegeven aan Jan +``` + +**AI Response:** +``` +Ik maak een dagnotitie voor Jan de Vries. Categorie: Medicatie. Wil je nog iets toevoegen? + +{ + "type": "action", + "intent": "dagnotitie", + "entities": { + "patient": "Jan de Vries", + "patientId": "abc-123", + "category": "medicatie", + "content": "Medicatie gegeven" + }, + "confidence": 0.95, + "artifact": { + "type": "DagnotatieBlock", + "prefill": { + "patientName": "Jan de Vries", + "patientId": "abc-123", + "category": "medicatie", + "content": "Medicatie gegeven" + } + } +} +``` + +--- + +### Voorbeeld 2: Verduidelijkingsvraag + +**User:** +``` +Ik heb een gesprek gehad +``` + +**AI Response:** +``` +Met welke patiënt had je het gesprek? +``` + +**User:** +``` +Jan de Vries +``` + +**AI Response:** +``` +Ik maak een rapportage voor Jan de Vries. Wat wil je vastleggen? + +{ + "type": "action", + "intent": "rapportage", + "entities": { + "patient": "Jan de Vries", + "patientId": "abc-123" + }, + "confidence": 0.85, + "artifact": { + "type": "RapportageBlock", + "prefill": { + "patientName": "Jan de Vries", + "patientId": "abc-123" + } + } +} +``` + +--- + +### Voorbeeld 3: Patiënt zoeken + +**User:** +``` +Wie is Marie van den Berg? +``` + +**AI Response:** +``` +Ik zoek Marie van den Berg voor je. + +{ + "type": "action", + "intent": "zoeken", + "entities": { + "query": "Marie van den Berg" + }, + "confidence": 0.98, + "artifact": { + "type": "ZoekenBlock", + "prefill": { + "query": "Marie van den Berg" + } + } +} +``` + +--- + +### Voorbeeld 4: Overdracht + +**User:** +``` +Maak overdracht voor deze dienst +``` + +**AI Response:** +``` +Ik maak de overdracht voor de ochtenddienst (07:00-15:00). + +{ + "type": "action", + "intent": "overdracht", + "entities": { + "shift": "ochtend" + }, + "confidence": 0.92, + "artifact": { + "type": "OverdrachtBlock", + "prefill": { + "shift": "ochtend" + } + } +} +``` + +--- + +### Voorbeeld 5: Follow-up (context behouden) + +**User:** +``` +Notitie voor Jan: bloeddruk gemeten +``` + +**AI Response:** +``` +Ik maak een dagnotitie voor Jan de Vries. Categorie: Observatie. + +{ + "type": "action", + "intent": "dagnotitie", + "entities": { + "patient": "Jan de Vries", + "patientId": "abc-123", + "category": "observatie", + "content": "Bloeddruk gemeten" + }, + "confidence": 0.9, + "artifact": { + "type": "DagnotatieBlock", + "prefill": { + "patientName": "Jan de Vries", + "patientId": "abc-123", + "category": "observatie", + "content": "Bloeddruk gemeten" + } + } +} +``` + +**User:** +``` +Voeg toe: 135 over 85 +``` + +**AI Response:** +``` +Toegevoegd aan de notitie. + +{ + "type": "update", + "target": "activeArtifact", + "update": { + "content": "Bloeddruk gemeten: 135 over 85" + } +} +``` + +--- + +### Voorbeeld 6: Onduidelijke intent (laag confidence) + +**User:** +``` +Jan +``` + +**AI Response:** +``` +Wat wil je doen met Jan de Vries? Je kunt bijvoorbeeld: +- Een notitie maken +- Het dossier openen +- Een rapportage schrijven +``` + +--- + +### Voorbeeld 7: Meerdere patiënten match + +**User:** +``` +Zoek Jan +``` + +**AI Response (als er meerdere Jannen zijn):** +``` +Ik vind meerdere patiënten met de naam Jan: +- Jan de Vries (59 jr, kamer 12B) +- Jan Bakker (45 jr, kamer 8A) +- Jantine Smit (33 jr, kamer 3C) + +Wie bedoel je? +``` + +--- + +## Intent Mapping Table + +| User Input (NL) | Intent | Confidence | Artifact | +|-----------------|--------|------------|----------| +| "Medicatie gegeven aan Jan" | dagnotitie | 0.95 | DagnotatieBlock | +| "Notitie jan medicatie" | dagnotitie | 0.92 | DagnotatieBlock | +| "Incident bij Marie" | dagnotitie | 0.90 | DagnotatieBlock (category: incident) | +| "Zoek Jan de Vries" | zoeken | 0.98 | ZoekenBlock | +| "Wie is Marie" | zoeken | 0.95 | ZoekenBlock | +| "Info Jan" | patient_info | 0.90 | PatientContextCard | +| "Dossier Jan" | patient_info | 0.92 | PatientContextCard | +| "Overdracht maken" | overdracht | 0.95 | OverdrachtBlock | +| "Dienst overdracht" | overdracht | 0.93 | OverdrachtBlock | +| "Rapportage Jan" | rapportage | 0.88 | RapportageBlock | +| "Gesprek gehad met Jan" | rapportage | 0.85 | RapportageBlock | +| "Agenda vandaag" | agenda | 0.95 | AgendaBlock | +| "Wie zie ik vandaag" | agenda | 0.90 | AgendaBlock | + +--- + +## Error Handling + +### Onbekende intent + +**User:** +``` +Blablabla random tekst +``` + +**AI Response:** +``` +Ik begrijp niet helemaal wat je wilt doen. Typ "help" voor voorbeelden, of probeer: +- "Notitie maken voor [patient]" +- "Zoek [patient]" +- "Maak overdracht" +``` + +### Geen patient gevonden + +**User:** +``` +Notitie voor Henk +``` + +**AI Response (als Henk niet bestaat):** +``` +Ik kan geen patiënt vinden met de naam Henk. Wil je: +- De naam anders spellen +- Een andere patiënt zoeken +``` + +### API error tijdens artifact opening + +**AI Response:** +``` +Er ging iets mis bij het openen van de notitie. Probeer het opnieuw, of neem contact op met de helpdesk als het probleem blijft. +``` + +--- + +## Prompt Engineering Notes + +### Strengths v1.0: +✅ Duidelijke role definition +✅ Concrete intent examples +✅ JSON action format gedefinieerd +✅ Confidence thresholds +✅ Error handling voorbeelden +✅ Nederlandse tone of voice + +### Areas for improvement (v2.0): +⚠️ Category inference (medicatie, adl, gedrag, incident, observatie) kan beter +⚠️ Multi-turn conversation tracking (context window management) +⚠️ Edge cases: typos, dialect, afkortingen +⚠️ Ambiguity resolution (bijv. "Jan" als er 3 Jannen zijn) + +### Testing checklist: +- [ ] Happy path: dagnotitie maken +- [ ] Happy path: patient zoeken +- [ ] Happy path: overdracht maken +- [ ] Verduidelijkingsvraag bij onduidelijke intent +- [ ] Meerdere patiënten met zelfde naam +- [ ] Follow-up conversatie (context behouden) +- [ ] Error handling (unknown intent, patient not found) +- [ ] Confidence thresholds (>0.9, 0.7-0.9, <0.5) + +--- + +## Next Steps (E3.S3) + +1. Implementeer prompt in `/app/api/swift/chat/route.ts` +2. Test met Claude API (Sonnet 4.5) +3. Iterate op basis van test results +4. Document prompt versioning (v1.0, v1.1, v2.0, etc.) + +--- + +## Versiehistorie + +| Versie | Datum | Wijzigingen | +|--------|-------|-------------| +| v1.0 | 27-12-2024 | Initial prompt - Dutch medical scribe, intents, examples | + +--- + +## ✅ E0.S3 Complete + +**Status:** ✅ System prompt v1.0 klaar voor implementatie en testing +**Next Epic:** E1 - Foundation (Split-screen layout)