Project Story
Inspiration
Two numbers started this project. Teachers spend an average of 9.9 hours a week grading — more than a full extra workday — and 95% of them take that work home. It's no surprise 62% call grading one of the worst parts of the job, while 75% say they'd welcome an AI tool that could cut that load in half (Learnosity, 2025).
At the same time, AI has made it trivially easy for students to get a finished answer without doing the thinking behind it — a shortcut that quietly erodes real understanding. But the research on the other side of that problem is encouraging: students guided through problems via questioning, rather than handed direct answers, report stronger ownership over their own learning, and prior exposure to that kind of guidance leads to deeper engagement even after the AI is gone. The evidence isn't unanimous — dialogue alone doesn't guarantee better test scores — but the direction is clear enough to build on.
That's the gap we set out to close: give teachers their time back, and give students a tutor that builds understanding that actually lasts, instead of one that just hands them the answer.
What it does
The platform ingests a course's real source material — lecture PDFs and DOCX files — and builds a knowledge graph over it. Two agent pipelines run on top of that same grounded graph:
Exam Mode (for teachers) A plain-language request plus the course documents produces an MCQ or essay exam, difficulty-balanced across the set (40% easy / 40% medium / 20% hard), with every question traceable back to its source. Each MCQ ships with four choices, the correct option, an explanation, and a difficulty label. Each essay question ships with a model answer and a ten-point grading rubric.
Tutoring Mode (for students) A student submits a question they're stuck on. The tutor classifies it on Bloom's Taxonomy, and if it's a low-level recall/understanding question, it just answers — no ritual needed. For anything higher (apply, analyze, evaluate, create), it withholds the answer and starts one Bloom level below the original question, then adapts turn by turn based on whether the student's answer was right or wrong — descending, climbing, or, after three consecutive misses at one level, stepping in to teach directly rather than trapping the student in a spiral of increasingly easy questions. Once the student clears the top level, they connect their own reasoning back to the original question themselves, and only then does the tutor name the specific misconception it saw and give the full answer.
How we built it
Everything runs on the Google stack: Gemini (via Vertex AI / the Gemini API) as the only model layer, Google ADK as the top-level agent with the five domain pipelines exposed to it as tools, and LangGraph retained inside each pipeline for the cyclic, checkpointed state machines that the Socratic loop and the exam solver/judge loop both need. Infrastructure is Cloud Run for the API and the async ingestion worker, Cloud SQL for Postgres for both relational data and the LangGraph checkpointer, Cloud Storage for documents, Memorystore for Redis for caching, Qdrant Cloud for hybrid vector search, and Neo4j Aura for the knowledge graph.
The pipeline behind both modes: Docling converts uploaded PDFs/DOCX to structured Markdown so concept extraction can work against the document's own headings rather than a flat wall of text. Chunks are split on semantic boundaries and embedded with BGE-M3, which produces a dense vector and a sparse lexical vector in one pass — both stored in Qdrant, retrieved separately, fused with reciprocal rank fusion, and reranked with a cross-encoder. In parallel, Gemini Flash reads each chunk and extracts concepts and relationships against a fixed ontology, writing them to Neo4j with the source chunk attached as provenance.
From there, the two modes diverge in how they use the graph. Exam generation runs a random walk over the graph whose length is the difficulty (1–2 concepts for easy, 5–7 for hard) — so difficulty is a structural property of the reasoning path, not an adjective a model assigns itself. A generator writes a question that can't be answered by skipping any concept on that path; for MCQ, wrong options are built by taking the correct path and diverging at the last step into a real neighboring concept, so they're wrong the way a student who almost understood would be wrong. A blind solver then attempts the question with the source passages shuffled and stripped of any hint about which one was used, and a judge checks grounding first, then whether the solver's reasoning actually traversed the concept chain — a question only survives if it's both real and demanding. Failed questions get regenerated up to four times before the slot is simply dropped.
The tutor uses the same graph differently: instead of walking a path, its difficulty axis is the Bloom level, and every sub-question it asks is constrained to concepts and relationships the retrieved material actually supports — it can't invent a connection the documents never made. Session state (current level, consecutive failures per level, questions already asked, the student's own reasoning so far) is saved after every turn to the Postgres checkpointer, so the conversation survives a server restart instead of living only in memory.
Provenance note: this build adapts an existing prototype of ours — the pedagogy, agent topologies, and retrieval design are unchanged — onto the required Google stack (Gemini/Vertex AI, ADK, Google Cloud infrastructure) for this hackathon. The infrastructure swap, the ADK integration, and the HTTP/production hardening were built inside the submission window.
Challenges we ran into
The hardest problem wasn't generating exam questions — it was generating ones we could trust. A question can fail for two very different reasons: it's genuinely hard, or it's built on something the model hallucinated. Treating those the same would be the worst possible failure mode, because hallucinated questions are exactly the ones that look "too hard" to a solver. We solved this by ordering the judge's checks so grounding is verified before difficulty is ever considered — a question resting on an ungrounded claim is thrown out immediately, no matter how well-connected it looks in the graph, because the graph shows structure, not evidence.
Getting the Socratic tutor to know when to stop scaffolding was its own challenge. A naive version traps a stuck student: wrong answer → easier question → wrong again → easier still, turning a conversation meant to build confidence into an interrogation. The fix — counting consecutive failures per level and switching to direct teaching after three — mattered more than any single prompt we wrote.
On the infrastructure side, combining ADK at the top level with LangGraph subgraphs underneath
took real design work: ADK needed to treat five independently-checkpointed state machines as ordinary
tools without losing their internal cyclic behavior. Moving the checkpointer off an in-process
MemorySaver and onto Cloud SQL was necessary but non-trivial, since it's what lets tutoring sessions
survive a Cloud Run instance restart. And because BGE-M3 and the cross-encoder load into memory at
import, sizing Cloud Run correctly (memory, CPU, and avoiding cold-start model loading mid-demo) took
some tuning.
Accomplishments that we're proud of
- A difficulty model that's structural, not vibes-based: exam difficulty comes from the length of a validated reasoning path through the knowledge graph, not from asking a model to "make it harder."
- A solver/judge adversarial loop that catches both unanswerable and hallucinated questions before a student or teacher ever sees them.
- Distractors that are wrong for a real reason — generated by mutating the correct concept path at exactly one step, so they resemble an actual student misconception rather than arbitrary filler.
- A tutor that can admit its own approach isn't working and switch strategy mid-session, instead of mechanically drilling a stuck student.
- State that survives a restart: tutoring sessions resume from a Postgres checkpointer rather than re-reading the whole chat history and guessing where they left off.
What we learned
We learned that grounding has to be checked before quality in any pipeline where a model can invent content — order of operations, not just criteria, determines whether hallucinations get caught. We learned that combining a top-level agent framework (ADK) with lower-level stateful subgraphs (LangGraph) is a good split of responsibility: let the framework built for orchestration own the top, and let the framework built for cyclic state machines own the parts that actually loop. And on the retrieval side, we confirmed something we'd suspected going in: dense and sparse retrieval fail in different, complementary ways, and fusing them beats trusting either alone.
What's next for Educational AI
- Cross-session memory for the tutor. State currently survives a restart but not a new session — a student who returns tomorrow starts from zero. A persistent per-student profile so known weak spots carry over is our top priority.
- More question types. Exam mode currently supports MCQ and essay; fill-in-the-blank, matching, and other formats are next.
- Human-in-the-loop graph review. Concept extraction is the step most likely to be wrong and currently has no correction mechanism — letting lecturers see and fix the extracted graph directly would close that gap.
- Personalized thresholds. The "three consecutive failures" rule and the Bloom-classification confidence cutoff are fixed constants today; personalizing them per student based on session history is a natural next step.
- Lecture slides, mind maps, and summaries. These are grounded in the same knowledge graph and share the retrieval layer already built — extending the platform to generate them is mostly a matter of adding new pipelines on top of infrastructure that already exists.
Log in or sign up for Devpost to join the conversation.