Back to Tutorials
intermediate

Build a Personal AI Tutor

Create an AI tutor that remembers what you've learned, tracks your progress, and adapts to your learning style

Prerequisites

  • Python 3.8+
  • OpenAI API key
  • Memoid account

What You’ll Build

A personalized AI tutor that:

  • Remembers topics you’ve studied
  • Tracks areas where you struggle
  • Adapts explanations to your learning style
  • Provides spaced repetition reminders
  • Builds on previous knowledge

Prerequisites

  • Python 3.8 or higher
  • OpenAI API key
  • Memoid API key

Setup

pip install openai requests

Full Implementation

import os
import requests
from datetime import datetime
from openai import OpenAI

MEMOID_API_KEY = os.environ.get("MEMOID_API_KEY")
OPENAI_API_KEY = os.environ.get("OPENAI_API_KEY")
MEMOID_BASE_URL = "https://api.memoid.dev/v1"

class PersonalAITutor:
    def __init__(self):
        self.openai = OpenAI(api_key=OPENAI_API_KEY)
        self.headers = {
            "Authorization": f"Bearer {MEMOID_API_KEY}",
            "Content-Type": "application/json"
        }

    def get_learning_context(self, student_id: str, topic: str) -> dict:
        """Get student's history with this topic."""
        response = requests.post(
            f"{MEMOID_BASE_URL}/memories/search",
            headers=self.headers,
            json={
                "query": f"studying {topic}",
                "user_id": student_id,
                "limit": 10
            }
        )

        context = {
            "previous_topics": [],
            "struggles": [],
            "strengths": []
        }

        if response.status_code == 200:
            memories = response.json().get("results", [])
            for m in memories:
                memory = m.get("memory", "")
                if "struggled" in memory.lower() or "confused" in memory.lower():
                    context["struggles"].append(memory)
                elif "understood" in memory.lower() or "learned" in memory.lower():
                    context["strengths"].append(memory)
                else:
                    context["previous_topics"].append(memory)

        return context

    def store_learning(self, student_id: str, topic: str, interaction: str, understood: bool):
        """Store learning interaction with metadata."""
        status = "understood" if understood else "needs_review"

        requests.post(
            f"{MEMOID_BASE_URL}/memories",
            headers=self.headers,
            json={
                "messages": [{"role": "user", "content": interaction}],
                "user_id": student_id,
                "metadata": {
                    "type": "learning",
                    "topic": topic,
                    "status": status,
                    "timestamp": datetime.now().isoformat()
                }
            }
        )

    def ask(self, student_id: str, topic: str, question: str) -> str:
        """Handle a learning question."""
        # Get student's learning history
        context = self.get_learning_context(student_id, topic)

        # Build personalized prompt
        system_prompt = f"""You are a patient, encouraging AI tutor.

Your student's learning profile:
- Previous topics: {', '.join(context['previous_topics'][:3]) or 'New student'}
- Areas of struggle: {', '.join(context['struggles'][:3]) or 'None identified yet'}
- Strengths: {', '.join(context['strengths'][:3]) or 'Building foundation'}

Teaching guidelines:
1. Build on what the student already knows
2. Use analogies and examples relevant to their background
3. If they've struggled with related concepts, provide extra clarity
4. Be encouraging and celebrate progress
5. Ask follow-up questions to check understanding"""

        messages = [
            {"role": "system", "content": system_prompt},
            {"role": "user", "content": f"Topic: {topic}\n\nQuestion: {question}"}
        ]

        response = self.openai.chat.completions.create(
            model="gpt-4",
            messages=messages
        )

        answer = response.choices[0].message.content

        # Store this learning interaction
        interaction = f"Studying {topic}: Asked about '{question}'. Tutor explained the concept."
        self.store_learning(student_id, topic, interaction, understood=True)

        return answer

    def mark_confusion(self, student_id: str, topic: str, concept: str):
        """Mark a concept as confusing for review."""
        interaction = f"Student struggled with {concept} while studying {topic}"
        self.store_learning(student_id, topic, interaction, understood=False)

    def get_review_suggestions(self, student_id: str) -> list:
        """Get topics that need review based on spaced repetition."""
        response = requests.get(
            f"{MEMOID_BASE_URL}/memories",
            headers=self.headers,
            params={"user_id": student_id, "limit": 50}
        )

        review_topics = []
        if response.status_code == 200:
            memories = response.json().get("results", [])
            for m in memories:
                metadata = m.get("metadata", {})
                if metadata.get("status") == "needs_review":
                    review_topics.append({
                        "topic": metadata.get("topic", "Unknown"),
                        "memory": m.get("memory", "")
                    })

        return review_topics[:5]

    def quiz(self, student_id: str, topic: str) -> dict:
        """Generate a quiz question based on what the student has learned."""
        context = self.get_learning_context(student_id, topic)

        prompt = f"""Based on what the student has learned about {topic}:
{chr(10).join(context['previous_topics'][:5])}

Generate a quiz question to test their understanding.
Return JSON: {{"question": "...", "answer": "...", "hint": "..."}}"""

        response = self.openai.chat.completions.create(
            model="gpt-4",
            messages=[{"role": "user", "content": prompt}],
            response_format={"type": "json_object"}
        )

        import json
        return json.loads(response.choices[0].message.content)


