Inspiration

That gap isn't a minor UX detail — it's a well-documented problem in learning science. Binary right/wrong feedback corrects the symptom of a mistake without ever touching the misconception that produced it.

A student who writes \( \frac{1}{2} + \frac{1}{3} = \frac{2}{5} \) hasn't made a careless slip. Somewhere in their mental model, fractions stopped being "parts of a whole" and became "two independent numbers stacked on top of each other" — so of course they added numerators to numerators and denominators to denominators, exactly the way you'd add any other pair of numbers. Marking that answer wrong and moving on doesn't fix anything. It guarantees the same error resurfaces on the next fraction, the next percentage, the next equation — each time looking like an unrelated new mistake instead of the same root cause wearing a different costume.

We kept returning to one reframe:

An error is not a failure to punish. It is a diagnostic signal to read.

A doctor doesn't tell a patient "you're sick, try again." They ask questions, isolate a cause, and treat that cause specifically. We wanted a tutor that did the equivalent for learning: read the reasoning behind an answer, name the exact mental model that produced it, and teach from there — not from a generic re-explanation of a method the student already half-understands and is misapplying.

The name and the visual identity come from the same instinct. Amauta is Quechua for wise teacher — the scholars of the Inca empire who transmitted knowledge without formal schools or writing as we know it. Their answer to "how do you keep a record of complex, evolving knowledge without books" was the khipu: knotted, colored cords where structure and color carried meaning. That felt like exactly the right metaphor for a progress tracker that isn't a percentage bar, but a living map of where a learner's understanding actually stands — concept by concept, thread by thread.

Midway through building, one question reshaped the entire project: is this about math, or about how minds go wrong in general? A student who writes "I am agree" in English hasn't forgotten grammar — they've translated the Spanish estoy de acuerdo word for word, because that is the model their native language trained them to reach for. Structurally, that is the identical failure mode as the fraction error: a real, coherent, confidently-applied model, deployed in the wrong place. Once we saw that, keeping Amauta restricted to math felt like an arbitrary cap on an idea that was never really about math to begin with. So we extended it to language learning — English, Japanese, and French — running on the same diagnostic engine, because the thesis underneath both is the same one.

What it does

Amauta is an AI tutor that diagnoses the reasoning behind your mistakes across two domains — mathematics and languages — instead of simply grading your final answer.

Zero-friction start. You land on the site and practice immediately as a guest: no account, no email, nothing to configure. Behind the scenes that guest identity is a real (if anonymous) session — which matters later.

You choose the topic, not a menu. Pick a domain, then type in plain language whatever you actually want to practice: "derivatives," "trigonometry," "は/が particles," "present perfect," "false friends." The AI interprets that request and generates a brand-new exercise on the spot, calibrated to a difficulty level — contextualized in an everyday scenario for math, or structured as the exercise type that best exposes the specific point being tested for languages (multiple choice, fill-in-the-blank, error correction, translation).

Then comes the part that separates Amauta from a practice app: you don't just submit an answer — you explain, in your own words, why you chose it. That written reasoning is the actual input to the diagnosis. If you're wrong, the AI doesn't say "incorrect." It names the precise mental model that produced your answer: "you added denominators as if they were separate numbers," or "you used が for the subject the way Spanish marks one," or "you calqued the Spanish possessive structure for age." You get a short, warm explanation of why your mind went there (it is almost always a very reasonable trap), a micro-lesson attacking only that misconception with a minimal contrasting pair, and a Socratic bridging question designed to let you reach the correction yourself rather than being handed it.

The next exercise is a targeted counter-strike. Having just revealed a specific misconception, the following exercise is deliberately engineered so that if the misconception is still there, it surfaces clearly and gets corrected. This repeats until the error is genuinely beaten, not merely marked.

Meta-diagnosis: Amauta also looks backward. It reads your entire practice history — every attempt, across every topic and both domains — and searches for the pattern connecting mistakes that appear unrelated on the surface. In testing: a learner mishandling operation order in both linear equations and percentage problems (one root error, two disguises); another whose English and Japanese mistakes both traced back to forcing native sentence structure onto the target language. No single exercise can reveal a pattern like that. Only the trajectory can.

Progress lives in the khipu — a rendered visualization of knotted cords, one per concept, colored by mastery, spanning math and language topics in a single unified map. Alongside it, a practice streak counts every exercise completed, correct or not, because the behavior worth rewarding is engaging with your mistakes, not avoiding them. And if you start as a guest and later create an account, everything comes with you automatically — khipu, streak, full history — because under the hood your guest identity becomes your account identity rather than being discarded and rebuilt.

How we built it

Frontend and hosting. Next.js 14 with the App Router, strict TypeScript, Tailwind, deployed on Vercel. The interface is fully bilingual (Spanish / English) — and not only the static UI text: AI-generated exercises and diagnoses switch language too, because the prompts take target language as a first-class parameter.

The AI layer — three distinct jobs, one provider (Groq). We deliberately treat generate an exercise, diagnose a reasoning, and find a pattern across history as three purpose-built prompts rather than one general-purpose prompt reused three times.

  • Math generation and reasoning diagnosis run on llama-3.3-70b-versatile — fast and more than accurate enough.
  • Language-exercise generation runs on openai/gpt-oss-120b, a decision that came directly out of the debugging described below, once it was clear that linguistic precision needed a stronger reasoning model and raw speed couldn't substitute for it.
  • Every call uses JSON mode so structured output is guaranteed to parse, and every prompt embeds explicit self-verification: the model must check its own generated exercise against a checklist — does the answer actually fit grammatically? does the question leak its own solution? — before returning it.

