Refine Claude WhatsApp agent: add empathy, dynamic dates, and cancellation intent
Build Curio HMS / build-and-deploy (push) Successful in 1m8s Details

This commit is contained in:
Deep Koluguri 2026-05-26 18:40:43 -04:00
parent 2b7cea1df3
commit c55467a068
1 changed files with 47 additions and 24 deletions

View File

@ -15,21 +15,37 @@ const anthropic = new Anthropic({
const LLM_MODEL = process.env.LLM_MODEL || 'claude-3-haiku-20240307'; const LLM_MODEL = process.env.LLM_MODEL || 'claude-3-haiku-20240307';
class ClinicAgent { class ClinicAgent {
constructor() { getSystemPrompt() {
this.systemPrompt = ` const today = new Date().toISOString().split('T')[0];
You are 'Curio AI', the assistant for Curio Health. return `
Your goal is to help patients book appointments. You are 'Curio AI', the highly empathetic and intelligent medical assistant for Curio Health.
Be extremely concise, friendly, and natural. Do not use markdown. Today's date is ${today}.
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: To book an appointment, you MUST collect:
1. Department (e.g. Cardiology, Neurology, General) 1. Department/Clinic
2. Date (Format as YYYY-MM-DD or today/tomorrow) 2. Date (Format as YYYY-MM-DD)
3. Time (Format as HH:MM AM/PM) 3. Time (Format as HH:MM)
If any of these 3 pieces of information are missing, naturally ask the user for them. If any booking info is missing, naturally ask the user. Do NOT output JSON until you have ALL THREE pieces.
Do NOT output JSON until you have ALL THREE pieces of information. If you have all three, reply with ONLY JSON:
{"action": "BOOK", "department": "Cardiology", "date": "${today}", "time": "10:00"}
If you have all three pieces of information, you MUST reply with ONLY a JSON block like this (no other text): INTENT 2: CANCEL
{"action": "BOOK", "department": "Cardiology", "date": "2026-05-26", "time": "10:00"} 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, model: LLM_MODEL,
max_tokens: 300, max_tokens: 300,
temperature: 0.2, temperature: 0.2,
system: this.systemPrompt, system: this.getSystemPrompt(),
messages: transcript messages: transcript
}); });
@ -49,29 +65,36 @@ class ClinicAgent {
// Try to parse JSON intent // Try to parse JSON intent
try { try {
// Remove potential markdown code blocks if the LLM wrapped it
const cleanJson = replyContent.replace(/```json/g, '').replace(/```/g, '').trim(); const cleanJson = replyContent.replace(/```json/g, '').replace(/```/g, '').trim();
const intent = JSON.parse(cleanJson); const intent = JSON.parse(cleanJson);
if (intent.action === 'BOOK') { if (intent.action === 'BOOK') {
// Extract HH:MM let timeVal = "10:00";
let timeVal = "10:00"; // fallback
const timeMatch = intent.time.match(/(\d{1,2}:\d{2})/); const timeMatch = intent.time.match(/(\d{1,2}:\d{2})/);
if (timeMatch) timeVal = timeMatch[1]; if (timeMatch) timeVal = timeMatch[1];
// Parse date
let dateVal = new Date().toISOString().split('T')[0]; let dateVal = new Date().toISOString().split('T')[0];
if (intent.date && intent.date.match(/^\d{4}-\d{2}-\d{2}$/)) { if (intent.date && intent.date.match(/^\d{4}-\d{2}-\d{2}$/)) {
dateVal = intent.date; dateVal = intent.date;
} }
// Book the token using Dr. Sharma's Cardiology ("sharma-clinic") let tenantId = 'sharma-clinic'; // Default
// In a real system, we'd map intent.department to tenantId if (intent.department && intent.department.toLowerCase().includes('general')) tenantId = 'apollo-hospitals';
const result = await queueManager.bookOnline(dateVal, timeVal, phone, 'sharma-clinic');
const result = await queueManager.bookOnline(dateVal, timeVal, phone, tenantId);
if (result.success) { 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 { } 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) { } catch (e) {