Skip to main content
Build an AI tutor that remembers student progress, identifies knowledge gaps, and personalizes the learning experience. EverMemOS enables long-term memory that persists across study sessions.

Architecture Overview

AI Tutor Architecture Memory types for tutoring:
  • Profile: Learning style, pace, strengths, goals
  • Episode: Topic discussions, quiz results, explanations given
  • Foresight: Study reminders, upcoming tests, review schedules

Setup: Initialize Tutor

import requests
from datetime import datetime, timedelta
import uuid

BASE_URL = "https://api.evermind.ai"
headers = {"Content-Type": "application/json"}

class AITutor:
    def __init__(self, subject: str):
        self.subject = subject
        self.tutor_id = f"tutor_{subject.lower().replace(' ', '_')}"

    def enroll_student(self, student_id: str, student_name: str, learning_goals: list = None):
        """Initialize a student's learning session."""
        group_id = f"learning_{student_id}_{self.subject.lower().replace(' ', '_')}"

        # Configure as assistant scene for full memory types
        meta = {
            "group_id": group_id,
            "group_name": f"{self.subject} Tutoring",
            "scene": "assistant",
            "user_details": [
                {"user_id": student_id, "user_name": student_name, "role": "user"},
                {"user_id": self.tutor_id, "user_name": f"{self.subject} Tutor", "role": "assistant"}
            ]
        }

        requests.post(f"{BASE_URL}/api/v0/memories/conversation-meta", json=meta, headers=headers)

        # Store initial learning goals
        if learning_goals:
            goals_text = ", ".join(learning_goals)
            self._store_message(
                group_id=group_id,
                sender_id=student_id,
                content=f"My learning goals for {self.subject}: {goals_text}"
            )

        return group_id

    def _store_message(self, group_id: str, sender_id: str, content: str):
        """Store a tutoring interaction."""
        message = {
            "group_id": group_id,
            "group_name": f"{self.subject} Tutoring",
            "message_id": str(uuid.uuid4()),
            "create_time": datetime.now().isoformat() + "Z",
            "sender": sender_id,
            "sender_name": "Tutor" if sender_id == self.tutor_id else "Student",
            "content": content
        }
        return requests.post(f"{BASE_URL}/api/v0/memories", json=message, headers=headers)

Track Learning Progress

Store quiz results and topic discussions to build a picture of student progress.
def record_quiz_result(self, group_id: str, student_id: str, topic: str, score: int, max_score: int, missed_concepts: list = None):
    """Record a quiz or assessment result."""
    percentage = (score / max_score) * 100

    result_message = f"Quiz completed on {topic}. Score: {score}/{max_score} ({percentage:.0f}%)."

    if missed_concepts:
        result_message += f" Concepts to review: {', '.join(missed_concepts)}."

    if percentage >= 90:
        result_message += " Excellent understanding demonstrated."
    elif percentage >= 70:
        result_message += " Good progress, some areas need reinforcement."
    else:
        result_message += " This topic needs more practice."

    self._store_message(group_id, self.tutor_id, result_message)

def record_explanation(self, group_id: str, student_id: str, topic: str, difficulty_level: str, understood: bool):
    """Record when a concept is explained."""
    status = "understood" if understood else "needs more explanation"

    message = f"Explained {topic} at {difficulty_level} level. Student {status}."

    if not understood:
        message += " Will revisit with different approach."

    self._store_message(group_id, self.tutor_id, message)

Identify Knowledge Gaps

Search memories to identify areas where the student struggles.
def identify_knowledge_gaps(self, student_id: str, group_id: str) -> list:
    """Analyze memory to find topics needing review."""
    search_params = {
        "user_id": student_id,
        "group_ids": [group_id],
        "query": "needs review struggled difficult missed concepts not understood",
        "retrieve_method": "hybrid",
        "top_k": 20,
        "memory_types": ["episodic_memory"]
    }

    response = requests.get(f"{BASE_URL}/api/v0/memories/search", json=search_params, headers=headers)
    memories = response.json().get("result", {}).get("memories", [])

    # Extract topics mentioned in struggling contexts
    gaps = []
    for mem in memories:
        content = mem.get("memory_content", "").lower()
        if any(word in content for word in ["review", "struggled", "difficult", "missed", "not understood"]):
            gaps.append(mem.get("memory_content"))

    return gaps

def get_strong_topics(self, student_id: str, group_id: str) -> list:
    """Find topics the student has mastered."""
    search_params = {
        "user_id": student_id,
        "group_ids": [group_id],
        "query": "excellent mastered understood good progress correct",
        "retrieve_method": "hybrid",
        "top_k": 20,
        "memory_types": ["episodic_memory"]
    }

    response = requests.get(f"{BASE_URL}/api/v0/memories/search", json=search_params, headers=headers)
    memories = response.json().get("result", {}).get("memories", [])

    strengths = [
        mem.get("memory_content")
        for mem in memories
        if any(word in mem.get("memory_content", "").lower()
               for word in ["excellent", "mastered", "understood well", "90%", "100%"])
    ]

    return strengths

