1
stars
23
commits
Python
primary language
May 13, 2026
updated
A RAG chatbot that answers questions about Stony Brook University's Undergraduate Student Government (USG) by reasoning over its real governing documents — the constitution, financial bylaws, procedural manual, standing committee bylaws, and weekly senate meeting minutes.
USG governs how millions of dollars of student activity fees are allocated, and the rules live across dozens of long, jargon-heavy documents. Students and club treasurers routinely need answers like "what's the procedure for requesting a budget reallocation?" or "how did Senator X vote on the IFC budget?" — questions that take a human 20 minutes of cross-referencing PDFs to answer. This project is that human.
Live demo: usg-chatbot.vercel.app — backend is on Render's free tier, so the first request after idle takes ~30s to wake up.
It's not just naive vector search. The backend is an agentic RAG system where gpt-5.4-mini picks from four retrieval tools depending on the question:
| Tool | When the agent uses it |
|---|---|
rag_retrieval_tool | Semantic search over FAISS — for conceptual "how do I…" questions |
search_directory_tool | Regex across a whole folder (e.g. all meeting minutes) — for finding every mention of a person or topic |
file_tool (search mode) | Regex inside one known file — for precise keyword lookup |
file_tool (read_range mode) | Read specific line ranges — to pull surrounding context after a match |
list_files_tool | Enumerates the document set so the model never guesses filenames |
The system prompt enforces a three-pass search protocol for questions like voting records: a direct keyword pass, an implicit-group pass (catching "the senate unanimously voted…" when the question was about a specific senator), then a context-resolution pass that expands the read window until the surrounding text actually identifies what was being voted on. This solved the biggest failure mode of pure RAG — confidently answering with chunks that lack enough context to be correct.
Next.js (App Router, React 19, Tailwind)
│ POST /chat (SSE stream)
▼
Flask + flask-cors
│
▼
Agent loop (agent_chatbot.py)
├─ gpt-5.4-mini function-calling (up to 15 tool turns)
├─ FAISS IndexFlatL2 + text-embedding-3-small
└─ Sandboxed filesystem tools over /documents
│
▼
Streamed tokens → SSE → React markdown renderer
vector_creation.py hashes every source file (SHA-256) and stores a per-file → FAISS-ID map in index_state.json. Re-running the build only re-embeds files whose hash changed, and deletes vectors for removed files by reconstructing the index without them. Embedding ~hundreds of chunks against OpenAI isn't free; this makes iteration cheap.fetch_documents.py authenticates with Google Drive via OAuth, exports the USG senate's shared folder of Google Docs and Sheets, and recursively follows hyperlinks inside those documents to pull in linked bylaws and budget sheets — so the corpus stays in sync with what USG actually publishes.os.path.realpath and reject any path that escapes documents/, so the agent can't be prompt-injected into reading .env or token.json.Backend: Python, Flask, OpenAI (gpt-5.4-mini, text-embedding-3-small), FAISS, tiktoken, Google Drive / Docs / Sheets APIs Frontend: Next.js 15, React 19, TypeScript, Tailwind CSS v4, react-markdown Deployment: Vercel (frontend), Render (backend)
app.py Flask server + SSE streaming
agent_chatbot.py Agent loop, tool definitions, RAG retrieval
vector_creation.py Incremental FAISS index builder
fetch_documents.py Google Drive ingestion pipeline
prompt.txt System prompt (search protocol)
documents/ Source corpus (bylaws, minutes, budgets)
frontend/ Next.js app
Prereqs: Python 3.8+, Node 18+, an OpenAI API key.
# Backend
python -m venv venv && source venv/bin/activate # Windows: venv\Scripts\activate
pip install -r requirements.txt
echo 'OPENAI_API_KEY="sk-..."' > .env
python vector_creation.py # builds faiss_index.faiss from documents/
python app.py # serves on :5001
# Frontend (separate terminal)
cd frontend && npm install && npm run dev # serves on :3000
If faiss-cpu won't install via pip, use conda: conda install -c conda-forge faiss-cpu.
To re-sync the corpus from USG's Google Drive, drop a Google Cloud OAuth credentials.json in the project root and run python fetch_documents.py.
Python
77.2%
TypeScript
21.7%
1
stars
23
commits
Python
primary language
May 13, 2026
updated
A RAG chatbot that answers questions about Stony Brook University's Undergraduate Student Government (USG) by reasoning over its real governing documents — the constitution, financial bylaws, procedural manual, standing committee bylaws, and weekly senate meeting minutes.
USG governs how millions of dollars of student activity fees are allocated, and the rules live across dozens of long, jargon-heavy documents. Students and club treasurers routinely need answers like "what's the procedure for requesting a budget reallocation?" or "how did Senator X vote on the IFC budget?" — questions that take a human 20 minutes of cross-referencing PDFs to answer. This project is that human.
Live demo: usg-chatbot.vercel.app — backend is on Render's free tier, so the first request after idle takes ~30s to wake up.
It's not just naive vector search. The backend is an agentic RAG system where gpt-5.4-mini picks from four retrieval tools depending on the question:
| Tool | When the agent uses it |
|---|---|
rag_retrieval_tool | Semantic search over FAISS — for conceptual "how do I…" questions |
search_directory_tool | Regex across a whole folder (e.g. all meeting minutes) — for finding every mention of a person or topic |
file_tool (search mode) | Regex inside one known file — for precise keyword lookup |
file_tool (read_range mode) | Read specific line ranges — to pull surrounding context after a match |
list_files_tool | Enumerates the document set so the model never guesses filenames |
The system prompt enforces a three-pass search protocol for questions like voting records: a direct keyword pass, an implicit-group pass (catching "the senate unanimously voted…" when the question was about a specific senator), then a context-resolution pass that expands the read window until the surrounding text actually identifies what was being voted on. This solved the biggest failure mode of pure RAG — confidently answering with chunks that lack enough context to be correct.
Next.js (App Router, React 19, Tailwind)
│ POST /chat (SSE stream)
▼
Flask + flask-cors
│
▼
Agent loop (agent_chatbot.py)
├─ gpt-5.4-mini function-calling (up to 15 tool turns)
├─ FAISS IndexFlatL2 + text-embedding-3-small
└─ Sandboxed filesystem tools over /documents
│
▼
Streamed tokens → SSE → React markdown renderer
vector_creation.py hashes every source file (SHA-256) and stores a per-file → FAISS-ID map in index_state.json. Re-running the build only re-embeds files whose hash changed, and deletes vectors for removed files by reconstructing the index without them. Embedding ~hundreds of chunks against OpenAI isn't free; this makes iteration cheap.fetch_documents.py authenticates with Google Drive via OAuth, exports the USG senate's shared folder of Google Docs and Sheets, and recursively follows hyperlinks inside those documents to pull in linked bylaws and budget sheets — so the corpus stays in sync with what USG actually publishes.os.path.realpath and reject any path that escapes documents/, so the agent can't be prompt-injected into reading .env or token.json.Backend: Python, Flask, OpenAI (gpt-5.4-mini, text-embedding-3-small), FAISS, tiktoken, Google Drive / Docs / Sheets APIs Frontend: Next.js 15, React 19, TypeScript, Tailwind CSS v4, react-markdown Deployment: Vercel (frontend), Render (backend)
app.py Flask server + SSE streaming
agent_chatbot.py Agent loop, tool definitions, RAG retrieval
vector_creation.py Incremental FAISS index builder
fetch_documents.py Google Drive ingestion pipeline
prompt.txt System prompt (search protocol)
documents/ Source corpus (bylaws, minutes, budgets)
frontend/ Next.js app
Prereqs: Python 3.8+, Node 18+, an OpenAI API key.
# Backend
python -m venv venv && source venv/bin/activate # Windows: venv\Scripts\activate
pip install -r requirements.txt
echo 'OPENAI_API_KEY="sk-..."' > .env
python vector_creation.py # builds faiss_index.faiss from documents/
python app.py # serves on :5001
# Frontend (separate terminal)
cd frontend && npm install && npm run dev # serves on :3000
If faiss-cpu won't install via pip, use conda: conda install -c conda-forge faiss-cpu.
To re-sync the corpus from USG's Google Drive, drop a Google Cloud OAuth credentials.json in the project root and run python fetch_documents.py.
Python
77.2%
TypeScript
21.7%