---
title: "Building a Study Planner Agent: Personalized Learning Schedules with Spaced Repetition"
description: "Create an AI study planner agent that builds personalized schedules using spaced repetition algorithms, optimizes review timing, and adapts to individual learning pace and availability."
canonical: https://callsphere.ai/blog/building-study-planner-agent-spaced-repetition-schedules
category: "Learn Agentic AI"
tags: ["Spaced Repetition", "Study Planning", "Education AI", "Python", "Learning Science"]
author: "CallSphere Team"
published: 2026-03-17T00:00:00.000Z
updated: 2026-06-05T08:47:33.060Z
---

# Building a Study Planner Agent: Personalized Learning Schedules with Spaced Repetition

> Create an AI study planner agent that builds personalized schedules using spaced repetition algorithms, optimizes review timing, and adapts to individual learning pace and availability.

## The Science Behind Spaced Repetition

Research in cognitive science consistently shows that distributing practice over time produces dramatically better retention than massing it into a single session. The spacing effect, first documented by Hermann Ebbinghaus in 1885, demonstrates that memories decay exponentially but each successful review extends the retention interval. A study planner agent leverages this principle to schedule reviews at optimal intervals — just before the student would forget.

The challenge is that optimal intervals vary per student and per topic. An AI agent can personalize these intervals by tracking individual performance and adjusting the schedule dynamically.

## The Forgetting Curve Model

Start with a mathematical model of memory decay that the agent uses to predict when a student will forget a given topic:

```mermaid
flowchart LR
    CALLER(["Client"])
    subgraph TEL["Telephony"]
        SIP["Twilio SIP and PSTN"]
    end
    subgraph BRAIN["Salon AI Agent"]
        STT["Streaming STT
Deepgram or Whisper"]
        NLU{"Intent and
Entity Extraction"}
        TOOLS["Tool Calls"]
        TTS["Streaming TTS
ElevenLabs or Rime"]
    end
    subgraph DATA["Live Data Plane"]
        CRM[("CRM and Notes")]
        CAL[("Calendar and
Schedule")]
        KB[("Knowledge Base
and Policies")]
    end
    subgraph OUT["Outcomes"]
        O1(["Appointment booked"])
        O2(["Reschedule completed"])
        O3(["Stylist handoff"])
    end
    CALLER --> SIP --> STT --> NLU
    NLU -->|Lookup| TOOLS
    TOOLS  CRM
    TOOLS  CAL
    TOOLS  KB
    NLU --> TTS --> SIP --> CALLER
    NLU -->|Resolved| O1
    NLU -->|Schedule| O2
    NLU -->|Escalate| O3
    style CALLER fill:#f1f5f9,stroke:#64748b,color:#0f172a
    style NLU fill:#4f46e5,stroke:#4338ca,color:#fff
    style O1 fill:#059669,stroke:#047857,color:#fff
    style O2 fill:#0ea5e9,stroke:#0369a1,color:#fff
    style O3 fill:#f59e0b,stroke:#d97706,color:#1f2937
```

```python
import math
from dataclasses import dataclass, field
from datetime import datetime, timedelta
from typing import Optional

@dataclass
class StudyItem:
    item_id: str
    topic: str
    subject: str
    difficulty: float = 0.3  # 0.0 = easy, 1.0 = hard
    stability: float = 1.0   # days until 90% recall probability
    last_reviewed: Optional[datetime] = None
    review_count: int = 0
    easiness_factor: float = 2.5  # SM-2 style factor
    consecutive_correct: int = 0

    def recall_probability(self, now: Optional[datetime] = None) -> float:
        """Estimate probability the student remembers this item."""
        if self.last_reviewed is None:
            return 0.0
        now = now or datetime.now()
        days_elapsed = (now - self.last_reviewed).total_seconds() / 86400
        # Exponential decay model
        return math.exp(-days_elapsed / self.stability)

    def next_review_date(self, target_recall: float = 0.85) -> datetime:
        """Calculate when recall probability drops to the target."""
        if self.last_reviewed is None:
            return datetime.now()
        # Solve: target = exp(-days / stability) for days
        days_until_target = -self.stability * math.log(target_recall)
        return self.last_reviewed + timedelta(days=days_until_target)

    def update_after_review(self, quality: int):
        """Update stability and easiness after a review.

        quality: 0-5 scale (SM-2 convention)
        0-2 = incorrect/difficult, 3 = correct with hesitation,
        4 = correct, 5 = perfect
        """
        self.review_count += 1
        self.last_reviewed = datetime.now()

        if quality >= 3:
            self.consecutive_correct += 1
            # Increase stability (longer intervals)
            if self.consecutive_correct == 1:
                self.stability = 1.0
            elif self.consecutive_correct == 2:
                self.stability = 6.0
            else:
                self.stability *= self.easiness_factor
        else:
            self.consecutive_correct = 0
            self.stability = 1.0  # Reset to short interval

        # Update easiness factor (SM-2 formula)
        self.easiness_factor = max(
            1.3,
            self.easiness_factor
            + 0.1 - (5 - quality) * (0.08 + (5 - quality) * 0.02),
        )
```

