Dev.to AI 🤖 Ai 👁 0 📖 4 min read

Building an Adaptive Learning System with LLM: A Step-by-Step Guide

We're building an adaptive learning system that assesses a student's level in real time and adjusts question difficulty and teaching style accordingly. This kind of personalized tutoring agent is expensive to run on toke

We're building an adaptive learning system that assesses a student's level in real time and adjusts question difficulty and teaching style accordingly. This kind of personalized tutoring agent is expensive to run on token-based providers because each turn includes long educational context, system instructions, and conversation history. Oxlo.ai's flat per-request pricing makes it practical to keep the full learning state in context without worrying about ballooning token costs.

What you'll need

Step 1: Model the student session state

We need a lightweight structure to track the learner's current level, concept strengths, and conversation history. I use a dataclass and a simple dictionary for concept mastery.

from dataclasses import dataclass, field
from typing import List, Dict

@dataclass
class StudentSession:
    student_id: str
    subject: str = "mathematics"
    current_difficulty: int = 3
    concept_mastery: Dict[str, float] = field(default_factory=dict)
    history: List[Dict[str, str]] = field(default_factory=list)

    def add_turn(self, role: str, content: str):
        self.history.append({"role": role, "content": content})

Step 2: Define the adaptive system prompt

The prompt is the core pedagogy. It tells the model to evaluate the last answer, update a concept mastery score, and generate the next question at the appropriate difficulty.

SYSTEM_PROMPT = """You are an adaptive tutoring engine. Your goal is to teach the student {subject} by asking one question at a time.

Rules:
- Evaluate the student's last answer. If wrong, explain the misconception in one sentence, then ask a simpler question on the same concept.
- If correct, increase difficulty slightly and move to a related concept.
- Output strictly valid JSON with keys: evaluation (string), concept (string), difficulty (integer 1-5), next_question (string).
- Never give the answer directly. Hint if the student is stuck.
- Current concept mastery scores: {mastery}
- Keep your total response under 150 words.

Respond only with the JSON object."""

Step 3: Build the tutor function with Oxlo.ai

We point the OpenAI SDK at Oxlo.ai's endpoint. I use Llama 3.3 70B here because it follows structured instructions reliably, but you can swap in Qwen 3 32B or Kimi K2.6 if you need stronger reasoning or vision later.

import json
from openai import OpenAI

client = OpenAI(base_url="https://api.oxlo.ai/v1", api_key="YOUR_OXLO_API_KEY")

def tutor_turn(session: StudentSession) -> dict:
    mastery_str = json.dumps(session.concept_mastery) if session.concept_mastery else "{}"
    
    system_msg = SYSTEM_PROMPT.format(
        subject=session.subject,
        mastery=mastery_str
    )
    
    messages = [{"role": "system", "content": system_msg}]
    messages.extend(session.history[-6:])
    
    response = client.chat.completions.create(
        model="llama-3.3-70b",
        messages=messages,
        temperature=0.4,
        response_format={"type": "json_object"},
    )
    
    raw = response.choices[0].message.content
    parsed = json.loads(raw)
    
    session.concept_mastery[parsed["concept"]] = parsed["difficulty"]
    session.current_difficulty = parsed["difficulty"]
    session.add_turn("assistant", raw)
    
    return parsed

Step 4: Handle the student's answer

After the student replies, we store their message and call the tutor again. This loop is the entire interaction pattern.

def submit_answer(session: StudentSession, answer: str) -> dict:
    session.add_turn("user", answer)
    return tutor_turn(session)

Step 5: Seed the first question and run

We start the session by sending an opening user message so the model generates the first question. Then we simulate a few student responses.

def start_session(student_id: str, subject: str) -> StudentSession:
    session = StudentSession(student_id=student_id, subject=subject)
    session.add_turn("user", "Start the lesson.")
    return tutor_turn(session)

if __name__ == "__main__":
    session = start_session("student_001", "algebra")
    print("Tutor:", session.history[-1]["content"])
    
    answers = [
        "I think x equals 3?",
        "Oh, so I subtract 5 from both sides?",
        "x equals 2",
    ]
    
    for ans in answers:
        print("Student:", ans)
        result = submit_answer(session, ans)
        print("Tutor:", json.dumps(result, indent=2))
        print("---")

Run it

Save everything in tutor.py, replace YOUR_OXLO_API_KEY, and run python tutor.py. You should see JSON output where the tutor evaluates each answer, adjusts the difficulty, and asks the next question. Here is an example of the first turn.

{
  "evaluation": "No previous answer to evaluate. Starting with a moderate algebra problem.",
  "concept": "linear_equations",
  "difficulty": 3,
  "next_question": "Solve for x: 2x + 5 = 11. Show your steps."
}

After a wrong guess, the difficulty drops:

{
  "evaluation": "Incorrect. The student added 5 instead of subtracting.",
  "concept": "linear_equations",
  "difficulty": 2,
  "next_question": "If x + 4 = 9, what is x? Think about what number plus 4 makes 9."
}

Wrap-up

That is the skeleton of a working adaptive tutor. Because Oxlo.ai charges per request rather than per token, you can keep the full concept mastery map and recent history in every context window without surprise costs. Two concrete next steps: wire in Oxlo.ai's vision models like Kimi K2.6 so students can upload photos of handwritten work, or add a retrieval layer using Oxlo.ai's embeddings endpoint to ground questions in a specific textbook or curriculum.

📰 Read the original article on Dev.to AI

Originally published by Dev.to AI. Aggregated on AIWithGhost for educational purposes — full credit and traffic to the original publisher.