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