By Sagar Shankaran, Founder of CallSphere
Create an AI agent for online learning platforms that handles student onboarding, monitors progress, detects when learners are stuck, and provides targeted help resources.
Key takeaways
Online course platforms face a brutal completion rate problem — typically 5-15% of enrolled students finish a course. The primary reasons are not content quality but lack of personalized support: students get stuck, lose motivation, or do not know where to find help. An AI agent can dramatically improve retention by providing proactive, personalized support at the moments that matter most.
from dataclasses import dataclass, field
from enum import Enum
from datetime import datetime, timedelta
from typing import Optional
class ModuleStatus(Enum):
NOT_STARTED = "not_started"
IN_PROGRESS = "in_progress"
COMPLETED = "completed"
SKIPPED = "skipped"
class LearnerRisk(Enum):
LOW = "low"
MEDIUM = "medium"
HIGH = "high"
CHURNED = "churned"
@dataclass
class CourseModule:
module_id: str
title: str
order: int
estimated_minutes: int
content_type: str # video, reading, exercise, quiz, project
prerequisites: list[str] = field(default_factory=list)
help_resources: list[dict] = field(default_factory=list)
@dataclass
class ModuleProgress:
module_id: str
status: ModuleStatus = ModuleStatus.NOT_STARTED
started_at: Optional[datetime] = None
completed_at: Optional[datetime] = None
time_spent_minutes: int = 0
attempts: int = 0
score: Optional[float] = None
last_activity: Optional[datetime] = None
@dataclass
class LearnerProfile:
learner_id: str
name: str
email: str
enrolled_courses: list[str] = field(default_factory=list)
experience_level: str = "beginner"
learning_goals: list[str] = field(default_factory=list)
preferred_pace: str = "self_paced"
timezone: str = "UTC"
@dataclass
class CourseEnrollment:
learner_id: str
course_id: str
enrolled_at: datetime
module_progress: dict[str, ModuleProgress] = field(
default_factory=dict
)
last_active: Optional[datetime] = None
completion_percentage: float = 0.0
@dataclass
class Course:
course_id: str
title: str
description: str
modules: list[CourseModule] = field(default_factory=list)
estimated_hours: float = 0.0
difficulty: str = "beginner"
category: str = ""
The most valuable feature of a learning platform agent is detecting when students are struggling before they drop out.
Hear it before you finish reading
Talk to a live CallSphere AI voice agent in your browser — 60 seconds, no signup.
flowchart LR
SIGNUP(["New signup"])
AGENT["AI onboarding agent"]
GOAL["Detect goal and<br/>persona"]
PATH{"Personalized path"}
ACT1["Activation step 1<br/>configure profile"]
ACT2["Activation step 2<br/>connect data"]
ACT3["Activation step 3<br/>first value moment"]
NUDGE["In-app and email<br/>nudges"]
CSM(["CSM handoff if<br/>account flagged"])
DONE(["Activated"])
SIGNUP --> AGENT --> GOAL --> PATH
PATH --> ACT1 --> ACT2 --> ACT3 --> DONE
AGENT --> NUDGE
AGENT --> CSM
style AGENT fill:#4f46e5,stroke:#4338ca,color:#fff
style DONE fill:#059669,stroke:#047857,color:#fff
style CSM fill:#f59e0b,stroke:#d97706,color:#1f2937
COURSES: dict[str, Course] = {}
ENROLLMENTS: dict[str, CourseEnrollment] = {}
LEARNERS: dict[str, LearnerProfile] = {}
def detect_stuck_learners(course_id: str) -> list[dict]:
stuck_learners = []
now = datetime.now()
for key, enrollment in ENROLLMENTS.items():
if enrollment.course_id != course_id:
continue
learner = LEARNERS.get(enrollment.learner_id)
if not learner:
continue
# Check for inactivity
days_inactive = 0
if enrollment.last_active:
days_inactive = (now - enrollment.last_active).days
# Check for repeated failures
struggling_modules = []
for mod_id, progress in enrollment.module_progress.items():
if progress.attempts >= 3 and progress.status != ModuleStatus.COMPLETED:
struggling_modules.append(mod_id)
if (progress.status == ModuleStatus.IN_PROGRESS
and progress.time_spent_minutes > 120
and progress.score is not None
and progress.score < 60):
struggling_modules.append(mod_id)
# Calculate risk level
risk = LearnerRisk.LOW
if days_inactive > 14 or len(struggling_modules) >= 2:
risk = LearnerRisk.HIGH
elif days_inactive > 7 or len(struggling_modules) >= 1:
risk = LearnerRisk.MEDIUM
if days_inactive > 30:
risk = LearnerRisk.CHURNED
if risk in (LearnerRisk.MEDIUM, LearnerRisk.HIGH, LearnerRisk.CHURNED):
stuck_learners.append({
"learner_id": enrollment.learner_id,
"learner_name": learner.name,
"risk_level": risk.value,
"days_inactive": days_inactive,
"completion": enrollment.completion_percentage,
"struggling_modules": struggling_modules,
"intervention": _suggest_intervention(
risk, days_inactive, struggling_modules
),
})
return stuck_learners
def _suggest_intervention(
risk: LearnerRisk,
days_inactive: int,
struggling_modules: list[str],
) -> str:
if risk == LearnerRisk.CHURNED:
return "Send re-engagement email with course highlights."
if risk == LearnerRisk.HIGH:
if struggling_modules:
return "Offer 1-on-1 tutoring or alternative resources."
return "Send personalized check-in and progress summary."
if risk == LearnerRisk.MEDIUM:
return "Send encouragement with next milestone preview."
return "No intervention needed."
from agents import Agent, function_tool, Runner
import json
@function_tool
def get_learner_progress(
learner_id: str, course_id: str
) -> str:
"""Get detailed progress for a learner in a course."""
enrollment_key = f"{learner_id}_{course_id}"
enrollment = ENROLLMENTS.get(enrollment_key)
if not enrollment:
return "Enrollment not found."
course = COURSES.get(course_id)
if not course:
return "Course not found."
module_details = []
for module in course.modules:
progress = enrollment.module_progress.get(module.module_id)
module_details.append({
"module": module.title,
"status": (
progress.status.value if progress
else "not_started"
),
"time_spent": (
progress.time_spent_minutes if progress else 0
),
"score": progress.score if progress else None,
"content_type": module.content_type,
})
return json.dumps({
"learner_id": learner_id,
"course": course.title,
"completion": enrollment.completion_percentage,
"modules": module_details,
"last_active": (
enrollment.last_active.isoformat()
if enrollment.last_active else None
),
})
@function_tool
def get_help_for_module(
course_id: str, module_id: str
) -> str:
"""Get help resources for a specific module."""
course = COURSES.get(course_id)
if not course:
return "Course not found."
for module in course.modules:
if module.module_id == module_id:
return json.dumps({
"module": module.title,
"estimated_time": module.estimated_minutes,
"prerequisites": module.prerequisites,
"help_resources": module.help_resources,
"content_type": module.content_type,
})
return "Module not found."
@function_tool
def get_at_risk_learners(course_id: str) -> str:
"""Identify learners who are stuck or at risk of dropping out."""
stuck = detect_stuck_learners(course_id)
return json.dumps(stuck) if stuck else "No at-risk learners."
platform_agent = Agent(
name="Learning Platform Assistant",
instructions="""You are an online learning platform assistant.
Help students track their progress, find help when stuck, and
stay motivated. When a student seems frustrated, be empathetic
and offer specific help resources for their current module.
For course staff, identify at-risk learners and suggest
interventions. Celebrate milestones and progress, not just
completion.""",
tools=[
get_learner_progress,
get_help_for_module,
get_at_risk_learners,
],
)
The system considers multiple signals: inactivity duration, number of attempts, time spent versus module estimate, and score trends. A student who is simply on vacation (inactive but was performing well) gets a lower risk score than one who failed multiple attempts and then went inactive. Configurable thresholds per course type reduce noise.
Yes. By analyzing which module types (video, reading, exercise) the student completes fastest and scores highest on, the agent can recommend alternative content formats. If a student struggles with video lectures but excels at reading materials, it can suggest the text-based alternatives for upcoming modules.
Still reading? Stop comparing — try CallSphere live.
CallSphere ships complete AI voice agents per industry — 14 tools for healthcare, 10 agents for real estate, 4 specialists for salons. See how it actually handles a call before you book a demo.
Canvas and Moodle expose REST APIs for enrollment, grades, and module completion data. The agent tools become API wrappers that translate LMS data into the internal model. This approach means the agent works as an overlay on the existing platform without requiring students to use a different interface.
#AIAgents #EdTech #OnlineLearning #Python #LMS #AgenticAI #LearnAI #AIEngineering

