Mayowa

ayowa

All projects
AI-Powered Examination Assessment SystemQ2 2026

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.

Screenshots

The product in action

Screenshot 1 of 8

TheoGrader screenshot 1
Case study

The story in three parts

01 — The Challenge

Automating Assessment Without Losing Academic Oversight

Grading theoretical examination scripts is a demanding process for university lecturers. Answers are handwritten, students express the same concepts in different ways, and large batches of scripts must be assessed against a consistent marking scheme. The process becomes more complicated when examination instructions specify which questions students must answer and which optional answers should contribute to their final scores. Building an automated solution introduces additional challenges. Handwriting recognition can make mistakes, answer boundaries may be ambiguous, and semantic similarity alone cannot establish that an answer deserves full marks. A system that produces scores without explaining how they were derived would leave lecturers with little basis for reviewing questionable results. My goal was to build a complete assessment workflow that could process scanned scripts, associate answers with the correct rubric questions, calculate rubric-based scores, and make the reasoning behind those scores inspectable. Human oversight was a core requirement: lecturers needed to review flagged results, adjust marks when necessary, and retain responsibility for the final assessment.
Read more

02 — The Strategy

Separating AI Processing From Application Control

I built the application as two cooperating services with clearly defined responsibilities. The Next.js application handles authentication, exam and rubric management, file uploads, grading orchestration, persistence, result review, and reporting. A separate Python FastAPI service handles the AI operations, including rubric extraction, vision-based OCR, answer segmentation, and embedding-based scoring. This separation keeps application state and database writes under the control of the web application while allowing the AI pipeline to evolve independently. For document handling, I implemented signed, direct-to-Supabase Storage uploads. The browser uploads scanned PDFs or images directly to storage instead of sending the entire file through a Vercel serverless function. The application then coordinates file confirmation, processing, and result persistence. This approach accommodates larger documents while avoiding unnecessary transfer through the web application's request handlers. The grading pipeline uses GPT-4o-mini Vision to transcribe scanned examination pages and text-embedding-3-small to generate embeddings for student answers and individual rubric points. Cosine similarity provides a scoring signal, with configured similarity bands and interpolation used to calculate partial credit. The resulting breakdown records matched, partially matched, and missing rubric concepts so lecturers can inspect how each question was scored. Similarity is an approximation of semantic alignment, not a guarantee of correctness, which is why review and score overrides remain part of the workflow. I also implemented rubric-aware answer segmentation using canonical question labels, rather than relying entirely on the model to infer answer boundaries. Exam instructions are converted into explicit selection rules, supporting scenarios such as answering any three questions out of five. The application applies the configured selection strategy when calculating totals and preserves excluded questions in the results for traceability. Finally, I addressed consistency and failure handling. Previously extracted OCR text is reused during re-grading unless it is unavailable or marked for reprocessing. Low-confidence results can be flagged for review, and grading writes replace previous results within a database transaction. If embedding generation fails, processing aborts before a new result is written, preventing a provider failure from being recorded as a genuine zero score.
Read more

03 — The Impact

A Deployed, End-to-End Assessment Workflow

The completed application supports the assessment workflow from exam setup to report generation. Lecturers can create exams, define rubrics manually or extract them from marking scheme documents, upload scanned scripts, run the grading pipeline, inspect question-level results, override marks, review flagged scripts, and export PDF reports. Exam-specific selection logic handles compulsory and optional questions so that only the answers permitted by the configured examination rules contribute to the total. Individual question results remain available for inspection, including answers excluded from the final score. The application also provides authentication, exam and script status tracking, dashboard statistics, notifications, and activity logging around the grading workflow. I deployed the Next.js application on Vercel, the FastAPI AI service as a Docker-based deployment on Render, and the PostgreSQL database and file storage on Supabase. The live application demonstrates the integration of a web platform, document-processing pipeline, external AI services, and persistent assessment data in one working system. The project demonstrates a complete implementation rather than an isolated AI experiment. Its evaluation still needs to be distinguished from deployment completeness: the fact that the workflow runs end to end does not, by itself, establish grading accuracy across a broad range of handwriting styles, subjects, or examination conditions.
Read more
Architecture

System Architecture

From Handwritten Scripts to Reviewable Scores

TheoGrader uses three main infrastructure components: a Next.js application deployed on Vercel, a Python FastAPI AI service deployed on Render, and Supabase for PostgreSQL and file storage. The web application owns authentication, orchestration, application state, and database persistence. The AI service handles rubric extraction, vision-based OCR, rubric-aware segmentation, and embedding similarity scoring. OpenAI provides the vision and embedding models. Results pass through the configured examination answer-selection strategy and confidence checks before the application records their status and makes them available for lecturer review.

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.

Takeaways

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.

Start a project

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.