Inspiration
I kept seeing the same frustration in German and English classrooms: me or another classmate would struggle with fractions on Monday, get help from a tutor or app, and by Wednesday it was like none of that happened. Every new session started from zero — the tutor did not remember, the School LMS(canvas) did not connect to the practice app, and generic chatbots had no idea what that student had already tried.
That gap is what pushed me to build Duselang. I wanted an education platform where continuity is the default — where practice, quizzes, coursework, and tutoring share one brain, and where an AI tutor actually remembers each student’s strengths, gaps, and preferences across sessions and terms.
Build Week gave me the push to make that memory layer real, not just a demo. The question I kept asking was: What if the model only had to speak — and never had to guess what a student already knows?
What it does
Duselang is a production education backend: adaptive K–12 language practice (IXL-style), live quizzes (Kahoot-style), an LMS (Canvas-style), billing, and an AI tutor with persistent cross-session memory.
When a student struggles — say, on a fractions quiz — the system writes that into episodic and semantic memory deterministically. In a later session, when they ask “What should I focus on?”, a ContextAssembler retrieves the right memories within a tight context budget, and GPT-5.6 phrases a reply that actually references what happened before.
Most chat never hits the model at all. A three-tier NLU pipeline handles greetings, grades, and common intents with Rust rules and pgvector; the LLM is reserved for genuinely open-ended questions. Grades and LMS actions go through a human-in-the-loop pipeline — the AI can suggest, but instructors approve before anything reaches a gradebook.
How I built it
I built Duselang as an Elixir/Phoenix umbrella — separate OTP apps for IXL, Kahoot, Canvas, payments, and AI, each with its own database and supervisor tree, behind one API gateway. That way a crash in a live quiz does not take down adaptive practice sessions.
The core architectural decision — one I landed on while building with Codex and GPT-5.6 — was: think in Elixir/Rust, speak with the model. All educational reasoning stays deterministic: SmartScore, diagnostic placement (A–M CAT), risk scoring, memory distillation, and policy enforcement. GPT-5.6 only does two jobs through a Python bridge: :interpret (unstructured text → structured JSON) and :phrase (structured facts → fluent tutor language).
Codex accelerated the hardest parts: the memory pipeline (working, episodic, semantic layers), the three-tier NLU routing, the safety action pipeline (propose → risk assess → audit → instructor approval), and wiring the umbrella so Canvas, Kahoot, and IXL events all feed the same AI layer.
Performance-critical paths use Rust NIFs (rule matching, policy engine, knowledge graph). Background work runs through Oban. Memory retrieval uses pgvector for semantic lookup. Frontends are separate SPAs that consume the REST/WebSocket API — the backend is the product in this repo.
Challenges I ran into
Making memory useful, not noisy. Storing every chat message is easy; retrieving the right facts within a context budget is hard. I had to distill events into episodic memories and reinforced semantic facts (struggles_with:fractions, prefers:visual_examples) instead of dumping raw logs into the prompt.
Keeping the LLM out of decisions that matter. Early on, it was tempting to let the model grade essays or recommend interventions directly. I pushed all of that into Elixir/Rust engines and left GPT-5.6 with phrasing only. That meant stricter contracts, more structured JSON, and a human-in-the-loop gate before any LMS write.
Building five products as one system. An umbrella with separate databases per domain is powerful but heavy — migrations, seeds, cross-app event schemas, and JWT auth across /v1, /api, /admin, and /canvas/v1 all had to stay consistent.
Cost and latency discipline. Letting every student message hit GPT-5.6 would not scale. Getting tier-1/tier-2 NLU to handle 80%+ of traffic — and instrumenting [:duselang_ai, :nlu, :route] telemetry to prove it — took real iteration.
From mocks to a judge-ready demo. Frontends started with mocked data; wiring every learn-hub and command endpoint to real backend responses (medals, pricing, todos, feedback, file uploads) was a long integration pass before the system felt production-real.
Deploying for real. Getting the backend onto cloud infrastructure with Postgres, Redis, object storage, and a live /health/qwen (or equivalent LLM health) endpoint meant production env wiring, not just mix phx.server on localhost.
Accomplishments that we're proud of
- A memory-aware tutor that recalls student struggles across sessions — not a stateless chatbot bolted onto homework.
- A full education stack in one API: adaptive practice, live quizzes, LMS, payments, and AI — not three separate SaaS tools duct-taped together.
- Safety by design: AI-proposed grades and actions require instructor approval; kill switches and audit rows on every side effect.
- Provider-agnostic NL layer: swap GPT-5.6 (or any OpenAI-compatible API) via env vars; offline deterministic fallback when no key is set — judges can run locally without paid keys.
- Open source under MIT with architecture docs, a judge testing guide, and a deploy runbook — not a throwaway hackathon repo.
- Shipping end-to-end: removed frontend mocks, connected the AI engine to Canvas/IXL/Kahoot events, and deployed to cloud.
What I learned
The biggest lesson: LLMs are great at language, not at trust. In education, the model should explain and encourage — not silently change grades, risk scores, or policy outcomes. Putting reasoning in Elixir/Rust and keeping the LLM on a short :interpret / :phrase leash made the system safer and easier to debug.
Second: memory needs architecture, not prompts. “Remember the student” in a system prompt does not survive scale. Explicit episodic + semantic stores, event-driven distillation, and budget-aware retrieval (Memory.bundle/3 → ContextAssembler) is what makes continuity real.
Third: Codex changed how fast I could explore architecture. I could iterate on the NLU tiers, memory pipeline, and umbrella boundaries in hours instead of days — but the hard part was still choosing the right boundaries. AI sped up building; it did not replace thinking about what schools actually need.
Fourth: schools need one platform, not ten tabs. Practice, assessment, coursework, and tutoring belong together — with one student identity and one memory graph.
What's next for Duselang
- Pilot with real schools — validate memory-aware tutoring against baseline “stateless tutor” outcomes on retention and time-to-mastery.
- Richer frontends — polish learn-hub, command, and command-center UX now that the API layer is wired.
- Stronger diagnostics — deepen the A–M CAT engine and per-strand Comprehension tracking for German language arts.
- LMS adapter expansion — tighten integrations with Canvas, Moodle, Schoology, Blackboard, and Google Classroom for schools that cannot rip-and-replace.
- Memory transparency — let students and teachers see what the tutor remembers and correct it, building trust in the agent.
- Multilingual tutoring — extend the same memory architecture across locales without fragmenting student profiles.
Built With
- bvm
- cloud
- codex
- cowboy
- dtrace
- elixir
- erlang
- hoenix-1.7+
- nifs
- oban
- openai
- pgvector
- phoenix
- postgresql
- python
- redis
- rust
- shell
- websockets
- wen
Log in or sign up for Devpost to join the conversation.