## Schedule Optimization

Given a student's available time slots and a set of items due for review, the agent needs to build an optimal daily schedule:

```python
@dataclass
class TimeSlot:
    start: datetime
    end: datetime
    energy_level: str = "medium"  # low, medium, high

    @property
    def duration_minutes(self) -> int:
        return int((self.end - self.start).total_seconds() / 60)

@dataclass
class ScheduleEntry:
    item: StudyItem
    scheduled_time: datetime
    duration_minutes: int
    priority: float

@dataclass
class StudySchedule:
    date: datetime
    entries: list[ScheduleEntry] = field(default_factory=list)
    total_minutes: int = 0

def build_daily_schedule(
    items: list[StudyItem],
    available_slots: list[TimeSlot],
    max_new_items: int = 5,
    minutes_per_review: int = 5,
    minutes_per_new: int = 15,
) -> StudySchedule:
    """Build an optimized study schedule for today."""
    now = datetime.now()
    schedule = StudySchedule(date=now)

    # Separate overdue reviews from new items
    overdue = [
        item for item in items
        if item.last_reviewed and item.recall_probability(now) = len(available_slots):
            break
        schedule.entries.append(ScheduleEntry(
            item=item,
            scheduled_time=available_slots[slot_idx].start,
            duration_minutes=minutes_per_review,
            priority=1.0 - item.recall_probability(now),
        ))
        remaining_minutes -= minutes_per_review
        schedule.total_minutes += minutes_per_review
        if remaining_minutes = len(available_slots):
            break
        schedule.entries.append(ScheduleEntry(
            item=item,
            scheduled_time=available_slots[slot_idx].start,
            duration_minutes=minutes_per_new,
            priority=0.5,
        ))
        remaining_minutes -= minutes_per_new
        schedule.total_minutes += minutes_per_new
        if remaining_minutes  str:
    """Generate today's optimized study schedule."""
    # In production, load from database
    items = load_student_items(student_id)
    slots = parse_availability(available_hours, energy_pattern)
    schedule = build_daily_schedule(items, slots)

    return json.dumps({
        "total_minutes": schedule.total_minutes,
        "review_count": len([
            e for e in schedule.entries if e.item.review_count > 0
        ]),
        "new_count": len([
            e for e in schedule.entries if e.item.review_count == 0
        ]),
        "entries": [
            {
                "topic": e.item.topic,
                "type": "review" if e.item.review_count > 0 else "new",
                "duration": e.duration_minutes,
                "recall_probability": f"{e.item.recall_probability():.0%}",
            }
            for e in schedule.entries
        ],
    })

study_planner = Agent(
    name="Study Planner",
    instructions="""You are a study planning assistant that creates
personalized schedules based on spaced repetition science.

When presenting a schedule:
1. Start with a brief overview of what is due today and why
2. List each study block with topic, duration, and purpose
3. Explain WHY certain topics are prioritized (low recall probability)
4. Suggest the best order based on energy levels
5. End with encouragement and what to expect tomorrow

Be specific about timing. Instead of 'review math', say 'review
quadratic equations — your recall has dropped to 40%, so this
needs attention today to prevent forgetting.'""",
    tools=[get_todays_schedule],
)
```

## FAQ

### How does the spaced repetition algorithm handle topics a student finds consistently easy?

The easiness factor in the SM-2 algorithm increases for items the student consistently rates highly, which extends the stability value exponentially. An item rated "perfect" five times in a row might not appear for review for several months. The system naturally concentrates review time on difficult material while letting easy items fade into long-interval maintenance reviews.

### What happens if a student misses several scheduled review sessions?

When a student returns after a gap, the recall probability for overdue items will have dropped significantly. The scheduler prioritizes these by urgency — items with the lowest recall probability appear first. However, it also respects the student's available time, so it will not schedule four hours of reviews just because everything is overdue. Instead, it triages the most critical items and spreads the backlog across several days.

### Can the agent account for exam dates and deadlines?

Yes. You extend the scheduling logic with a deadline parameter that overrides the normal spaced repetition intervals. When an exam is approaching, the scheduler increases review frequency for exam-relevant topics and front-loads new material that needs to be learned before the deadline, while still maintaining minimum review intervals for retention.

---

#SpacedRepetition #StudyPlanning #EducationAI #Python #LearningScience #AgenticAI #LearnAI #AIEngineering

---

Source: https://callsphere.ai/blog/building-study-planner-agent-spaced-repetition-schedules
