From c55467a068265dfb5205c711ad6b21d1618185ff Mon Sep 17 00:00:00 2001 From: Deep Koluguri Date: Tue, 26 May 2026 18:40:43 -0400 Subject: [PATCH] Refine Claude WhatsApp agent: add empathy, dynamic dates, and cancellation intent --- backend/clinicAgent.js | 71 ++++++++++++++++++++++++++++-------------- 1 file changed, 47 insertions(+), 24 deletions(-) diff --git a/backend/clinicAgent.js b/backend/clinicAgent.js index 5655c72..d4321fe 100644 --- a/backend/clinicAgent.js +++ b/backend/clinicAgent.js @@ -15,21 +15,37 @@ const anthropic = new Anthropic({ const LLM_MODEL = process.env.LLM_MODEL || 'claude-3-haiku-20240307'; class ClinicAgent { - constructor() { - this.systemPrompt = ` - You are 'Curio AI', the assistant for Curio Health. - Your goal is to help patients book appointments. - Be extremely concise, friendly, and natural. Do not use markdown. - To book an appointment, you MUST collect: - 1. Department (e.g. Cardiology, Neurology, General) - 2. Date (Format as YYYY-MM-DD or today/tomorrow) - 3. Time (Format as HH:MM AM/PM) - - If any of these 3 pieces of information are missing, naturally ask the user for them. - Do NOT output JSON until you have ALL THREE pieces of information. + getSystemPrompt() { + const today = new Date().toISOString().split('T')[0]; + return ` + You are 'Curio AI', the highly empathetic and intelligent medical assistant for Curio Health. + Today's date is ${today}. - If you have all three pieces of information, you MUST reply with ONLY a JSON block like this (no other text): - {"action": "BOOK", "department": "Cardiology", "date": "2026-05-26", "time": "10:00"} + Your goal is to help patients book, cancel, or reschedule appointments, and answer basic clinic questions. + Be extremely concise, warm, empathetic, and natural. Do not use markdown formatting. + + AVAILABLE CLINICS/DEPARTMENTS: + - Cardiology (Dr. Sharma) + - General Medicine (Apollo) + - Pediatrics + - Orthopedics + + INTENT 1: BOOKING + To book an appointment, you MUST collect: + 1. Department/Clinic + 2. Date (Format as YYYY-MM-DD) + 3. Time (Format as HH:MM) + + If any booking info is missing, naturally ask the user. Do NOT output JSON until you have ALL THREE pieces. + If you have all three, reply with ONLY JSON: + {"action": "BOOK", "department": "Cardiology", "date": "${today}", "time": "10:00"} + + INTENT 2: CANCEL + If they want to cancel, ask them to confirm the date and time of the appointment they want to cancel. Once confirmed, reply with ONLY JSON: + {"action": "CANCEL", "date": "${today}", "time": "10:00"} + + INTENT 3: GENERAL QUERY + If they ask general questions (e.g., location, hours), just answer naturally as the assistant. If they express pain or worry, respond with empathy before helping them. `; } @@ -41,7 +57,7 @@ class ClinicAgent { model: LLM_MODEL, max_tokens: 300, temperature: 0.2, - system: this.systemPrompt, + system: this.getSystemPrompt(), messages: transcript }); @@ -49,29 +65,36 @@ class ClinicAgent { // Try to parse JSON intent try { - // Remove potential markdown code blocks if the LLM wrapped it const cleanJson = replyContent.replace(/```json/g, '').replace(/```/g, '').trim(); const intent = JSON.parse(cleanJson); if (intent.action === 'BOOK') { - // Extract HH:MM - let timeVal = "10:00"; // fallback + let timeVal = "10:00"; const timeMatch = intent.time.match(/(\d{1,2}:\d{2})/); if (timeMatch) timeVal = timeMatch[1]; - // Parse date let dateVal = new Date().toISOString().split('T')[0]; if (intent.date && intent.date.match(/^\d{4}-\d{2}-\d{2}$/)) { dateVal = intent.date; } - // Book the token using Dr. Sharma's Cardiology ("sharma-clinic") - // In a real system, we'd map intent.department to tenantId - const result = await queueManager.bookOnline(dateVal, timeVal, phone, 'sharma-clinic'); + let tenantId = 'sharma-clinic'; // Default + if (intent.department && intent.department.toLowerCase().includes('general')) tenantId = 'apollo-hospitals'; + + const result = await queueManager.bookOnline(dateVal, timeVal, phone, tenantId); if (result.success) { - return { reply: `Awesome! Your appointment for ${intent.department} is booked. Your token is ${result.token}. You should receive a confirmation message shortly.` }; + return { reply: `Awesome! Your appointment for ${intent.department} is booked for ${dateVal} at ${timeVal}. Your token is ${result.token}. We look forward to seeing you!` }; } else { - return { reply: `Sorry, ${result.message}` }; + return { reply: `I'm so sorry, but ${result.message.toLowerCase()} Would you like to try a different time or day?` }; + } + } else if (intent.action === 'CANCEL') { + let dateVal = intent.date || new Date().toISOString().split('T')[0]; + let timeVal = intent.time || "10:00"; + const result = await queueManager.cancelBooking(dateVal, timeVal, 'sharma-clinic'); + if (result.success) { + return { reply: `Done. I've cancelled your appointment for ${dateVal} at ${timeVal}. Let me know if you need to book a new one!` }; + } else { + return { reply: `Hmm, I couldn't find a booking for ${dateVal} at ${timeVal} to cancel. Could you double-check the time for me?` }; } } } catch (e) {