feat(swift): voeg chat orchestration toe (E5)

Epic 5 compleet: Chat orchestration voor Swift Agenda Planning.
Alle agenda intents worden nu correct gerouteerd naar AgendaBlock
met user-friendly error handling en fallback opties.

E5.S1 - Action Routing (2 SP)
- routeIntentToArtifact() functie in action-parser
- Maps agenda intents naar juiste AgendaBlock mode
- Confidence threshold enforcement (< 0.7 → fallback)
- Required entity validation (patient voor create, identifier voor reschedule)
- Command-input gebruikt nieuwe routing ipv legacy openBlock
- Migratie naar modern artifact systeem (openArtifact)

E5.S2 - Chat Prompt Update (2 SP)
- 4 agenda intents toegevoegd aan Swift chat system prompt
- agenda_query: afspraken opvragen op datumrange
- create_appointment: nieuwe afspraak maken (required: patient, datetime)
- cancel_appointment: afspraak annuleren (disambiguation support)
- reschedule_appointment: afspraak verzetten (required: identifier)
- Entity extraction rules gedocumenteerd (dateRange, datetime, identifier)
- 4 complete voorbeelden met JSON action format
- Clarification questions voor incomplete data
- Prompt size: ~325 → ~525 regels (+60%)

E5.S3 - Error States (2 SP)
- AgendaErrorState component voor full-page errors
- AgendaErrorAlert component voor inline form errors
- getUserFriendlyMessage() vertaalt technical → user-friendly Dutch
- Auto-redirect bij auth errors (401 → /login)
- Fallback link naar /epd/agenda in alle error states
- Context-aware messaging (query/create/cancel/reschedule)
- Retry functionaliteit voor recoverable errors
- Dev-only technical details collapsible

Error message mapping:
- 401 → "Je sessie is verlopen. Log opnieuw in." + auto-redirect
- 403 → "Je hebt geen toegang tot deze afspraak."
- 404 → "De gevraagde afspraak kon niet worden gevonden."
- 500 → "Er ging iets mis op de server. Probeer het opnieuw."
- Network → "Geen internetverbinding. Controleer je netwerkverbinding."
- Timeout → "De aanvraag duurde te lang. Probeer het opnieuw."

Components updated:
- command-input: gebruikt routeIntentToArtifact + openArtifact
- agenda-create-form: gebruikt AgendaErrorAlert met fallback link
- chat/route: uitgebreide system prompt met agenda sectie

Nieuwe files:
- lib/swift/action-parser.ts: routeIntentToArtifact() functie
- components/swift/artifacts/blocks/agenda-error-state.tsx (225 regels)
- docs/swift/implementation-e5-s1-action-routing.md
- docs/swift/implementation-e5-s2-chat-prompt.md
- docs/swift/implementation-e5-s3-error-states.md

Documentatie:
- Bouwplan bijgewerkt: Epic 5 → Done
- 3 implementation docs met API specs en testing scenarios
- Error handling best practices gedocumenteerd

Progress: 48 SP / 51 SP (94%) - Epic 6 (QA) remaining

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
colinislit
2025-12-27 22:44:18 +01:00
parent a6b63665e1
commit 14a9d34337
9 changed files with 1445 additions and 19 deletions

View File

