Skip to main content
Prakriti Bista

AI/ML Systems

SEAS Search: GenAI Academic Advisor

Built a Knowledge Graph-RAG advising system for course planning. Fine-tuned Llama 3.1 with LoRA and made prerequisite reasoning visible in the interface.

SEAS Search: GenAI Academic Advisor

Project thesis

A graph-first advising system where the interface exposes the reasoning path instead of asking students to trust a black box.

Question

Course planning needed graph reasoning, not another confident chatbot.

Decision

GPU limits and advising risk pushed the interface toward visible reasoning and advisor checks.

Every SEAS student hits this wall

This was the final project for CSCI 6366, Neural Networks and Deep Learning at GWU's School of Engineering and Applied Science. A team project with my classmate Anurag Dhungana. We had a semester to build something real using what we'd learned, and we wanted to pick a problem we actually felt.

The problem we picked was one every SEAS student has hit: trying to figure out which courses you need before you can take the one you actually want. The official GWU systems are fragmented, the course bulletin is a PDF, the schedule is a separate portal, and prerequisite chains require you to manually trace through multiple pages. We wanted to build something that could answer the question naturally: "What do I need to take before CSCI 6364?"

Why fine-tuning alone invented prerequisites

The obvious first move was fine-tuning a language model on course data, and we did that. It handled simple questions, but it broke down on anything that required tracing a chain. A fine-tuned model doesn't reason over structured relationships so much as pattern-match on text that looks similar, so it would give you a confident wrong answer about prerequisites that didn't exist.

Course relationships are a graph problem wearing a text costume.

Prerequisites form a directed acyclic graph. If you want to answer multi-hop questions correctly, you need something that can walk that structure, not just retrieve text that sounds nearby. That's what pushed us toward building a knowledge graph.

System architecture

Build time · Python notebook

scraped

GWU schedule

my.gwu.edu · Spring 2026 · 586 rows

scraped

Course bulletin

CSCI + DATS · 187 courses

NetworkX DiGraph

Knowledge Graph

Nodes: course · professor · topic — Edges: prerequisite · taught_by · covers_topic (489 nodes / 566 links)

Query time · GraphRetriever

Student question

“What do I need before CSCI 6364?”

serialized subgraph

Structured context

The resolved prerequisite chain, as text

Unsloth · LoRA

Llama 3.1 8B (4-bit)

Fine-tuned on 154 graph-grounded multi-hop examples; r=32

answer

Grounded natural-language reply

The separate force-graph explorer lets you trace the path yourself

The graph gets built once, offline. At query time it's traversed so the model is handed a real prerequisite chain instead of guessing one. The live site just reads exported JSON; its chat box proxied to a HuggingFace Space and is currently switched off for lack of hosted inference.

A knowledge graph that does the reasoning first

We scraped and structured data from two sources: the GWU CSCI and DATS course bulletin (187 courses) and the Spring 2026 course schedule (586 instances). From that, we built a knowledge graph in NetworkX where nodes are courses and edges are prerequisite relationships. spaCy handled the entity extraction from the bulletin text.

The graph became the backbone for our QA system. At query time, instead of handing the question straight to the model, we first traversed the graph to resolve prerequisite chains, then passed that context to the language model. The model's job changed from "figure out what the prerequisites are" to "explain the prerequisites I've already found."

Knowledge graph visualization
The interactive force-directed graph (react-force-graph): students can trace prerequisite paths themselves, making the system's reasoning visible and checkable

Fine-tuning with LoRA

For the language model, we fine-tuned Llama 3.1 8B using LoRA adapters via Unsloth. We generated 2,828 training Q&A pairs: a mix of simple factual questions and complex multi-hop questions. LoRA meant we weren't fine-tuning all 8 billion parameters. We were adapting a small set of low-rank matrices, which made the whole thing feasible on academic resources. The graph-grounded run we trained later used only a small fraction of those pairs and finished in about 4 minutes, against 108 for our longest standard run, although a faster GPU accounts for much of that gap.

Four attempts, four lessons

We didn't get to the Knowledge Graph approach immediately. We went through four approaches across the semester.

