Building a Medication Reminder Agent: Adherence Support with AI
Build an AI agent for medication adherence that handles reminder scheduling, drug interaction checking, refill tracking, and caregiver notifications — with practical Python implementation.
The Medication Adherence Crisis
About half of all patients do not take their medications as prescribed. This leads to 125,000 preventable deaths annually in the United States alone and costs the healthcare system over 500 billion dollars per year. An AI medication reminder agent goes beyond simple alarm clocks — it understands medication schedules, checks for dangerous interactions, tracks refills, and alerts caregivers when adherence drops.
Modeling the Medication Schedule
The foundation of a reminder agent is an accurate medication model that captures dosing complexity:
from dataclasses import dataclass, field
from datetime import datetime, time, timedelta
from enum import Enum
from typing import Optional
class DoseFrequency(Enum):
ONCE_DAILY = "once_daily"
TWICE_DAILY = "twice_daily"
THREE_TIMES_DAILY = "three_times_daily"
EVERY_X_HOURS = "every_x_hours"
AS_NEEDED = "as_needed"
WEEKLY = "weekly"
class MealRelation(Enum):
WITH_FOOD = "with_food"
EMPTY_STOMACH = "empty_stomach"
NO_RESTRICTION = "no_restriction"
@dataclass
class Medication:
id: str
name: str
dosage: str
frequency: DoseFrequency
meal_relation: MealRelation
prescribed_times: list[time] = field(default_factory=list)
hour_interval: Optional[int] = None
start_date: datetime = field(default_factory=datetime.utcnow)
end_date: Optional[datetime] = None
refill_date: Optional[datetime] = None
pills_remaining: Optional[int] = None
special_instructions: str = ""
@dataclass
class ReminderEvent:
medication: Medication
scheduled_time: datetime
acknowledged: bool = False
taken: bool = False
skipped_reason: Optional[str] = None
Intelligent Reminder Scheduling
The scheduler accounts for time zones, meal timing, and spacing between medications that should not be taken together:
class ReminderScheduler:
# Minimum gap between medications that interact
MIN_INTERACTION_GAP_HOURS = 2
def __init__(self, medications: list[Medication], wake_time: time, sleep_time: time):
self.medications = medications
self.wake_time = wake_time
self.sleep_time = sleep_time
def generate_daily_schedule(self, date: datetime) -> list[ReminderEvent]:
events: list[ReminderEvent] = []
for med in self.medications:
if med.end_date and date > med.end_date:
continue
times = self._resolve_times(med)
for t in times:
scheduled = datetime.combine(date.date(), t)
events.append(ReminderEvent(medication=med, scheduled_time=scheduled))
events.sort(key=lambda e: e.scheduled_time)
return self._adjust_for_interactions(events)
def _resolve_times(self, med: Medication) -> list[time]:
if med.prescribed_times:
return med.prescribed_times
if med.frequency == DoseFrequency.ONCE_DAILY:
return [time(9, 0)]
if med.frequency == DoseFrequency.TWICE_DAILY:
return [time(9, 0), time(21, 0)]
if med.frequency == DoseFrequency.THREE_TIMES_DAILY:
return [time(8, 0), time(14, 0), time(20, 0)]
return [self.wake_time]
def _adjust_for_interactions(self, events: list[ReminderEvent]) -> list[ReminderEvent]:
# Ensure minimum spacing between medications that interact
for i in range(1, len(events)):
prev = events[i - 1]
curr = events[i]
gap = (curr.scheduled_time - prev.scheduled_time).total_seconds() / 3600
if gap < self.MIN_INTERACTION_GAP_HOURS:
adjusted = prev.scheduled_time + timedelta(hours=self.MIN_INTERACTION_GAP_HOURS)
curr.scheduled_time = adjusted
return events
Drug Interaction Checking
Before confirming any schedule, the agent checks for known drug interactions:
See AI Voice Agents Handle Real Calls
Book a free demo or calculate how much you can save with AI voice automation.
@dataclass
class Interaction:
drug_a: str
drug_b: str
severity: str # "mild", "moderate", "severe", "contraindicated"
description: str
recommendation: str
class InteractionChecker:
def __init__(self, interaction_db: list[Interaction]):
self._interactions = interaction_db
def check_all(self, medications: list[Medication]) -> list[Interaction]:
found = []
names = [m.name.lower() for m in medications]
for i, name_a in enumerate(names):
for name_b in names[i + 1:]:
for interaction in self._interactions:
pair = {interaction.drug_a.lower(), interaction.drug_b.lower()}
if name_a in pair and name_b in pair:
found.append(interaction)
return found
def get_severe_interactions(self, medications: list[Medication]) -> list[Interaction]:
return [
i for i in self.check_all(medications)
if i.severity in ("severe", "contraindicated")
]
Refill Tracking and Caregiver Alerts
The agent monitors pill counts and proactively notifies patients and caregivers when refills are needed or adherence drops:
from typing import Callable
@dataclass
class AdherenceStats:
total_scheduled: int
total_taken: int
adherence_rate: float
streak_days: int
missed_medications: list[str]
class AdherenceMonitor:
REFILL_WARNING_DAYS = 7
LOW_ADHERENCE_THRESHOLD = 0.8
def __init__(self, notify_patient: Callable, notify_caregiver: Callable):
self.notify_patient = notify_patient
self.notify_caregiver = notify_caregiver
def check_refills(self, medications: list[Medication]) -> list[str]:
warnings = []
for med in medications:
if med.pills_remaining is not None and med.pills_remaining <= self.REFILL_WARNING_DAYS:
msg = f"{med.name}: {med.pills_remaining} doses remaining. Time to request a refill."
warnings.append(msg)
self.notify_patient(msg)
return warnings
def evaluate_adherence(self, events: list[ReminderEvent]) -> AdherenceStats:
total = len(events)
taken = sum(1 for e in events if e.taken)
rate = taken / total if total > 0 else 0.0
missed = list({e.medication.name for e in events if not e.taken and not e.skipped_reason})
stats = AdherenceStats(
total_scheduled=total,
total_taken=taken,
adherence_rate=rate,
streak_days=self._calculate_streak(events),
missed_medications=missed,
)
if rate < self.LOW_ADHERENCE_THRESHOLD:
self.notify_caregiver(
f"Adherence alert: {rate:.0%} over recent period. Missed: {', '.join(missed)}"
)
return stats
def _calculate_streak(self, events: list[ReminderEvent]) -> int:
streak = 0
for event in reversed(events):
if event.taken:
streak += 1
else:
break
return streak
FAQ
How does the agent handle as-needed medications like pain relievers?
As-needed medications are not scheduled with fixed reminders. Instead, the agent tracks when the patient reports taking a dose and enforces minimum intervals between doses. If the patient tries to log a dose too soon, the agent warns them about the minimum time between doses and the maximum daily limit.
What if a patient wants to stop a medication?
The agent never advises stopping medication. It acknowledges the patient's concern, logs the request, and recommends they discuss it with their prescribing provider. If the patient confirms they are stopping, the agent removes the reminders but flags the change in the patient's record for clinical review.
How are caregiver notifications authorized?
The patient must explicitly grant permission for caregiver notifications through a consent process. The agent stores a signed consent record specifying which caregiver, what information they receive (adherence rates but not specific medication names, for example), and under what conditions notifications are triggered.
#HealthcareAI #MedicationAdherence #Reminders #DrugInteractions #Python #AgenticAI #LearnAI #AIEngineering
CallSphere Team
Expert insights on AI voice agents and customer communication automation.
Try CallSphere AI Voice Agents
See how AI voice agents work for your industry. Live demo available -- no signup required.