ProjectOctober 17, 2025Final year project / Part 1

Building an AI grading system for my final year project

How I built a grading workflow around OCR, RAG, and LLMs to make subjective exam evaluation faster and more consistent.

final-year-projectairagnextjsfastapi
Back to blog

I wanted my final year project to solve a real problem, not just demo a trendy model. Grading written exam answers felt like the right place to start. It takes time, it gets tiring, and once the pile of papers grows, consistency becomes harder to maintain. That was the starting point for AI Grading System, a project I built to see whether an LLM could help grade both multiple choice and subjective answers without drifting away from the actual course material.

The main idea was simple: do not let the model grade from vague prior knowledge when the reference answers, notes, and rubrics already exist. I built the system around retrieval-augmented generation so the grading step could pull from the right academic context first, then generate a score and feedback from that grounded material.

AI Grading System project logo
AI Grading System Logo.

The problem I wanted to fix

Manual grading has a few obvious problems. It is slow, especially for subjective answers. It also becomes inconsistent when the evaluator is tired, rushing, or switching between many students with slightly different writing styles. In large classes, the delay is just as painful as the workload because feedback arrives after the moment when it would have been most useful.

I did not start this project thinking AI should replace teachers. I started it thinking AI could handle the repetitive part, surface relevant reference material, and give instructors a faster first pass that they could still verify before publishing results.

What the system actually does

At a high level, the workflow looks like this:

  1. A teacher uploads exam material and reference documents.
  2. The system extracts text from PDFs, including scanned pages.
  3. Reference material is chunked, embedded, and stored in a vector database.
  4. When a student answer needs grading, the system retrieves the most relevant context.
  5. The grading layer evaluates the answer against that retrieved context and returns a score with feedback.
  6. The instructor reviews the result instead of grading everything from scratch.

That flow let me support both objective and subjective questions in the same project. MCQs could be checked with more direct matching logic, while longer answers used the retrieval and LLM pipeline. Under the hood, those two paths shared the same ingestion and extraction stages, then split when it was time to score.

Image placeholder: end-to-end workflow diagram showing upload -> OCR -> retrieval -> grading -> instructor review

Why I used RAG instead of a plain LLM call

A plain LLM prompt is easy to set up, but it is also where the project would have fallen apart. If the model answers from general knowledge, it can sound confident while missing what was actually taught in the course. That is a bad fit for grading.

Building the retrieval layer

RAG gave me a way to anchor each grading decision to the source material. I stored answer keys, notes, and related references, then retrieved the most relevant chunks at grading time. The reference documents were partitioned into logical sections, split into smaller chunks with RecursiveCharacterTextSplitter, converted into embeddings with the Nomic embedding model, and stored in ChromaDB so the index could persist between sessions.

At retrieval time, I did not want the system to depend on one brittle search phrase. In the report, I explored multi-query retrieval, where the original query is expanded into a few paraphrased forms, each variant runs through the retriever, and the results are merged and reranked. I used MMR-based search to keep the retrieved context relevant without returning five nearly identical chunks.

That mattered because students do not all write the same way. Two good answers can use different wording, different order, and different levels of detail. Retrieval helped the system compare those answers against the intended material rather than just matching surface-level phrases.

Reference diagram showing RAG query construction, routing, indexing, retrieval, and generation stages

A RAG reference diagram that lines up with the retrieval-heavy direction I took in the project.

The stack I used

I built the backend with FastAPI because the project needed a clean Python layer for document processing, orchestration, and API endpoints. The frontend used Next.js to give the system a proper web interface for uploads, dashboards, and result review. PostgreSQL handled structured application data, and ChromaDB stored vector embeddings for retrieval.

The AI side pulled together OCR, document chunking, embeddings, vector search, and LLM-based evaluation. I also used LangChain to help coordinate the retrieval pipeline and grading flow. On the application side, the project was split into a few practical modules: user management for authentication, assignment upload for file intake, an AI grading module for evaluation and feedback, result management for reports, and the frontend layer for dashboards and state handling.

This was one of the parts I enjoyed most. The project was not just "call model, get answer." It was a full application with authentication, file handling, storage, retrieval, grading logic, and a UI that made the workflow usable for actual people.

Architecture overview of the AI Grading System showing frontend modules, FastAPI services, OCR pipeline, RAG pipeline, and data layer

