Brown Assistant

The goal of this project is implementing a Retrieval-Augmented Generation (RAG) system that helps all students at Brown University explore the courses that are open in the semester and understand their degree requirements by combining two sources:

A FastAPI backend is implemented to handle data processing, and serve queries efficiently. In addition, a user-interface is built to provide an interactive interface for users.

Note: Currently, the concentration section is specific to undergraduate programs, and the course-related information can be taken only for the Fall 2025 semester. But this can be easily extended to other programs and semesters.

Demo

End-to-End Workflow

1) Data Acquisition

2) Indexing and Vectorization

3) Retrieval and Generation

4) Serving

In addition,

Models Used

1) Bi-Encoder Embeddings

2) Vector Database for Indexing and Retrieval

3) Cross-Encoder Reranking for Retrieval

4) Generator

Observations

Currently, the pipeline performs well for most questions. Wrong answers usually occur when the question is too short or when it contains only technical words (e.g., APMA 2230, CSCI 0320, ECON 2950, etc.) without context. Because retrieval runs over the entire collection (Bulletin + CAB), very short or vocabulary-overlapping queries can pull in chunks from unintended departments. To solve the issue of poor performance with technical words, I tried to integrate a sparse retriever, BM25, along with the dense retriever, retrieved and then re-ranked chunks with a Cross Encoder, but this did not help much. Therefore, I removed it. The other option was integrating components to the user-interface that allows metadata pre filtering by department or concentration, but I wanted to make things as simple as possible for the users. Therefore, I did not do this.

In addition, retrieval + generation process usually takes between 1-5 seconds. To reduce the latency, the Cross Encoder reranker is disabled by default. It can be enabled either per-request by sending "rerank": true in the /query payload, or globally by flipping the rerank argument in the rag.retrieve() call inside backend/api.py.

Running Locally

1) Create .env

Copy example environment and edit:

cp env.example .env

Open .env and set OPENAI_API_KEY and API_TOKEN

2) Build and Run

docker-compose up --build

3) Access

First run notes

Running on AWS EC2

1) Create EC2 Instance

2) Connect and Prepare the Machine

ssh -i your-key.pem ubuntu@your-public-ip
  
sudo apt update && sudo apt upgrade -y
sudo apt install -y docker.io git
sudo systemctl enable --now docker
sudo usermod -aG docker ubuntu
  
# Install Docker Compose
sudo curl -L "https://github.com/docker/compose/releases/latest/download/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
sudo chmod +x /usr/local/bin/docker-compose
  
exit
ssh -i your-key.pem ubuntu@your-public-ip

3) Deploy Code

Clone your repository and configure environment:

git clone https://github.com/ozyurtf/brown-assistant.git
cd brown-assistant
cp env.example .env
nano .env   # set OPENAI_API_KEY and any other variables

4) Launch Services

docker-compose up -d --build
docker-compose up

5) Access