Use Foresight for Study Reminders

Create time-based reminders for review sessions and upcoming tests.
def schedule_review(self, group_id: str, student_id: str, topic: str, review_date: datetime):
    """Schedule a topic review using foresight memory."""
    # Store a message that will create a foresight memory
    message = f"Remember to review {topic} on {review_date.strftime('%B %d')}. This topic needs reinforcement based on recent quiz results."

    self._store_message(group_id, student_id, message)

def get_due_reviews(self, student_id: str, group_id: str) -> list:
    """Get reviews and reminders due now."""
    search_params = {
        "user_id": student_id,
        "group_ids": [group_id],
        "query": "review remember study practice",
        "retrieve_method": "hybrid",
        "top_k": 10,
        "memory_types": ["foresight"],
        "current_time": datetime.now().isoformat() + "Z"  # Filter by current time
    }

    response = requests.get(f"{BASE_URL}/api/v0/memories/search", json=search_params, headers=headers)
    memories = response.json().get("result", {}).get("memories", [])

    return [mem.get("memory_content") for mem in memories]

Personalized Question Generation

Use memory context to generate adaptive questions.
def generate_adaptive_question(self, student_id: str, group_id: str, topic: str) -> dict:
    """Generate a question adapted to student's level."""

    # Get student's profile and progress on this topic
    profile_params = {
        "user_id": student_id,
        "query": f"learning style pace {topic}",
        "retrieve_method": "hybrid",
        "top_k": 5,
        "memory_types": ["profile"]
    }

    profile_response = requests.get(f"{BASE_URL}/api/v0/memories/search", json=profile_params, headers=headers)
    profile_memories = profile_response.json().get("result", {}).get("memories", [])

    # Get topic progress
    progress_params = {
        "user_id": student_id,
        "group_ids": [group_id],
        "query": f"{topic} quiz score understanding",
        "retrieve_method": "hybrid",
        "top_k": 5,
        "memory_types": ["episodic_memory"]
    }

    progress_response = requests.get(f"{BASE_URL}/api/v0/memories/search", json=progress_params, headers=headers)
    progress_memories = progress_response.json().get("result", {}).get("memories", [])

    # Format for LLM
    context = {
        "profile": [m.get("memory_content") for m in profile_memories],
        "progress": [m.get("memory_content") for m in progress_memories],
        "topic": topic
    }

    # Generate question with LLM (placeholder)
    return {
        "context": context,
        "prompt": f"Generate a {topic} question appropriate for this student's level"
    }

Complete AI Tutor Implementation

import requests
from datetime import datetime, timedelta
import uuid

BASE_URL = "https://api.evermind.ai"
headers = {"Content-Type": "application/json"}