Written by
Sagar Shankaran· Founder, CallSphere
LinkedInSagar Shankaran is the founder of CallSphere, where he builds production AI voice and chat agents deployed across healthcare, hospitality, real estate, and home services. He writes about agentic AI, LLM engineering, and shipping voice agents that handle real calls in production.
See how AI voice agents work for your industry. Live demo available -- no signup required.
A how-to guide for Bangladeshi coaching centres, edtech startups, and schools on using a CallSphere AI voice + chat agent to answer every admission enquiry in Bengali and English during peak season.
A founder's guide to the personal AI assistant market: best AI assistant apps, business-grade options, and how CallSphere's voice agent fits in.
A founder's guide to free AI agents, low-code AI agent builders, and how to know when you should pay for a real platform like CallSphere.
Graphiti is the open-source temporal knowledge graph for AI agents in 2026. Learn how bi-temporal memory beats vector RAG for voice agents and long-running LLMs.
Chatbot app vs ChatGPT in 2026: a founder's clear take on the difference, when to use which, and how a real AI chatbot app development works.
How we built a fault-tolerant HVAC emergency triage and tech-dispatch platform on Kubernetes — three-tier CQRS, 11 micro-agents on the OpenAI Agents SDK + LangGraph, NATS JetStream, DTMF/SMS/WebSocket acceptance, circuit breakers, and an evaluation pipeline that catches regressions before they wake a tech at 3 AM.
© 2026 CallSphere Inc. All rights reserved.
Made within San Francisco