Files
triqura-ecd/docs/swift/implementation-e5-s2-chat-prompt.md
colinislit 14a9d34337 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>
2025-12-27 22:44:18 +01:00

8.9 KiB

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

{
  "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)

{
  "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

{
  "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):

{
  "start": "YYYY-MM-DD",
  "end": "YYYY-MM-DD",
  "label": "vandaag" | "morgen" | "deze week" | "volgende week"
}

datetime (create_appointment, reschedule_appointment):

{
  "date": "YYYY-MM-DD",
  "time": "HH:mm"
}

identifier (cancel_appointment, reschedule_appointment):

{
  "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%