This repository contains an early terminal-based prototype of an adaptive tutoring agent built with LangGraph, a YAML-defined tutoring routine, and two LLM calls: one for generating tutor messages and one for classifying learner responses.
The goal of this prototype is to explore how an AI tutor can guide a learner through an exercise without immediately giving away the answer. The system collects learner context, follows a routine defined in routine.yaml, routes through a LangGraph state machine, and adapts its next response based on whether the learner is correct, incorrect, stuck, or off-topic.
This repo should be treated as the prototype / proof-of-concept version of the work. The continued implementation work is now happening in:
This repository represents the first working version of the adaptive tutoring routine.
The currently testable runtime is:
src/routine_graph.py
src/routine.yaml
The file:
src/routine_v2.yaml
is included as a design artifact for a more flexible future routine. It is useful for documenting the next direction of the tutoring flow, but it should not be treated as the fully runnable or fully tested runtime path.
In other words:
routine.yamlis the current runnable routine.routine_graph.pyis the current executable LangGraph prototype.routine_v2.yamlis a non-runnable design artifact for future iteration.Allogy/the-primeris the continuation of this work in a more structured codebase.
At a high level, the system works like this:
Learner Profile + routine.yaml
↓
Initialize TutorState
↓
route_next_node
↓
Run the node matching the current YAML step type
↓
Update current_step_id
↓
Return to route_next_node
↓
Repeat until current_step_id == "end"
↓
Update Learner Profile at the end of the session
The most important idea is that routine.yaml defines the tutoring flow, while routine_graph.py executes that flow through LangGraph.
The current prototype supports:
- collecting learner information from the terminal
- loading a YAML-defined tutoring routine
- generating tutor responses with an LLM
- collecting learner responses interactively
- classifying learner responses as
correct,incorrect,stuck, oroff_topic - routing to the next tutoring step based on the classification
- maintaining a lightweight conversation history
- ending the session when the YAML routine reaches the
endstep - testing the YAML routing and LangGraph node behavior without live LLM calls
The tutor is designed to act like a second instructor: it gives hints, scaffolds, redirects, and reinforces learning while avoiding direct final-answer reveals.
.
├── README.md
├── pyproject.toml
├── uv.lock
├── docs/
│ └── tutor_pipeline_flowchart.svg
├── src/
│ ├── __init__.py
│ ├── routine_graph.py
│ ├── routine.yaml
│ └── routine_v2.yaml
└── tests/
└── test_adaptive_tutoring_prototype.py
This file contains the executable LangGraph workflow.
Important components:
| Component | Purpose |
|---|---|
TutorState |
Stores the learner profile, current step, messages, selected route, and conversation history. |
load_routine() |
Loads the YAML routine from routine.yaml. |
get_current_step() |
Finds the YAML step matching the current current_step_id. |
route_next_node() |
Reads current_step_id, checks the step type, and decides which LangGraph node runs next. |
tutor_node() |
Generates a tutor message for a tutor_message step and advances to step.next. |
learner_input_node() |
Collects the learner’s terminal response and advances to step.next. |
learner_check_node() |
Classifies the learner response and selects the next step from the YAML routes. |
build_graph() |
Builds and compiles the LangGraph state machine. |
run_interactive_session() |
Collects initial learner inputs and starts the tutoring session. |
This file defines the current runnable tutoring routine and teaching policy.
It includes:
- session goals and duration assumptions
- required and optional learner inputs
- teaching constraints
- conceptual knowledge-graph goals
- exercise-generation goals
- response classification labels
- YAML flow steps
- route behavior after learner evaluation
- future learner-state update goals
The current runnable flow starts at:
start_step: present_exerciseThen proceeds through this loop:
present_exercise
↓
wait_for_learner_response
↓
evaluate_response
↓
correct → correct_feedback → another_exercise
incorrect → targeted_hint → wait_for_learner_response
stuck → scaffold → wait_for_learner_response
off_topic → redirect → wait_for_learner_response
The continuation flow then asks whether the learner wants another exercise:
another_exercise
↓
check_another_exercise
↓
yes → present_exercise
no → end
unclear → another_exercise
This file is a design artifact for a more flexible future tutoring routine.
It should be read as a planning document for the next version of the system, not as the current tested runtime. The current tests only verify that this file parses as valid YAML. The executable behavior is still centered on routine_graph.py and routine.yaml.
The LangGraph workflow is controlled by current_step_id.
run_interactive_session()collects learner information.routine.yamlis loaded into the initialTutorState.current_step_idis set to the YAMLstart_step, which is currentlypresent_exercise.route_next_node()checks the current YAML step.- Depending on the YAML step type, the graph runs one of three nodes:
tutor_message → tutor_node
learner_input → learner_input_node
learner_check → learner_check_node
- Each node returns a partial state update.
- The updated
current_step_iddetermines the next step. - LangGraph returns to
route_next_node(). - The session repeats until
current_step_id == "end".
The evaluator classifies the learner’s latest response into one of four labels:
| Label | Meaning | Next Behavior |
|---|---|---|
correct |
The learner answered the exercise correctly. | Give brief positive feedback, then offer another exercise. |
incorrect |
The learner attempted an answer but made an error. | Give one targeted hint and ask them to try again. |
stuck |
The learner is unsure, asks for help, or gives no substantive answer. | Scaffold with a simpler diagnostic question. |
off_topic |
The learner’s response is unrelated to the exercise. | Redirect the learner back to the current exercise. |
These labels are not hardcoded as final destinations in the Python graph. Instead, learner_check_node() reads the route map from routine.yaml:
routes:
correct: correct_feedback
incorrect: targeted_hint
stuck: scaffold
off_topic: redirectThis keeps the tutoring policy easier to change without rewriting the graph logic.
When the program starts, it asks for:
Name:
Session goals:
Difficulty level:
Current level:
Learning preferences:
Target concepts, separated by commas:
Example input:
Name: Joseph
Session goals: Practice solving one-step equations
Difficulty level: beginner
Current level: understands variables but struggles with inverse operations
Learning preferences: hints before explanations
Target concepts, separated by commas: variables, equations, inverse operations
The tutor then generates an exercise, waits for the learner’s answer, classifies the response, and routes to the next YAML step.
Install dependencies with uv:
uv syncRun the interactive prototype:
uv run python src/routine_graph.pyThe prototype expects the relevant LLM API key configuration to be available in your local environment.
The tests are designed to validate the prototype behavior without making live LLM calls.
Run:
uv run pytest -vThe current tests cover:
- loading the runnable routine
- validating YAML step references
- checking route targets and fallback routes
- confirming that
routine_v2.yamlparses as a design artifact - testing route selection for tutor, learner input, learner check, and end states
- monkeypatching fake tutor/classifier models
- checking that the LangGraph workflow compiles
The routine is stored in routine.yaml so that prompts, transitions, and route behavior can be edited without changing the LangGraph implementation.
The graph does not manually call steps in a fixed order. Instead, it repeatedly checks current_step_id, finds the matching YAML step, and routes to the correct node type.
The system uses two LLM configurations:
- a tutor model with a larger token budget for student-facing responses
- a classifier model with a small token budget for route labels
This separates the teaching behavior from the routing/evaluation behavior.
The tutor is constrained to guide without giving away final answers. It should ask questions, provide targeted hints, and scaffold the learner toward the next step.
This repository is the earlier XRP Primer Kit prototype.
The continuation of this work is now happening in:
That repository is intended to be the more structured implementation path, with a cleaner project layout and a broader YAML-driven tutoring engine. This repo remains useful as a compact prototype showing the original LangGraph routine loop and routing logic.
This is an early prototype. Some parts of the larger pipeline are currently conceptual rather than fully implemented.
Current limitations include:
- the knowledge graph is described in
routine.yamlbut not yet implemented as a persistent data structure - learner state is not saved across sessions
- evaluation metrics are specified conceptually but not logged automatically
- the interface is terminal-based
routine_v2.yamlis not a tested executable routine- live tutor behavior still depends on external LLM configuration
- this repo is not the main continuation repo for the project
Planned extensions include will all be in the following repository: Allogy/the-primer
MIT License.