| š Utz'tutor | š Docs | āļø Backend | š± PWA | š Infra |
|---|
š Root Overview ⢠Quick Links: Architecture ⢠Roadmap ⢠API Hub ⢠Database Schema
A classroom AI tutor in a box, for rural schools with no internet at all.
A small computer and a Wi-Fi router turn any classroom into an interactive maths lesson: students answer on their phones or on cheap button clickers, and the box spots common mistakes and explains them out loud, in Spanish or K'iche'.
A real session on a development machine: the teacher builds a fractions quiz, students join, and when most of the class falls for the same wrong answer, the box explains that specific mistake aloud.
Note
Work in progress: this is an engineering capstone project under active development.
Many schools in rural Guatemala have no internet and few resources, and many students learn in K'iche' as well as Spanish. Utz'tutor brings an AI tutor to them anyway: everything, including the language model and the voices, runs on a box in the classroom. Nothing ever needs a connection.
- Three modes in one box: a classroom quiz the whole class plays together, a Socratic tutor for practice after class, and take-home apps with offline games for each grade.
- Wrong answers that teach. Every wrong option is designed around a specific mistake ("divided only the numerator", "subtracted to simplify"), so the results show why students got it wrong, not just how many.
- Speaks up when the class is stuck. If more than 51% of the students pick the same wrong answer, the box explains that mistake aloud, offline, in Spanish or K'iche'.
- AI that is kept honest. A small language model on the box (Gemma) writes new questions and talks to students, but a computer algebra engine (SymPy) checks the maths every time. The model never decides what is correct.
- Works with or without phones. Students answer on a phone or on an ESP32 clicker with four buttons and a light.
- Safe for kids. Students log in with a PIN that's stored hashed, never in plain text, and the network is a closed local Wi-Fi.
The recording at the top: the teacher picks questions from a bank of 66 (or has new ones written, below), students join, and the results show not just who was right but which mistake the class made. When most of the class falls for the same one, the box explains it out loud, and after the game the teacher gets a short report of the mistakes that keep coming up.
Three fraction questions written by Gemma on the box. Question 2 failed the check and was dropped.
The teacher picks a topic and Gemma writes the questions, each with one right answer and three wrong options built around known mistakes. Before anything reaches the class, SymPy works every problem out on its own:
- the answer marked as correct must equal what SymPy computes,
- no wrong option may secretly be correct too,
- and each wrong option must name a mistake from that topic's list.
A draft that fails goes back to the model with the reason; after three tries the question is dropped. In this recording Gemma marked 2/5 as the answer to a question whose answer is 1/2: SymPy caught it, and the question never made it into the game. The teacher can still read and edit every question, including the explanations, in the question bank.
A real practice chat on a student's phone, with the classroom TV in tutor mode.
After class, students practise on their phones. When an answer is wrong, the tutor works out which mistake it was (4/12 means the tops and the bottoms were added) and replies with a hint from a ladder of up to three, never with the answer. Gemma rewrites each hint in a friendlier voice, and a guard throws the rewrite away if it reveals the answer, loses the problem's numbers or stops asking a question; the planned hint is used instead. In this chat it did exactly that, twice. The classroom TV only shows totals (connected, solved, asking for help), never names.
The first-grade app: pick a character, follow Q'uq' the quetzal across the map, and play.
In take-home mode the box serves an Android app for each grade (first, second and third) at /descargas. Once installed it works with no connection at all: short "look, listen, tap" games that need no reading, one level per competency of the national curriculum (CNB), with progress saved on the phone.
The clicker on the right is a drawing, but its light follows the real firmware, which ran here in its PC simulator and voted as mateo.
A clicker is a small ESP32-C3 board with four answer buttons and one light. It joins the box's Wi-Fi by itself and appears in the teacher's list; the teacher assigns it to a student once. When a question opens the light blinks white, the student presses A, B, C or D, and the light turns green when the vote is counted. The build and flashing guide covers the parts, the wiring and what every light means.
The interface is in Spanish, as used in the classroom. Everything shown was recorded on a development machine, with Gemma 4 running locally through llama.cpp.
| Part | What it does |
|---|---|
| NVIDIA Jetson Orin Nano (8 GB) | The box: serves the web app, runs the language model, the voices, the maths engine and the database, and drives the classroom display over HDMI. |
| GL.iNet GL-AR300M16 router | A private Wi-Fi network called Utz'tutor that students' phones join. |
| ESP32-C3 clickers | Cheap physical answer buttons for students without phones. |
All of it works 100% offline.
For development you need uv and pnpm; one command does the rest:
curl -LsSf https://astral.sh/uv/install.sh | sh # install uv, if needed ./run.py
run.py checks what's installed, builds the web app, sets up the database, loads the question bank and starts the server. Then open:
| Who | Address |
|---|---|
| Teacher | http://localhost:8000/maestro/ |
| Students | http://localhost:8000/alumno/ |
| Classroom display | http://localhost:8000/pantalla/ |
| API documentation | http://localhost:8000/docs |
A first teacher account, teacher1, is created automatically; change its PIN before real use (see the authentication docs).
- Without the language model the question bank and quizzes still work; generating new questions needs
llama-serveron127.0.0.1:8080. - Voices: without the neural voice models it falls back to eSpeak-ng. Download better voices with
./run.py --download-models all(orminimal,kokoro,qwen). - A real classroom kit (the Jetson and its router) is set up with Ansible: follow the kit setup guide, which runs
./run.py --prepare.
graph TD
Students["Phones & ESP32 clickers"] <-->|Wi-Fi| Router["Local router<br/>(no internet)"]
Router <--> Nginx["Nginx"]
subgraph Box ["Jetson Orin Nano"]
Nginx --> PWA["Web app (React PWA)"]
Nginx <--> API["FastAPI backend"]
API <--> SymPy["SymPy: checks all the maths"]
API <--> DB[("SQLite")]
API <--> LLM["Gemma via llama.cpp:<br/>writes questions, tutors"]
API <--> Voice["Offline voices:<br/>Qwen3-TTS ā Sherpa ā Piper ā eSpeak"]
API --> HDMI["Classroom display (HDMI)"]
end
- Backend: Python 3.11+, FastAPI and WebSockets, SQLite with versioned migrations.
- Frontend: a React and Vite progressive web app, served by Nginx from the box.
- Maths: SymPy is the only authority on whether an answer is correct.
- Language model: Gemma 4 (A2B, quantised to Q4_K_M) through
llama.cpp. - Voices: Qwen3-TTS for the best Spanish, falling back to Sherpa-ONNX, Piper and finally eSpeak-ng so there is always a voice.
TutorBox/
āāā backend/ # FastAPI app: quiz sessions, Socratic tutor, SymPy checks, voices, database
āāā pwa/ # teacher, student and classroom-display web apps
āāā firmware/ # ESP32-C3 clicker firmware and a PC simulator
āāā infra/ # Ansible kit setup, Nginx, systemd, captive portal, router guides
āāā tools/ # launcher modules, voice model downloader, benchmarks
āāā docs/ # architecture, pedagogy, API and database documentation
āāā run.py # one-command startup
- Documentation portal
- The three modes and how wrong answers diagnose mistakes
- Voice feedback
- REST API and database
- Backend developer guide
Made by @GabGP and @guillermo-martinez42.








