AI-тютор: система індивідуального навчання
Персоналізоване навчання — одне з найсильніших практичних застосувань LLM. Традиційні курси припускають однаковий темп і формат для всіх; AI-тютор адаптується до рівня знань, переважного стилю пояснень, темпу засвоєння та конкретних прогалин знань кожного студента.
Архітектура AI-тютора
from anthropic import Anthropic
from pydantic import BaseModel
from typing import Literal, Optional
import json
from datetime import datetime
client = Anthropic()
class StudentProfile(BaseModel):
student_id: str
subject: str
level: Literal["beginner", "intermediate", "advanced"]
learning_style: Literal["visual", "conceptual", "practical", "socratic"]
known_topics: list[str] = []
weak_topics: list[str] = []
session_count: int = 0
last_assessment_score: Optional[float] = None
class LearningSession(BaseModel):
session_id: str
student_id: str
topic: str
messages: list[dict] = []
quiz_results: list[dict] = []
started_at: datetime
class AITutor:
def __init__(self, subject: str, curriculum: dict):
self.subject = subject
self.curriculum = curriculum # {topic: {subtopics, prerequisites, content}}
def _build_system_prompt(self, profile: StudentProfile) -> str:
"""Формує персоналізований system prompt"""
style_instructions = {
"visual": "Використовуй багато прикладів, аналогій, текстових схем. Структуруй інформацію візуально через списки та таблиці.",
"conceptual": "Спочатку пояснюй концепцію цілком, потім деталі. Пов'язуй з іншими концепціями.",
"practical": "Починай з прикладу коду/завдання, потім пояснюй чому. Давай практичні вправи.",
"socratic": "Задавай наводячі питання замість прямих пояснень. Веди студента до розуміння через діалог.",
}
weak_topics_context = ""
if profile.weak_topics:
weak_topics_context = f"\nСтудент испытує труднощі з: {', '.join(profile.weak_topics)}. Звертай особливу увагу при пов'язаних темах."
known_context = ""
if profile.known_topics:
known_context = f"\nСтудент уже добре знає: {', '.join(profile.known_topics[-5:])}. Можеш спиратися на ці знання."
return f"""Ти — персональний тютор по {self.subject}.
Рівень студента: {profile.level}
Стиль навчання: {style_instructions[profile.learning_style]}
{known_context}{weak_topics_context}
Правила:
- Ніколи не давай відповідь одразу на навчальні питання — спочатку перевір розуміння наводячим питанням
- Хвали за правильні відповіді конкретно: "Правильно, саме тому що..."
- При помилці — не кажи "неправильно", запитай "А що якщо розглянути з іншого боку?"
- Адаптуй складність пояснень до рівня {profile.level}
- Після пояснення теми пропонуй перевірочне питання"""
def explain_topic(
self,
topic: str,
profile: StudentProfile,
session: LearningSession,
student_question: str = None,
) -> str:
"""Пояснює тему з урахуванням профілю студента"""
# Отримуємо матеріал по темі з навчальної програми
topic_content = self.curriculum.get(topic, {})
messages = session.messages.copy()
if not messages:
# Перше повідомлення в сесії
user_content = f"Давай вивчимо тему: {topic}"
if topic_content.get("prerequisites"):
user_content += f"\n(Передумови: {', '.join(topic_content['prerequisites'])})"
else:
user_content = student_question or "Продовжимо"
messages.append({"role": "user", "content": user_content})
response = client.messages.create(
model="claude-sonnet-4-5",
max_tokens=2048,
system=self._build_system_prompt(profile),
messages=messages,
)
assistant_message = response.content[0].text
session.messages.append({"role": "user", "content": user_content})
session.messages.append({"role": "assistant", "content": assistant_message})
return assistant_message
def generate_quiz(self, topic: str, profile: StudentProfile, num_questions: int = 5) -> list[dict]:
"""Генерує персоналізовані контрольні питання"""
response = client.messages.create(
model="claude-sonnet-4-5",
max_tokens=2048,
messages=[{
"role": "user",
"content": f"""Створи {num_questions} питань для перевірки знання теми "{topic}".
Рівень студента: {profile.level}
Слабкі місця: {profile.weak_topics}
Поверни JSON:
[{{
"question": "...",
"type": "multiple_choice|open|true_false",
"options": ["A: ...", "B: ...", "C: ...", "D: ..."] (для multiple_choice),
"correct_answer": "...",
"explanation": "Чому цей ответ правильний",
"difficulty": "easy|medium|hard"
}}]
Розподіли складність: {{"easy": 2, "medium": 2, "hard": 1}} для intermediate рівня."""
}]
)
text = response.content[0].text
start = text.find("[")
end = text.rfind("]") + 1
return json.loads(text[start:end])
def check_answer(
self,
question: dict,
student_answer: str,
profile: StudentProfile,
) -> dict:
"""Перевіряє відповідь та генерує зворотний зв'язок"""
is_correct = student_answer.strip().lower() == question["correct_answer"].strip().lower()
if is_correct:
feedback = f"Правильно! {question['explanation']}"
else:
# Генеруємо персоналізоване пояснення помилки
response = client.messages.create(
model="claude-haiku-4-5",
max_tokens=512,
messages=[{
"role": "user",
"content": f"""Студент відповів на питання неправильно.
Питання: {question['question']}
Правильна відповідь: {question['correct_answer']}
Відповідь студента: {student_answer}
Пояснення: {question['explanation']}
Рівень студента: {profile.level}
Напиши коротку, не образливу зворотний зв'язок на 2-3 речення, яка пояснює, чому правильна відповідь правильна."""
}]
)
feedback = response.content[0].text
return {
"is_correct": is_correct,
"feedback": feedback,
"correct_answer": question["correct_answer"],
}
def update_profile(self, profile: StudentProfile, quiz_results: list[dict]) -> StudentProfile:
"""Оновлює профіль студента на основі результатів"""
# Аналізуємо помилки
errors_by_topic = {}
for result in quiz_results:
if not result.get("is_correct"):
topic = result.get("topic", "general")
errors_by_topic[topic] = errors_by_topic.get(topic, 0) + 1
# Оновлюємо слабкі теми
new_weak = [topic for topic, errors in errors_by_topic.items() if errors >= 2]
profile.weak_topics = list(set(profile.weak_topics + new_weak))[-10:] # Максимум 10
# Оновлюємо скор
correct_count = sum(1 for r in quiz_results if r.get("is_correct"))
profile.last_assessment_score = correct_count / len(quiz_results) if quiz_results else None
# Якщо скор > 80% — пропонуємо перехід на наступний рівень
if profile.last_assessment_score and profile.last_assessment_score > 0.8:
if profile.level == "beginner":
profile.level = "intermediate"
elif profile.level == "intermediate":
profile.level = "advanced"
return profile
Адаптивна навчальна траєкторія
class AdaptiveLearningPath:
"""Будує та коригує навчальну траєкторію під студента"""
def generate_path(self, goal: str, profile: StudentProfile, curriculum: dict) -> list[str]:
"""Генерує послідовність тем для досягнення цілі"""
response = client.messages.create(
model="claude-sonnet-4-5",
max_tokens=1024,
messages=[{
"role": "user",
"content": f"""Склади навчальну траєкторію.
Ціль студента: {goal}
Рівень: {profile.level}
Вже знає: {profile.known_topics}
Навчальна програма (доступні теми): {list(curriculum.keys())}
Поверни JSON: {{"topics": ["topic1", "topic2", ...], "estimated_weeks": <число>}}
Порядок від простого до складного, з урахуванням вже вивчених тем."""
}]
)
text = response.content[0].text
start = text.find("{")
end = text.rfind("}") + 1
return json.loads(text[start:end])
Практичний кейс: онлайн-курс програмування
Контекст: EdTech платформа, курс Python для початківців, 2400 студентів. Проблеми: 68% дропаут rate, студенти застрягають на різних темах, один ментор не справлявся.
Впровадження AI-тютора:
- Персональний профіль для кожного студента
- Адаптивні пояснення (4 стилі навчання)
- Автоматичні квізи після кожної теми
- Оновлення профілю за помилками
Результати за 3 місяці:
- Дропаут rate: 68% → 41%
- Середній час на тему: 45 хв → 28 хв (студенти отримували потрібний формат одразу)
- NPS платформи: 34 → 67
- Середній скор фінального тесту: 63% → 78%
Ключове спостереження: студенти зі "socratic" стилем навчання показали найбільший ріст — питально-відповідний формат утримував залучення краще, ніж пасивне читання.
Терміни
- Базовий тютор (пояснення + квіз): 3–5 днів
- Персоналізований профіль + адаптивний контент: 2 тижні
- Адаптивна навчальна траєкторія + оновлення профілю: 1 тиждень
- Повна система з дашбордом прогресу: 4–6 тижнів







