curaflow/backend/clinicAgent.js

118 lines
5.5 KiB
JavaScript

/**
* ClinicAgent: An agentic orchestrator for Curio.
* It uses natural language understanding to map user requests to hospital tools.
*/
const patientManager = require('./patientManager');
const queueManager = require('./queueManager');
const pharmacyAgent = require('./pharmacyAgent');
const configManager = require('./configManager');
const Anthropic = require('@anthropic-ai/sdk');
const anthropic = new Anthropic({
apiKey: process.env.ANTHROPIC_API_KEY, // Defaults to process.env.ANTHROPIC_API_KEY
});
const LLM_MODEL = process.env.LLM_MODEL || 'claude-3-haiku-20240307';
class ClinicAgent {
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}.
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)
CRITICAL INSTRUCTIONS FOR ASKING:
- If the user hasn't specified a department, you MUST list the 4 available departments so they know what to choose. Do not make them guess!
- If they haven't specified a time, suggest common intervals (e.g. 10:00 AM, 11:00 AM, 6:00 PM).
If any booking info is missing, naturally ask the user using the instructions above. 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.
`;
}
async processMessage(phone, transcript) {
console.log(`[Agent] Processing for ${phone} via Claude`);
try {
const response = await anthropic.messages.create({
model: LLM_MODEL,
max_tokens: 300,
temperature: 0.2,
system: this.getSystemPrompt(),
messages: transcript
});
const replyContent = response.content[0].text.trim();
// Try to parse JSON intent
try {
const cleanJson = replyContent.replace(/```json/g, '').replace(/```/g, '').trim();
const intent = JSON.parse(cleanJson);
if (intent.action === 'BOOK') {
let timeVal = "10:00";
const timeMatch = intent.time.match(/(\d{1,2}:\d{2})/);
if (timeMatch) timeVal = timeMatch[1];
let dateVal = new Date().toISOString().split('T')[0];
if (intent.date && intent.date.match(/^\d{4}-\d{2}-\d{2}$/)) {
dateVal = intent.date;
}
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 for ${dateVal} at ${timeVal}. Your token is ${result.token}. We look forward to seeing you!` };
} else {
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) {
// Not JSON, just regular conversational text
return { reply: replyContent };
}
return { reply: replyContent };
} catch (error) {
console.error('[ClinicAgent] LLM Error:', error.message);
return { reply: "I'm having trouble connecting to my AI brain right now. Please try again in a moment." };
}
}
}
module.exports = new ClinicAgent();