The first two were standard fine-tunes without graph augmentation, a baseline run and a longer optimized run. These handled factual lookups reasonably well but failed on relational reasoning. On a test set of simple factual questions they scored 26% and 38% respectively.

The third approach introduced the Knowledge Graph as a retrieval mechanism but didn't train the model with graph-augmented examples. Its answers were inconsistent, and we never recorded a comparable score for it.

The fourth approach trained the model on graph-grounded examples so it would learn to use the retrieved context. It scored around 34%, but on a harder set of multi-hop questions, and it got there on a fraction of the training pairs the other runs used. Because the standard runs and the graph-grounded run were graded on different test sets, these numbers aren't a like-for-like comparison, and I can't claim from them that the graph beat the standard runs. A later evaluation file in the repo, which scored only those two runs, actually gives the graph-grounded run a lower F1 (0.17) than the optimized standard run (0.47).

So the case for the graph rests on what happens at answer time, where the model is handed an actual prerequisite chain instead of being left to invent one. We never measured hallucination directly, though, so that is a design argument rather than a benchmark result.

Designing for a system that's sometimes wrong

Building the frontend raised a design problem the model work didn't: how do you build an interface for a system that's partially correct, where the stakes of being wrong are real? A student asking about prerequisites might use the answer to decide what to register for.

The choice to use a chat interface rather than a search box was deliberate. Chat implies reasoning rather than fact retrieval, which is more honest for this system. More practically, chat lets students ask in the language they already use. "What do I need before 6364?" is a question a student would ask an advisor. Forcing that into a structured query form adds work for no real benefit.

I designed the answer card to treat graph traversal queries differently from simple factual lookups. A factual lookup would get a compact answer, while a traversal query would show which part of the graph was traversed and what relationships were resolved before the model generated its answer, which conveys model confidence through design without asking a student to interpret a probability score. For anything touching graduation requirements or prerequisite chains, the card was also meant to carry a prompt to confirm with an academic advisor, as prominent as the answer itself rather than tucked at the bottom.

Neither of those made it into the chat we shipped, and the live chat is currently switched off because we don't have the resources to host inference, so what you can try today is the standalone knowledge graph explorer.

What the graph changed, and what we couldn't measure

Every run started from the same Llama 3.1 8B base. What the graph changed was the context the model was handed at answer time: a real prerequisite chain instead of nothing.

This project was my first time working with knowledge graphs seriously, and the thing that stuck with me was how much the data representation matters. The graph-grounded model trained on far fewer examples than the others, and structuring the knowledge as a traversable graph meant it could be handed a real prerequisite chain at answer time. We never measured hallucination directly, though, and our scores don't prove the graph version was more accurate. If I ran this again, I'd test every approach on the same held-out multi-hop questions and count invented prerequisites as their own metric.

That insight carried into the interface. Because the data was structured as a graph, the interface could show graph-level concepts to users. The interactive visualization was only possible because the underlying data had that shape. If course relationships had been stored as unstructured text or a flat table, there would have been nothing useful to show.

Data architecture and UX are not separate decisions. How you structure your data determines what your interface can show.

What worked

  • Graph-grounded training reached 34% on harder multi-hop questions, although that test set differs from the one behind the 26% and 38% standard runs, so the numbers are not a clean comparison
  • LoRA adapters via Unsloth meant adapting a small set of low-rank matrices instead of all 8 billion parameters, which kept every run feasible on academic resources
  • An interactive force-directed graph (react-force-graph) lets students trace prerequisite paths themselves, making the system's reasoning checkable rather than a black box
  • Advisor confirmation prompt is as visible as the answer, because course planning has stakes

What didn't

  • 34% on the multi-hop questions is meaningful but still leaves most hard questions unanswered, so it is not ready to replace an advisor.
  • Live chat inference requires GPU resources we can't host for free, so the chat demo is currently disabled.
  • Edge cases in complex prerequisite chains may not be covered by our generated training data

Built with

Python · Llama 3.1 · NetworkX · RAG · Vercel · LoRA · Unsloth · Next.js