Backend — Appwrite for authentication and data, chosen (and, as it turned out, re-chosen mid-project) specifically because its session model solves guest-to-account continuity elegantly. A guest receives an anonymous session: a real Appwrite account, simply without an email attached. When that guest registers, we don't create a second account and copy data across — we upgrade the existing anonymous account in place, so it keeps the same internal ID and therefore every document already bound to it. The one scenario that genuinely requires moving data between two different accounts — a guest with existing progress logging into a separate pre-existing account — is handled by a dedicated server route that verifies a signed JWT proving the caller's identity before it is permitted to reassign anything, using an admin-privileged client kept strictly server-side.

Database design. Three collections — students, attempts, and a mastery map — using document-level permissions rather than a blanket open policy: each document is created with read and write access scoped to precisely the user who owns it, guest or registered alike. Appwrite has no composite primary keys, so the mastery map derives its document ID from a small deterministic hash of (student_id, concept) to obtain the same uniqueness guarantee. The full schema — collections, attributes, indexes, permissions — was provisioned by a one-off Node script calling Appwrite's REST API directly, which doubles as executable documentation of exactly what the data model requires.

Challenges we ran into

We migrated the entire backend mid-build. The project began on Supabase. Partway through, its free-tier limits ran out; rather than stall, we rebuilt authentication and the database on Appwrite from scratch — a different session model, a different permission system, and no built-in auth-state-change event stream, which meant hand-rolling that reactivity in the frontend. We treated the forced rewrite as an opportunity to improve the design rather than port it one-to-one, which is how we arrived at the anonymous-session-upgrade pattern above: cleaner than the migration-table approach the Supabase version had required for the common case.

The language exercises were badly wrong at first — and not in ways that failed loudly. Early generations looked plausible at a glance and broke the entire pedagogical premise on inspection. Japanese exercises came back in romaji instead of native kana and kanji. The correct particle was sometimes embedded inside the very sentence shown to the student, making the exercise answerable by copying rather than reasoning. Worst of all, one generated exercise presented "I have 20 years old" as the correct English answer — actively teaching the Spanish calque the feature exists to correct.

None of this surfaced as an API error or malformed JSON. Every one of those outputs was syntactically perfect. We found them only by manually inspecting generated content against real pedagogical intent. Two fixes followed: rewriting the prompts with explicit "do not leak the answer" and "verify grammatical correctness before responding" instructions plus concrete few-shot examples of the exact output shape required, and — because the faster model kept failing that check even with better prompting — routing language generation specifically to the stronger reasoning model.

That stronger model then introduced a different failure. Reasoning models spend tokens thinking before emitting final JSON, so a token budget sized correctly for the fast model left the reasoning model running out of room mid-thought and returning incomplete, unparseable output. The fix was to size token budgets per task — generous for reasoning-heavy language generation, tight for fast math and diagnosis calls — instead of assuming a single number would serve everywhere.

One smaller but instructive issue: our development sandbox couldn't reach Google Fonts, which silently broke every production build relying on next/font/google. The fix — moving to system font stacks — was a useful reminder not to lean on an external dependency the design doesn't structurally need.

Accomplishments that we're proud of

The meta-diagnosis. Most practice tools grade the exercise in front of you and stop. Amauta also looks backward across your whole history and surfaces the root cause tying together mistakes that don't look related — something no single well-diagnosed exercise could ever reveal on its own.

We caught the pedagogical bugs before shipping, not after. An exercise that teaches an incorrect form as correct is strictly worse than no exercise at all, and our testing was built to hunt for exactly that failure mode rather than merely confirming that responses parsed.

One khipu, two domains. Math and languages aren't two features bolted together side by side — they are the same mastery map and the same diagnostic engine, because they rest on the same claim about how errors work in a learner's mind.

Guest-first without sacrificing permanence. The full product is usable with zero signup friction, and nothing already built is discarded if the learner later decides to create an account.

What we learned

Structured-output prompting is not one-size-fits-all across model families. A fast non-reasoning model and a reasoning model need different token budgets and different degrees of explicit self-checking to reach comparable reliability. Treating them interchangeably produced several of our worst bugs.

Schema validity and pedagogical correctness are entirely different guarantees. Confirming that AI-generated content parses as valid JSON says nothing about whether it is true, fair, or safe to teach. We had to write real test cases — deliberately wrong answers paired with realistic reasoning — to catch content that was structurally flawless and educationally backwards.

The most effective fix for "the model keeps getting X wrong" is usually one well-chosen worked example of the output you actually want, not three more paragraphs of prose instruction. Few-shot examples outperformed long rule lists every time we compared them.

An identity that begins anonymous and can later become permanent is a fundamentally better shape than a throwaway "guest mode" migrated into a real account after the fact.

What's next for Amauta

More languages. Chinese and Korean are the natural next additions — the architecture already treats a target language as a configuration entry with its own linguistic profile, not something requiring changes to the core engine.

A proactive meta-diagnosis. Today the pattern analysis runs on request. Next it should surface automatically once there is enough signal, routing the learner straight back into practicing their single weakest concept instead of waiting for them to think to ask.

Spaced repetition, driven by the mastery map Amauta already maintains, so weak concepts resurface on a schedule rather than only when the learner happens to choose that topic again.

A teacher-facing view: the same diagnostic engine aggregated across a classroom, so a teacher sees which specific misconception is spreading — not just which students scored low.

Built With

Share this project:

Updates

Submission history