Course Matrix | Kevin Lan

course matrix banner

Overview

Course Matrix is an intelligent course timetable builder that generates an optimal timetable for you based on a specific set of restrictions. It also leverages an AI agent that easily translates complex requirements into personalized timetables. course matrix timetable I built out the AI infrastructure behind this project. It uses the following tools:

  • Pinecone to store vectorized indices of course info and offering info (semester, timeslot, remote/in-person etc)
  • LangChain to orchestrate the RAG pipeline
  • Vercel AI SDK to handle text streaming
  • assistant-ui to bring a chat/thread based UX to the app and to store conversations

ChatGPT-like UX

I added a lot of functionality to the client-side experience when interacting with the chatbot. The app supports multiple conversations, thread branching, message edit + resend, message copying, and more. Conversation threads are persisted to assistant-ui cloud. course matrix chat interface

Agent Pipeline

A major goal of ours was to provide an AI agent that balanced speed and response quality. I tested various approaches and ended up with a dual-mode chat flow, with one mode being question-and-answer-based (e.g., “What 3rd year math courses can I take in Winter 2026 that satisfy my degree requirements?”) and the other being action-based (e.g., “Generate me a timetable that follows criteria X, Y, Z.”). These are the pipeline steps:

  1. Determine Mode: Mode is determined by the query prefix. In the UI, the user is prompted to use the command /timetable to trigger the agentic flow in which the system uses tool calling to perform a task. Otherwise, the flow defaults to Q&A. course matrix chat 2
  2. Q&A mode:
  • Reformulate Query: I found that if the user’s raw query were used as the search prompt within our vector DB, the search results often lacked full context. In a multi-message conversation, previous messages add context to the most recent message.

  • Filter by namespace: Our vector indices are sorted into namespaces on Pinecone, which are essentially buckets for similar documents. We have namespaces for courses, offerings, programs, prerequisites, corequisites, and departments. This allows us to speed up vector searches by narrowing our search to the most relevant group of documents.

  • Build response: Given the last few messages in the conversation history and the retrieval results, another call is made to the LLM to write a response.

  1. Agentic mode: In this flow, tool calls are made by the agent. Results are returned and the agent can determine whether to make another tool call or return a response. This is implemented using Vercel’s AI SDK.
generateTimetable: tool({
    description:
        "Return a list of possible timetables based on provided courses and restrictions.",
    parameters: TimetableFormSchema,
    execute: async (args) => {
        console.log("restrictions :", JSON.stringify(args.restrictions));
        const data = await availableFunctions.generateTimetable(
          args,
          req,
        );
        console.log("Generated timetable: ", data);
        return data;
    },
}),
  1. Stream response: The AI SDK streams response data using Server-Sent Events (SSE), which sends data in chunks to avoid a long wait time. The response is rendered with full markdown capabilities as well as in-app hyperlinks.
const result = streamText({
    model: openai("gpt-4o-mini"),
    system: `System prompt ...`,
    messages,
    tools: {...},
    maxSteps: CHATBOT_TOOL_CALL_MAX_STEPS, 
})
result.pipeDataStreamToResponse(res);

course matrix chat 1

Data Extraction

Course data is scraped from the school’s course search site. Offering data is scraped from the school’s timetable archive, which is downloadable as PDFs. I wrote a script that extracts data from these sources using regex matching and PDF tooling in Python, namely pdfplumber.

Timetable Generation

This uses a backtracking algorithm to output the top 10 timetables consisting of all selected course sessions and meeting all time restrictions. Restrictions include blocking off certain times, setting a maximum number of days per week, and more. Results are shuffled for better randomness (variation).

course matrix course list

Other contributions I made

  • Email notifications: Used a cron job and the Brevo email API to send opt-in notifications when a class is starting soon
  • Timetable Diffing (comparison): Share and compare timetables with friends to align schedules side by side