_SH Log's
Back to Root
EST: 5 min read

Building offSchool: AI Adaptive Learning + Knowledge Graphs

offSchool builds personalized study plans with spaced repetition and knowledge graphs on a Next.js + Go + Python stack. Here's the architecture and design decisions.

#ai#education#knowledge-graph#go

offSchool is an AI-powered adaptive learning platform that generates personalized study plans using knowledge graphs and spaced repetition. Unlike generic flashcard apps, offSchool understands which concepts you know, which you're shaky on, and — critically — which concepts are blocking your understanding of others.

The core insight: learning has a dependency graph

Every field of knowledge is a directed graph:

Linear Algebra
├── Vector Spaces → requires Matrix Operations
├── Eigenvalues → requires Linear Independence
├── Matrix Operations → requires Scalar Multiplication
└── ...

Most learning apps treat content as a flat list. offSchool treats it as a graph. If you can't factor polynomials, the platform knows not to show you calculus — and it knows exactly why.

Knowledge graph data model

CREATE TABLE concepts (
    id          UUID PRIMARY KEY,
    subject     TEXT NOT NULL,
    name        TEXT NOT NULL,
    description TEXT,
    difficulty  INT CHECK (difficulty BETWEEN 1 AND 10),
    embedding   vector(1536)  -- for semantic similarity search
);

CREATE TABLE prerequisites (
    concept_id     UUID REFERENCES concepts(id),
    prerequisite_id UUID REFERENCES concepts(id),
    strength       FLOAT DEFAULT 1.0, -- 0.5 = soft prereq, 1.0 = hard
    PRIMARY KEY (concept_id, prerequisite_id)
);

CREATE TABLE user_mastery (
    user_id     UUID,
    concept_id  UUID REFERENCES concepts(id),
    score       FLOAT DEFAULT 0.0,     -- 0.0 = unknown, 1.0 = mastered
    next_review TIMESTAMPTZ,            -- SM-2 spaced repetition
    reviews     INT DEFAULT 0,
    PRIMARY KEY (user_id, concept_id)
);

The prerequisites table with strength values enables soft and hard prerequisites. A hard prerequisite (strength: 1.0) blocks the concept entirely. A soft prerequisite (strength: 0.5) adjusts difficulty weighting but doesn't block.

Spaced repetition with SM-2

offSchool uses the SM-2 algorithm for review scheduling:

def sm2_update(quality: int, repetitions: int, easiness: float, interval: int):
    """
    quality: 0-5 (0=blackout, 5=perfect recall)
    Returns: (new_repetitions, new_easiness, new_interval_days)
    """
    if quality < 3:
        repetitions = 0
        interval = 1
    else:
        if repetitions == 0:
            interval = 1
        elif repetitions == 1:
            interval = 6
        else:
            interval = round(interval * easiness)
        repetitions += 1

    easiness = max(1.3, easiness + 0.1 - (5 - quality) * (0.08 + (5 - quality) * 0.02))
    return repetitions, easiness, interval

The next_review timestamp is set based on now() + interval days. Items due for review surface at the top of the study queue.

Personalized study plan generation

The study plan algorithm combines three signals:

  1. Prerequisite satisfaction — which concepts are unlocked given current mastery?
  2. SM-2 due queue — which concepts need review today?
  3. Learning velocity — what's the fastest path to a user-stated goal?
func (s *StudyPlanner) GeneratePlan(ctx context.Context, userID, goalConceptID uuid.UUID) (StudyPlan, error) {
    mastery, err := s.db.GetUserMastery(ctx, userID)

    // Find all unmastered prerequisites on path to goal
    path := s.graph.ShortestPath(mastery.Mastered(), goalConceptID)
    
    // Get SM-2 due items
    dueReviews, _ := s.db.GetDueReviews(ctx, userID, time.Now())
    
    // Merge: reviews first (SM-2 efficiency), then new concepts
    plan := StudyPlan{}
    plan.Sessions = append(plan.Sessions, dueReviews...)
    for _, concept := range path {
        if !mastery.IsMastered(concept) {
            plan.Sessions = append(plan.Sessions, concept)
        }
    }
    
    // Trim to 45-minute session (~8-10 items)
    return plan.Trim(10), nil
}

The shortest path algorithm uses Dijkstra with edge weights = prerequisite strength. Stronger prerequisites are traversed first.

AI content generation

When a user encounters a concept they don't understand, offSchool generates an explanation tailored to their current mastery level:

EXPLANATION_PROMPT = """
User knows: {known_concepts}
User struggles with: {weak_concepts}
Concept to explain: {concept_name}

Generate a 200-word explanation that:
1. Builds on what the user already knows
2. Uses analogies from their strongest known concepts
3. Ends with one concrete worked example
4. Avoids prerequisite concepts they haven't mastered yet
"""

The explanation is generated once and cached per (user_mastery_snapshot, concept) pair. Cache key includes a hash of the user's mastery vector — similar mastery profiles share cached explanations.

Stack

  • Frontend: Next.js 15 App Router + TypeScript + Tailwind
  • API: Go + Chi router + sqlc
  • ML/AI: Python FastAPI service (SM-2, path finding, embedding generation)
  • DB: PostgreSQL with pgvector extension
  • Deployment: AWS ECS Fargate (Go + Python services), Cloudflare Pages (Next.js)

FAQ

What is offSchool? offSchool is an AI adaptive learning platform that builds personalized study plans using knowledge graphs (concept prerequisites) and spaced repetition (SM-2 algorithm) to help learners reach their goals efficiently.

What is spaced repetition? Spaced repetition is a learning technique that schedules reviews at increasing intervals based on recall quality. The SM-2 algorithm, used by Anki and offSchool, calculates optimal review timing to maximize long-term retention.

How does offSchool use knowledge graphs? offSchool models each subject as a directed graph where concepts depend on prerequisites. The platform identifies which concepts are currently accessible given a learner's mastery, avoiding presenting content that requires unlearned foundations.

What subjects does offSchool support? Mathematics, computer science, physics, and chemistry. Each subject's knowledge graph is curated manually and refined by tracking which prerequisites actually correlate with mastery.


Written by Shihab Shahriar Antor — AI Engineer & Founder of Shahriar Labs. See also: Building Context-Heavy: Knowledge-Graph API for AI Agents · Building QuantumSketch: AI + Manim for STEM Video.