def main():
    tutor = PersonalAITutor()
    student_id = "student_alex"

    print("Personal AI Tutor")
    print("=" * 40)
    print("Commands: 'quit', 'confused', 'review', 'quiz [topic]'\n")

    current_topic = None

    while True:
        user_input = input("You: ").strip()

        if not user_input:
            continue

        if user_input.lower() == "quit":
            break

        if user_input.lower() == "confused":
            if current_topic:
                concept = input("What concept is confusing? ")
                tutor.mark_confusion(student_id, current_topic, concept)
                print("Noted! I'll explain this differently next time.\n")
            continue

        if user_input.lower() == "review":
            topics = tutor.get_review_suggestions(student_id)
            if topics:
                print("\nTopics to review:")
                for t in topics:
                    print(f"  - {t['topic']}: {t['memory']}")
            else:
                print("\nNo topics need review right now!")
            print()
            continue

        if user_input.lower().startswith("quiz"):
            parts = user_input.split(maxsplit=1)
            topic = parts[1] if len(parts) > 1 else current_topic
            if topic:
                quiz = tutor.quiz(student_id, topic)
                print(f"\nQuiz: {quiz['question']}")
                answer = input("Your answer: ")
                print(f"Correct answer: {quiz['answer']}")
                print(f"Hint: {quiz['hint']}\n")
            continue

        # Parse topic and question
        if ":" in user_input:
            topic, question = user_input.split(":", 1)
            current_topic = topic.strip()
            question = question.strip()
        else:
            question = user_input
            if not current_topic:
                current_topic = input("What topic are you studying? ")

        response = tutor.ask(student_id, current_topic, question)
        print(f"\nTutor: {response}\n")


if __name__ == "__main__":
    main()

Example Session

Personal AI Tutor
========================================
Commands: 'quit', 'confused', 'review', 'quiz [topic]'

You: Python: What is a list comprehension?

Tutor: Great question! A list comprehension is a concise way to create
lists in Python. Think of it as a compact for-loop that fits in one line.

Instead of writing:
squares = []
for x in range(10):
    squares.append(x ** 2)

You can write:
squares = [x ** 2 for x in range(10)]

The pattern is: [expression for item in iterable]

Would you like to try writing one yourself?

You: confused
What concept is confusing? the syntax order
Noted! I'll explain this differently next time.

You: Can you explain the syntax again?

Tutor: Of course! I noticed you found the syntax order confusing, so
let me break it down differently...

[Output adapts based on the confusion marker]

You: review

Topics to review:
  - Python: Student struggled with the syntax order of list comprehensions

Key Features

Adaptive Teaching

The tutor tracks struggles and adjusts explanations:

if "struggled" in memory.lower():
    context["struggles"].append(memory)

Spaced Repetition

Mark topics for review and get suggestions:

def get_review_suggestions(self, student_id: str):
    # Returns topics marked as needs_review

Knowledge Building

References previous learning when explaining new concepts:

system_prompt = f"""
Your student's learning profile:
- Previous topics: {context['previous_topics']}
"""

Enhancements

Add Progress Tracking

def get_progress(self, student_id: str, topic: str) -> dict:
    """Calculate learning progress for a topic."""
    response = requests.post(
        f"{MEMOID_BASE_URL}/memories/search",
        headers=self.headers,
        json={
            "query": topic,
            "user_id": student_id,
            "limit": 100
        }
    )

    understood = 0
    needs_review = 0

    if response.status_code == 200:
        for m in response.json().get("results", []):
            status = m.get("metadata", {}).get("status")
            if status == "understood":
                understood += 1
            elif status == "needs_review":
                needs_review += 1

    total = understood + needs_review
    return {
        "topic": topic,
        "mastery": (understood / total * 100) if total else 0,
        "concepts_learned": understood,
        "concepts_to_review": needs_review
    }

Add Study Plan Generation

def generate_study_plan(self, student_id: str, goal: str) -> list:
    """Generate a personalized study plan."""
    context = self.get_learning_context(student_id, goal)

    prompt = f"""Create a study plan for learning {goal}.
Student background: {context['previous_topics']}
Areas to reinforce: {context['struggles']}

Return a JSON list of topics in order."""

    response = self.openai.chat.completions.create(
        model="gpt-4",
        messages=[{"role": "user", "content": prompt}],
        response_format={"type": "json_object"}
    )

    import json
    return json.loads(response.choices[0].message.content)

Next Steps

  • Add flashcard generation from learned concepts
  • Implement visual progress charts
  • Add support for study groups
  • Integrate with external learning resources

Related Tutorials