@@ -0,0 +1,325 @@
# Epic 5.S2 Implementation: Chat Prompt Update
**Story**: E5.S2 - Chat prompt update
**Date**: 2025-12-27
**Status**: ✅ Complete
**Story Points**: 2 SP
---
## 🎯 Objective
Update the Swift chat API system prompt to include agenda intent descriptions, entity extraction rules, and action format examples.
---
## 📝 Implementation Summary
### File Modified
**`app/api/swift/chat/route.ts`** - Function: `buildMedicalScribePrompt()`
### Changes Made
#### 1. Added Agenda Intents to P2 Section
Added 4 new agenda intents to the P2 (belangrijk, middenfrequent) section:
**agenda_query** - Afspraken opvragen
- Triggers: "afspraken vandaag", "agenda morgen", "wat is mijn volgende afspraak", "afspraken deze week"
- Entities: `dateRange` (vandaag/morgen/deze week/volgende week)
- Action: Toon lijst van afspraken in AgendaBlock
**create_appointment** - Nieuwe afspraak maken
- Triggers: "maak afspraak [patient]", "plan intake [patient]", "afspraak maken met [patient] [datum] [tijd]"
- Entities: `patientName`, `datetime`, `appointmentType`, `location`
- Required: `patientName` OR `patientId`, `datetime`
- Optional: `appointmentType` (default: behandeling), `location` (default: praktijk)
- Action: Open create form met pre-fill
**cancel_appointment** - Afspraak annuleren
- Triggers: "annuleer afspraak [patient]", "cancel [tijd]", "afspraak van [patient] annuleren"
- Entities: `identifier` (patient naam/tijd combinatie voor matching)
- Action: Toon confirmation dialog, bij meerdere matches: disambiguation
**reschedule_appointment** - Afspraak verzetten
- Triggers: "verzet afspraak [patient]", "verzet [oude tijd] naar [nieuwe tijd]", "[patient] naar [nieuwe datum]"
- Entities: `identifier`, `newDatetime`
- Required: `identifier`
- Action: Toon edit form met oude en nieuwe tijd
#### 2. Extended Clarification Questions
Added agenda-specific clarification examples:
- "Voor welke datum wil je de afspraak maken?" (datum ontbreekt bij create_appointment)
- "Op welk tijdstip?" (tijd ontbreekt bij create_appointment)
- "Welke afspraak wil je verzetten?" (identifier onduidelijk bij reschedule/cancel)
#### 3. Added Agenda Examples
**Voorbeeld 5: Agenda query**
```json
{
"type": "action",
"intent": "agenda_query",
"entities": {
"dateRange": {
"start": "2025-12-27",
"end": "2025-12-27",
"label": "vandaag"
}
},
"confidence": 0.98,
"artifact": {
"type": "agenda_query",
"prefill": {
"dateRange": {...}
}
}
}
```
**Voorbeeld 6: Afspraak maken (compleet)**
```json
{
"type": "action",
"intent": "create_appointment",
"entities": {
"patientName": "Jan",
"datetime": {
"date": "2025-12-28",
"time": "14:00"
},
"appointmentType": "behandeling",
"location": "praktijk"
},
"confidence": 0.95,
"artifact": {
"type": "create_appointment",
"prefill": {...}
}
}
```
**Voorbeeld 7: Afspraak maken (incompleet)**
User: "Plan intake Marie"
Response: "Voor welke datum en tijd wil je de intake voor Marie plannen?"
(Geen JSON action omdat datetime ontbreekt)
**Voorbeeld 8: Afspraak verzetten**
```json
{
"type": "action",
"intent": "reschedule_appointment",
"entities": {
"identifier": {
"type": "time",
"time": "14:00"
},
"newDatetime": {
"date": "2025-12-27",
"time": "15:00"
}
},
"confidence": 0.92,
"artifact": {
"type": "reschedule_appointment",
"prefill": {...}
}
}
```
---
## 🔑 Key Prompt Additions
### Entity Structure for Agenda Intents
**dateRange** (agenda_query):
```json
{
"start": "YYYY-MM-DD",
"end": "YYYY-MM-DD",
"label": "vandaag" | "morgen" | "deze week" | "volgende week"
}
```
**datetime** (create_appointment, reschedule_appointment):
```json
{
"date": "YYYY-MM-DD",
"time": "HH:mm"
}
```
**identifier** (cancel_appointment, reschedule_appointment):
```json
{
"type": "patient" | "time" | "both",
"patientName"?: "string",
"patientId"?: "uuid",
"time"?: "HH:mm",
"date"?: "YYYY-MM-DD",
"encounterId"?: "uuid"
}
```
**appointmentType** (create_appointment):
- intake
- behandeling (default)
- follow-up
- telefonisch
- huisbezoek
- online
- crisis
**location** (create_appointment):
- praktijk (default)
- online
- thuis
---
## ✅ Acceptance Criteria
| Criterion | Status | Notes |
|-----------|--------|-------|
| Agenda intents described in prompt | ✅ | 4 intents added to P2 section |
| Entity extraction rules documented | ✅ | All entities with types and defaults |
| Required vs optional entities specified | ✅ | Clear for each intent |
| Clarification question examples | ✅ | 3 new agenda-specific examples |
| Action format examples | ✅ | 4 complete examples with JSON |
| Confidence thresholds mentioned | ✅ | Inherited from base prompt (≥0.7) |
| TypeScript compilation | ✅ | 0 errors |
---
## 📊 Prompt Structure
The updated prompt now includes:
```
Je rol
├── Kernkwaliteiten
└── Tone of voice
Wat je DOET
├── 1. Intents herkennen
│ ├── P1 Intents (dagnotitie, zoeken, overdracht)
│ └── P2 Intents (rapportage, agenda_query, create_appointment,
│ cancel_appointment, reschedule_appointment) ✨ NEW
├── 2. Verduidelijkingsvragen stellen (+ agenda examples) ✨ UPDATED
├── 3. Action objects genereren
└── 4. Follow-up conversatie
Wat je NIET doet
Context die beschikbaar is
Voorbeelden
├── Voorbeeld 1-4 (bestaand)
├── Voorbeeld 5: Agenda query ✨ NEW
├── Voorbeeld 6: Afspraak maken (compleet) ✨ NEW
├── Voorbeeld 7: Afspraak maken (incompleet) ✨ NEW
└── Voorbeeld 8: Afspraak verzetten ✨ NEW
Error Handling
```
---
## 🧪 Testing Scenarios
### Scenario 1: Agenda Query - High Confidence
**Input**: "afspraken vandaag"
**Expected Claude Response**:
- Text: "Ik toon je de afspraken voor vandaag."
- JSON: `{ intent: "agenda_query", confidence: 0.98, entities: { dateRange: {...} } }`
### Scenario 2: Create Appointment - Complete
**Input**: "maak afspraak met Jan morgen 14:00"
**Expected Claude Response**:
- Text: "Ik maak een afspraak voor Jan morgen om 14:00."
- JSON: `{ intent: "create_appointment", confidence: 0.95, entities: { patientName, datetime } }`
### Scenario 3: Create Appointment - Incomplete
**Input**: "plan intake Marie"
**Expected Claude Response**:
- Text: "Voor welke datum en tijd wil je de intake voor Marie plannen?"
- JSON: None (confidence < 0.7 due to missing datetime)
### Scenario 4: Reschedule - Time Based
**Input**: "verzet 14:00 naar 15:00"
**Expected Claude Response**:
- Text: "Ik verzet de afspraak van 14:00 naar 15:00."
- JSON: `{ intent: "reschedule_appointment", confidence: 0.92, entities: { identifier, newDatetime } }`
### Scenario 5: Cancel - Patient Based
**Input**: "annuleer afspraak Jan"
**Expected Claude Response**:
- Text: Confirmation or disambiguation if multiple Jans
- JSON: `{ intent: "cancel_appointment", entities: { identifier: { type: "patient", patientName: "Jan" } } }`
---
## 🎯 Prompt Engineering Techniques Used
1. **Clear Intent Definitions**: Each intent has triggers, entities, and actions clearly defined
2. **Required/Optional Distinction**: Helps Claude decide when to ask clarification questions
3. **Confidence Guidance**: Thresholds guide when to open artifacts vs ask questions
4. **Concrete Examples**: 4 full examples with expected JSON structure
5. **Error Cases**: Example 7 shows incomplete input handling
6. **Entity Templates**: JSON structures show exact format expected
7. **Natural Language Triggers**: Multiple trigger phrases per intent
---
## 💡 Design Decisions
### Why P2 Instead of P1?
Agenda management is important but not as critical/frequent as dagnotitie (P1). Healthcare workers make notes constantly but schedule appointments less frequently.
### Why Include Defaults?
- `appointmentType`: "behandeling" (most common case)
- `location`: "praktijk" (most common location)
This reduces friction - users don't need to specify every detail.
### Why identifier Instead of encounterId?
The `identifier` structure allows flexible matching:
- By patient name: "annuleer afspraak Jan"
- By time: "cancel 14:00"
- By combination: "verzet Jan's afspraak"
The backend (Epic 3 API + Epic 4 UI) handles the disambiguation.
### Why Separate Examples for Complete/Incomplete?
Shows Claude two paths:
1. Complete data → Generate action (Example 6)
2. Incomplete data → Ask question (Example 7)
This demonstrates the confidence threshold logic clearly.
---
## 🚀 Next Steps
**E5.S3** - Error states
- Add error handling for API failures
- Add fallback links to `/epd/agenda`
- Handle offline/network errors gracefully
---
## 📁 Files Modified
```
✅ app/api/swift/chat/route.ts (Updated system prompt)
✅ docs/swift/bouwplan-swift-agenda-planning.md (E5.S2 → Done)
✅ docs/swift/implementation-e5-s2-chat-prompt.md (New documentation)
```
---
**Implementation Status**: ✅ Complete
**Ready for**: E5.S3 (Error states)
**Prompt Length**: ~525 lines (was ~325 lines) - increased by ~60%