Inspiration

Most students find out a course is going badly when the final grade arrives, but the decision that matters, whether to stay in or withdraw, has a deadline weeks earlier. By then the signs were already there: missed deadlines, a weak midterm, late-night cramming. We wanted something that reads those signs early, explains them in plain language, and helps a student make that call with an advisor instead of guessing alone.

What it does

MiAdvisor is an AI student hub. A student:

  1. Uploads a syllabus. Gemini pulls out the graded items with their weights and due dates, and the student reviews them before saving.
  2. Rates how confident they feel about each item (1–10) and fills in a short weekly check-in.
  3. Gets a risk prediction from a decision tree, with the exact path the tree took, shown in plain English.
  4. Sees a projected final grade and the score they'd need on the remaining work to finish with a C.
  5. Gets a stay/withdraw recommendation with reasons, next steps and a "talk to your advisor" prompt.
  6. Listens to a 30-second spoken briefing, written by Gemini and read aloud by ElevenLabs.
  7. Studies with AI-generated flashcards, practice quizzes and a day-by-day study plan.

Every risk card carries a "This is guidance, not a verdict" disclaimer, and everything an AI writes is labeled "AI-generated".

How we built it

  • ML: scikit-learn decision tree trained on the WolfHacks synthetic student-outcomes dataset (target at_risk, about 28% positive), using an 80/20 stratified split and class_weight="balanced". Depth was chosen with cross-validation, and we kept it at 3 so the whole tree can be explained. The model is served by FastAPI.
  • Backend: Spring Boot 3 (Java 17) and Postgres. It handles auth, courses, ratings and the recommendation logic.
  • Frontend: React + Vite + Tailwind.
  • AI services: Google Gemini (syllabus extraction, study materials, plain-language rewrites, briefing scripts) and ElevenLabs (text-to-speech).
  • Running it: the whole stack runs locally. Postgres in Docker, the FastAPI ML service, the Spring Boot API and the Vite dev server start with a few commands (see the README).

The grade projection

Each item $i$ has a weight $w_i$ (its share of the final grade). Its score $x_i$ is the actual percentage if it has been graded, or $10 \times \text{self-rating}$ if not. The projected final grade is the weighted average:

$$ \hat{g} = \frac{\sum_i w_i \, x_i}{\sum_i w_i} $$

From actual grades only, the average needed on all remaining work to finish at 70 is

$$ s_{\text{req}} = \frac{70 - E}{R} $$

where $E$ is the points already earned and $R$ is the share of the grade still ahead.

The stay/withdraw score

We combine the model with the student's own situation:

$$ W = 0.40\,r \;+\; 0.25\,\mathbb{1}[\hat{g} < 70] \;+\; 0.20\,\mathbb{1}[s_{\text{req}} > 85] \;+\; 0.15\,\mathbb{1}[\text{distress}] $$

Here $r$ is the model's risk, and "distress" means more than half of the rated items are at or below 4/10. $W$ maps to one of five outcomes, from strong stay to strong withdraw. Two guard-rail rules sit on top. If a C is still reachable and the student is passing or has most of the grade ahead, the outcome is never worse than lean stay. If they're below 70 and a C is out of reach, it is at least lean withdraw.

Results on 160 held-out students

Metric Decision tree Always say "fine"
Accuracy 79.4% 72.5%
Recall on at-risk 77.3% (34 of 44) 0%
Precision when flagged 60% n/a
ROC-AUC 0.87 0.5

Train accuracy was 86.1% against 79.4% on the test set, a gap of 6.7 points, so there's little overfitting. The tree relies mostly on midterm score (59%), on-time submissions (22%), attendance (11%) and late-night studying (8%).

Challenges we ran into

  • The accuracy trap. A "model" that says everyone is fine scores 72.5% accuracy and catches no struggling students. We optimised for recall with balanced class weights, because missing a student who needs help costs far more than a false alarm.
  • Probabilities that aren't calibrated. A tree's leaf probability isn't a true confidence, so we show high / moderate / low risk rather than a misleading "82% confident".
  • Keeping the LLM honest. Gemini sometimes invented statistics ("9 out of 10 students recover"). Every AI rewrite is now checked: if it adds a number that wasn't in its input, or changes the number of reasons, we fall back to template text. The voice briefing's disclaimer is added by code, so the model can't drop it.
  • Reasons that contradicted the headline. Early versions could say "consider withdrawing" next to "a C is within reach". We now write the reasoning after the decision, using wording that matches it.
  • Missing data. Sleep hours and work hours had gaps, so we imputed or dropped them in the notebook.
  • Free-tier limits. We added per-user and global rate limits, caching and graceful fallbacks so the app keeps working when an API key is missing or a quota runs out.

What we learned

  • On imbalanced problems, a simple, explainable model with the right metric beats a black box chasing accuracy.
  • Responsible AI is mostly product design: plain-language "why" explanations, uncalibrated scores shown as levels, labels on AI content, and a nudge to talk to a human before a big decision.
  • LLMs are best used to explain decisions, not make them. Our decisions come from a transparent model and fixed rules, and the LLM only rewords them, under checks.

What's next

  • Train on real (consented, anonymised) institutional data and calibrate the probabilities.
  • Read deadlines directly from calendar ICS feeds.
  • Read flashcards aloud and send weekly voice check-ins.
  • Add an advisor view so students can share their briefing before a withdrawal meeting.
  • Deploy it so students can use it without running it themselves.

Built With

Share this project:

Updates

Submission history