class AITutor:
    def __init__(self, subject: str):
        self.subject = subject
        self.tutor_id = f"tutor_{subject.lower().replace(' ', '_')}"

    def enroll_student(self, student_id: str, student_name: str) -> str:
        """Initialize learning session for a student."""
        group_id = f"learning_{student_id}_{self.subject.lower().replace(' ', '_')}"

        meta = {
            "group_id": group_id,
            "group_name": f"{self.subject} Tutoring",
            "scene": "assistant",
            "user_details": [
                {"user_id": student_id, "user_name": student_name, "role": "user"},
                {"user_id": self.tutor_id, "user_name": f"{self.subject} Tutor", "role": "assistant"}
            ]
        }
        requests.post(f"{BASE_URL}/api/v0/memories/conversation-meta", json=meta, headers=headers)
        return group_id

    def study_session(self, group_id: str, student_id: str, student_message: str) -> str:
        """Handle a study session interaction."""
        # Store student's question/response
        self._store(group_id, student_id, student_message)

        # Get learning context
        context = self._get_learning_context(student_id, group_id, student_message)

        # Get due reminders
        reminders = self._get_reminders(student_id, group_id)

        # Generate personalized response (implement with your LLM)
        response = self._generate_response(student_message, context, reminders)

        # Store tutor response
        self._store(group_id, self.tutor_id, response)

        return response

    def record_assessment(self, group_id: str, student_id: str, topic: str, score: int, total: int, notes: str = ""):
        """Record quiz/assessment results."""
        pct = (score / total) * 100
        msg = f"Assessment on {topic}: {score}/{total} ({pct:.0f}%). {notes}"
        self._store(group_id, self.tutor_id, msg)

        # Schedule review if score is low
        if pct < 80:
            review_date = datetime.now() + timedelta(days=3)
            self._store(group_id, student_id,
                f"Need to review {topic} by {review_date.strftime('%B %d')} - scored {pct:.0f}%")

    def _store(self, group_id: str, sender_id: str, content: str):
        message = {
            "group_id": group_id,
            "group_name": f"{self.subject} Tutoring",
            "message_id": str(uuid.uuid4()),
            "create_time": datetime.now().isoformat() + "Z",
            "sender": sender_id,
            "sender_name": "Tutor" if sender_id == self.tutor_id else "Student",
            "content": content
        }
        requests.post(f"{BASE_URL}/api/v0/memories", json=message, headers=headers)

    def _search(self, query: str, user_id: str = None, group_ids: list = None,
                memory_types: list = None, top_k: int = 5, current_time: str = None) -> list:
        params = {
            "query": query,
            "retrieve_method": "hybrid",
            "top_k": top_k,
            "memory_types": memory_types or ["episodic_memory", "profile"]
        }
        if user_id:
            params["user_id"] = user_id
        if group_ids:
            params["group_ids"] = group_ids
        if current_time:
            params["current_time"] = current_time

        resp = requests.get(f"{BASE_URL}/api/v0/memories/search", json=params, headers=headers)
        return resp.json().get("result", {}).get("memories", [])

    def _get_learning_context(self, student_id: str, group_id: str, query: str) -> dict:
        return {
            "profile": self._search(query, user_id=student_id, memory_types=["profile"]),
            "progress": self._search(query, user_id=student_id, group_ids=[group_id], memory_types=["episodic_memory"]),
            "gaps": self._search("struggled difficult needs review", user_id=student_id, group_ids=[group_id])
        }

    def _get_reminders(self, student_id: str, group_id: str) -> list:
        return self._search(
            "review remember practice",
            user_id=student_id,
            group_ids=[group_id],
            memory_types=["foresight"],
            current_time=datetime.now().isoformat() + "Z"
        )

    def _generate_response(self, message: str, context: dict, reminders: list) -> str:
        # Implement with your LLM
        return f"[Tutor response based on {len(context['progress'])} progress memories]"


# Usage Example
tutor = AITutor("Calculus")

# Enroll student
group = tutor.enroll_student("student_emma", "Emma")

# Study session
response = tutor.study_session(group, "student_emma",
    "I'm having trouble understanding derivatives. Can you explain?")
print(f"Tutor: {response}")

# Record assessment
tutor.record_assessment(group, "student_emma", "Basic Derivatives", 7, 10,
    "Struggled with chain rule applications")

# Another session - tutor now knows about the struggle
response = tutor.study_session(group, "student_emma",
    "Can we practice more derivative problems?")
# Response will be informed by previous assessment results

Spaced Repetition with Foresight

Implement spaced repetition by scheduling reviews at increasing intervals.
def schedule_spaced_reviews(self, group_id: str, student_id: str, topic: str, mastery_level: int):
    """Schedule reviews using spaced repetition intervals."""
    # Intervals based on mastery (1 = new, 5 = mastered)
    intervals = {
        1: [1, 3, 7],      # New: review in 1, 3, 7 days
        2: [3, 7, 14],     # Learning: 3, 7, 14 days
        3: [7, 14, 30],    # Familiar: 7, 14, 30 days
        4: [14, 30, 60],   # Good: 14, 30, 60 days
        5: [30, 90]        # Mastered: 30, 90 days
    }

    review_days = intervals.get(mastery_level, [7, 14, 30])

    for days in review_days:
        review_date = datetime.now() + timedelta(days=days)
        self._store(group_id, student_id,
            f"Scheduled review: {topic} on {review_date.strftime('%B %d, %Y')}. "
            f"Current mastery level: {mastery_level}/5.")

Best Practices

Build rich profiles over time.
# Store learning preferences explicitly
messages = [
    "I learn better with visual examples",
    "I prefer to practice problems before theory",
    "I usually study in the evenings",
    "My goal is to pass the AP Calculus exam"
]
# These become profile memories for personalization
Record specific, actionable progress notes.
# Good: Specific and actionable
"Scored 85% on integration by parts. Struggled with choosing u and dv."

# Bad: Too vague
"Did okay on integration quiz."
Use memory to adjust question difficulty.
def get_difficulty_level(context: dict) -> str:
    recent_scores = [m for m in context["progress"] if "%" in m.get("memory_content", "")]
    if not recent_scores:
        return "medium"

    # Parse scores and adjust
    # High scores -> harder questions
    # Low scores -> easier questions, more scaffolding

Next Steps