VECTIQ
← All posts
Engineering note · Platforms & Software

Nineteen AI Features and Still No Memory of the Student

ECMS had a tutor, a grader, a question generator and fifteen other AI features that had never met each other. The fix was not a twentieth feature - it was one shared model of what each student actually knows.

by Vectiq Engineering · 2026-09-17 · 8 min read
Nineteen AI Features and Still No Memory of the Student

At one point ECMS had nineteen AI features: a RAG tutor, a mentor, an essay grader, a question generator, a content summariser, video transcription, OCR, exam extraction, plagiarism detection, adaptive learning, spaced repetition, early warning, at-risk detection, parent insights, executive analytics, cost governance, multi-provider LLM routing, and more.

It was, on paper, the most AI-rich education platform we had built. In practice it was nineteen strangers. The tutor did not know the student had failed the same topic on last week's exam. The practice module did not know what the tutor had just explained. The early-warning system read attendance and grades and nothing else.

The answer was not a twentieth feature.

One model of the student, written to by everything

We built a learner model: a knowledge state per student, holding mastery per topic, updated as an exponentially weighted moving average with question difficulty as the weight.

The point is not the formula. The point is that every AI in the system reads from it and writes to it. Mastery is fed from three producers:

  1. Exam marking — topic taken from the question's subject.
  2. Assignment auto-grading — same topic source, same path.
  3. Spaced review — correct means a rating of 3 or better, difficulty taken from the FSRS card.

All three write non-blockingly. A failure in the learner-model update must never break the grading flow that triggered it: a student's score is the system of record, the mastery estimate is a derived signal, and the derived signal does not get to take down the record.

CAUTION
Topics have to converge or none of this works

Three producers naming the same concept three different ways gives you three useless half-populated topics. Every producer passes through a topic normalisation layer (topic_aliases) so "Quadratic equations", "Phương trình bậc hai" and "algebra-quadratics" land on one canonical topic. Without that step the model looks like it is working and is quietly measuring nothing.

What the shared model unlocked

Once a student's weak topics were a queryable fact rather than a guess, three things became possible that were not worth building before:

  • Adaptive practice. Questions are drawn from the real question bank, aimed at the weakest topics, with difficulty adjusted per topic from the last three answers. The question is sanitised before it reaches the client, so the correct answer is not sitting in the network response for anyone who opens developer tools. Every answer writes back to the model, which makes practice a producer as well as a consumer.
  • A tutor that is personal. The same tutor, given the student's mastery map, explains from where the student actually is instead of from where the syllabus assumes they are.
  • Earlier early warning. Attendance and grades tell you a student is failing. A falling mastery trend on the topics the next exam covers tells you earlier, while there is still time to do something.

The audit that made us rewrite the roadmap

Before any of this shipped, we ran a review of every AI feature the documentation claimed was done. It found that the roadmap had marked several things "connected" that were not running at all: the AI quota system was dead code, RAG did not separate organisations, search inside transcriptions always returned empty, and the entire AI stack was absent from the production compose file.

We fixed each one, and then we changed the rule for what the word means.

"Connected" now means three things together: there is a path from the UI to real data, permissions are enforced on it, and there is a test. Miss one and it is not connected. It is an unglamorous rule and it has done more for the product's honesty than any feature on the list.

Off by default

The AI layer is disabled by default. AI_SERVICE_ENABLED, EXAM_EXTRACTOR_ENABLED, OCR_SERVICE_ENABLED and AI_MENTOR_ENABLED all ship as false, and every AI endpoint answers with a clean 503 saying the feature is not enabled rather than a stack trace.

This is not hedging. A centre that wants scheduling, attendance, exams and tuition should be able to run the same build without an LLM bill, and a centre that wants the AI should be turning it on deliberately, with the per-organisation token quota that logs what it costs.

The part that compounds

A competitor can copy a feature list in a quarter. What they cannot copy is the accumulated record of what every student in your centre has got right and wrong for two years. The learner model is the piece that gets more valuable the longer it runs — which is exactly why it should have been the first thing built, not the twentieth.

ECMSLearner ModelFSRSEducationNestJS