TheoGrader
An AI-powered assessment system that processes handwritten university examination scripts, evaluates answers against lecturer-defined marking schemes, and flags uncertain results for human review.
- Category
- AI/ML
- Timeline
- Q2 2026
- Stack
- Next.js 15ReactTypeScriptPythonFastAPI+10 more
Overview — read the full write-up
TheoGrader is an intelligent assessment system developed as a final-year project to support the grading of handwritten theoretical examination scripts in Nigerian universities. It combines a Next.js application for exam management, rubric creation, script processing, and result review with a dedicated Python FastAPI service for AI-assisted grading. Using vision-based transcription, rubric-aware answer segmentation, and embedding similarity, the system generates question-level scores and concept breakdowns against a lecturer's marking scheme. It also handles exam-specific answer-selection rules, identifies results that fall below configurable confidence thresholds, and allows lecturers to inspect and override marks before approving results. The system is deployed end to end, demonstrating how AI-assisted assessment can be integrated into a complete, reviewable academic workflow.
The product in action
Screenshot 1 of 8

The story in three parts
01 — The Challenge
Automating Assessment Without Losing Academic Oversight
Read moreShow less
02 — The Strategy
Separating AI Processing From Application Control
Read moreShow less
03 — The Impact
A Deployed, End-to-End Assessment Workflow
Read moreShow less
System Architecture
From Handwritten Scripts to Reviewable Scores
Ctrl + scroll to zoom
Application Orchestration
The Next.js application manages authenticated lecturer workflows, exams, rubrics, uploaded scripts, grading requests, result persistence, notifications, and reports. Keeping database writes in the application layer gives the system a central point for managing grading state and review decisions.
AI Processing Service
The FastAPI service separates rubric extraction, vision OCR, answer segmentation, and embedding-based scoring into distinct processing stages. The web application can reuse stored OCR text during re-grading, avoiding unnecessary repeated transcription when the existing extraction remains usable.
Direct-to-Storage Uploads
Scanned PDFs and images are uploaded from the browser to Supabase Storage through signed upload URLs. The application coordinates upload confirmation and server-side processing, reducing the amount of large-file traffic passing through Vercel serverless functions.
Reviewable and Retry-Safe Results
Grading results include question-level breakdowns and configurable confidence checks. Re-grading replaces the previous result within a database transaction, while embedding-provider failures abort before a new result is persisted. This helps prevent duplicate results and distinguishes processing failures from genuine assessment scores.
Lessons & trade-offs
Cache Expensive, Variable Processing Steps
Re-running vision OCR during grading retries could produce a different transcript for the same script, which could then change the resulting score. I addressed this by persisting extracted text and reusing it during subsequent grading attempts unless the extraction is unavailable or has been flagged for reprocessing. This reduces repeated AI work and improves consistency between grading attempts, although it means an existing transcript will not automatically benefit from a better OCR result.
A Processing Failure Must Not Become an Assessment Score
I identified a failure mode in which an unavailable embedding provider could produce unusable vectors that, if treated as valid similarity inputs, could lead to misleading zero scores. The service now returns an error when embedding generation fails, and the application aborts result persistence for that attempt. This preserves the distinction between an unanswered question that legitimately receives zero and a script that could not be graded successfully.
Make Ambiguous Inputs More Structured
Students do not always label answers consistently. Variations such as '1(a)', 'Q2b', and 'Answer 3' make unconstrained segmentation unreliable. I addressed this by passing canonical question labels from the rubric into the segmentation process. I also implemented matriculation-number normalization for common OCR character confusions, with a raw-text fallback that allows the lecturer to correct uncertain values rather than losing the extracted information.
Serverless Scaling Requires Deliberate Connection Management
Each serverless function instance can create its own database connections, so concurrent invocations may place pressure on a shared PostgreSQL connection pool. I bounded Prisma's connection pool per function and relied on the pooler and Prisma query handling to manage access within that limit. This made database connection usage more predictable while introducing a trade-off: requests may wait for a connection when concurrency increases.
Want something like this built?
TheoGrader is one example of how I work — from architecture to shipped product. If you have a problem that needs this kind of thinking, let’s talk.

