Skip to content
Learn Agentic AI15 min read0 views

AI Agent for Online Course Platforms: Student Onboarding, Progress Tracking, and Support

Create an AI agent for online learning platforms that handles student onboarding, monitors progress, detects when learners are stuck, and provides targeted help resources.

The Online Learning Retention Problem

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.

Learning Platform Data Model

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 = ""

Stuck Detection and Risk Scoring

The most valuable feature of a learning platform agent is detecting when students are struggling before they drop out.

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."

Agent Tools

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,
    ],
)

FAQ

How does the stuck detection avoid false positives?

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.

See AI Voice Agents Handle Real Calls

Book a free demo or calculate how much you can save with AI voice automation.

Can the agent personalize content recommendations?

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.

How does this integrate with existing LMS platforms like Canvas or Moodle?

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

Share this article
C

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.