The system architecture: Next.js on the frontend, FastAPI in the backend, plus OCR, grading orchestration, and retrieval services.

A closer look at the pipeline

The part I cared about most was the path from raw PDF to grade. It sounds neat in a diagram. In practice, it is a chain of small decisions where one weak link can wreck the output.

PDF extraction and OCR

The document pipeline started with PDF loading through PyMuPDF. For scanned files, pages were converted to images and passed through an OCR-style extraction path. If that failed or the scan quality was poor, the system could fall back to text-oriented extraction routes such as PyPDF2 or layout-preserving extraction through PyMuPDF. That fallback logic mattered because student submissions are not clean benchmark data. They are whatever the scanner, camera, or photocopier decided to give you that day.

OCR detection output marking handwritten text, equations, and diagram regions on a student answer sheet

A detection example from the OCR stage, where the system separates text, equations, and diagram regions before grading.

Chunking and retrieval

Once the text was extracted, the reference documents moved into the retrieval side of the system. The text was partitioned, chunked, embedded, and written to ChromaDB. At grading time, the retriever pulled back the most relevant chunks with metadata, and that retrieved context was inserted into the grading prompt instead of asking the model to improvise.

How the grading logic worked

I did not treat every question type the same. That would have been lazy, and it would have made the system worse.

MCQ path

For MCQs, the grading path was more direct. The system looked for recognizable option patterns, parsed questions and responses with regex-based extraction, compared the detected answers against the key, and aggregated the score. If the answer key structure was incomplete, the report mentions a fallback where the LLM could help infer the likely match, but the general idea was still deterministic scoring first.

Subjective-answer path

For subjective answers, the system needed a different approach. The prompt was built around criteria like accuracy, completeness, clarity, and relevance. The model then graded the answer against the retrieved reference context, produced feedback, and normalized the raw result into a 0 to 100 scale. That grading path was less about string matching and more about whether the answer covered the right ideas in a way that matched the course material.

One detail I liked in the report was the instructor verification step. The system was never meant to be a mysterious black box that spits out final marks no one can question. The output had to stay reviewable.

The harder parts

The messy part of a project like this starts before grading. It starts with inputs. Exam papers come as PDFs, scanned pages, inconsistent layouts, and handwriting that is not always friendly to OCR. The quality of extraction directly affects the quality of grading, so OCR accuracy became one of the biggest practical constraints in the system.

Another challenge was keeping subjective grading reasonable. It is easy to produce feedback that sounds polished. It is harder to produce feedback that is fair, relevant, and tied to the expected answer. The model can sound convincing while being slightly off, which is exactly the kind of mistake that becomes unacceptable in grading. That is why grounding mattered so much, and why I treated instructor verification as part of the workflow instead of an optional extra.

There were also the usual tradeoffs that come with building around external AI APIs: cost, response time, and availability. Those tradeoffs are acceptable in a research prototype, but they need more careful planning in a production system.

What I ended up with

By the end of the project, I had a working prototype that automated the grading flow from document intake to result generation. According to the report validation, the system reached about 85 percent alignment with manual human grading, with an average grading response time of 7.2 seconds per request.

That number is not a claim that the problem is solved forever. It does show that the approach is useful. For a final year project, that felt like the right outcome: not a flashy promise, but a system that could already reduce repetitive work and make grading more consistent when used with human oversight.

What I would improve next

If I keep pushing this project forward, the first area I would revisit is OCR and document understanding. Better layout analysis and stronger handling for low-quality scans would improve everything downstream. I would also like to adapt the grading layer more aggressively to domain-specific material so the system gets better at subject vocabulary, expected reasoning patterns, and rubric interpretation.

Another obvious next step is broader classroom testing. A prototype can behave well in controlled conditions and still struggle once you throw real variation at it. I would want more subjects, more answer styles, and more instructor feedback before treating it as a dependable grading assistant.

Final thoughts

This project taught me that the interesting part of AI products is rarely the model alone. The hard part is building the system around it so the output is grounded, reviewable, and useful in the setting where people actually work.

AI Grading System started as a final year project, but it ended up being a practical lesson in product design, retrieval systems, and the gap between a smart demo and a tool someone could trust. That gap is where most of the work lives. It is also where I learned the most.