Skip to content

About

πŸ”¬ An advanced Multi-Agent AI Research Assistant built with LangGraph and Python. Orchestrates independent Planner, Retriever, and Synthesizer agents using a decoupled FastAPI backend and a beautiful Streamlit web chat UI. Features Corrective RAG (FAISS + CRAG), RAGAS evaluation, MCP tool servers, custom web APIs, and dual-layer memory.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Latest commit

Β 

History

7 Commits

Folders and files

NameName
Last commit message
Last commit date
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

πŸ”¬ AI Research Assistant

Multi-Agent AI System Β· LangGraph Β· RAG Β· MCP Β· FastAPI Β· Streamlit

Python LangChain LangGraph FastAPI Streamlit OpenAI

Bootcamp Capstone Project β€” Built with LangChain, LangGraph, FAISS, MCP Servers, and Live APIs


πŸ“Œ What Is This?

The AI Research Assistant is a production-grade multi-agent AI system that answers complex research questions by intelligently combining information from multiple sources simultaneously:

  • πŸ“„ Your own documents (PDFs, TXT, Markdown) via a FAISS vector database
  • πŸ“° Live news articles via NewsAPI
  • πŸ“š Academic books via Open Library API
  • πŸ—‚οΈ Local research notes via Filesystem MCP Server
  • πŸ—„οΈ Session history via SQLite MCP Server

Three specialized AI agents collaborate inside a LangGraph state machine to plan, retrieve, and synthesize a comprehensive answer β€” all served through a beautiful Streamlit UI backed by a FastAPI REST API.


🎯 Problem & Users

Problem Researchers and students waste hours manually searching across papers, news, and books to answer a single question
End Users Students, researchers, analysts, and knowledge workers who need fast, multi-source answers
Solution An agentic AI that autonomously searches all sources in parallel and synthesizes a cited, structured response

πŸ—οΈ System Architecture

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                    Streamlit UI  (port 8501)                     β”‚
β”‚   Chat Β· History Β· Summary Β· Document Upload                     β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                           β”‚ HTTP REST
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                   FastAPI Backend  (port 8000)                   β”‚
β”‚   /query Β· /history Β· /summary Β· /add-document Β· /health        β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                           β”‚
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                   LangGraph Orchestrator                         β”‚
β”‚                                                                  β”‚
β”‚  check_query β†’ [clarify?]                                        β”‚
β”‚       ↓                                                          β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”        β”‚
β”‚  β”‚ Planner  │───▢│  Retriever   │───▢│  Synthesizer    β”‚        β”‚
β”‚  β”‚  Agent   β”‚    β”‚    Agent     β”‚    β”‚     Agent       β”‚        β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜    β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜    β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜        β”‚
β”‚  Breaks query    RAG + APIs +        Writes cited answer         β”‚
β”‚  into plan       MCP reads           + saves to MCP             β”‚
β”‚                 + CRAG grading      + RAGAS scoring              β”‚
β”‚                 (web fallback)      (3 metrics)                  β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
          β”‚                  β”‚                    β”‚
   β”Œβ”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”   β”Œβ”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”   β”Œβ”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”
   β”‚  FAISS      β”‚   β”‚  NewsAPI      β”‚   β”‚  Open Library β”‚
   β”‚  Vector DB  β”‚   β”‚  (live news)  β”‚   β”‚  API (books)  β”‚
   β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜   β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜   β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
          β”‚                                      β”‚
   β”Œβ”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”                       β”Œβ”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”
   β”‚ Filesystem  β”‚                       β”‚  SQLite MCP   β”‚
   β”‚ MCP Server  β”‚                       β”‚  (sessions)   β”‚
   β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜                       β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

πŸ€– The Three Agents

Agent 1 β€” Planner Agent (agents/planner_agent.py)

Receives the raw user query and uses GPT-4o-mini to produce a structured JSON research plan. Decides which sources to activate, generates focused search terms, and breaks the query into sub-questions.

Agent 2 β€” Retriever Agent (agents/retriever_agent.py)

Executes the plan in full. Searches the FAISS vector store for relevant document chunks, fetches live news articles, searches academic books, and reads local research notes β€” all in one pass. Every RAG chunk is graded for relevance by the CRAG Grader before being handed off; see 🩺 Corrective RAG (CRAG) below.

Agent 3 β€” Synthesizer Agent (agents/synthesizer_agent.py)

Takes every piece of retrieved context and writes a comprehensive, cited, well-structured final answer. Also persists the session to SQLite and appends a note to the filesystem via MCP. The finished answer is then scored by the RAGAS Evaluator; see πŸ“Š RAGAS Quality Evaluation below.


πŸ”€ LangGraph Workflow

START
  β”‚
  β–Ό
check_query ──(too vague?)──▢ clarify ──▢ END
  β”‚
  β–Ό
planner ──(error?)──▢ error ──▢ END
  β”‚
  β–Ό
retriever ──(error?)──▢ error ──▢ END
  β”‚
  β–Ό
synthesizer ──(error?)──▢ error ──▢ END
  β”‚
  β–Ό
END

Every node reads from and writes to a shared ResearchState TypedDict. Conditional edges route based on error flags and query validation results.


πŸ› οΈ MCP Servers

Server File What It Does
Filesystem MCP mcp_tools/filesystem_mcp.py Read/write local research notes in data/local/
SQLite MCP mcp_tools/sqlite_mcp.py Store every research session, log sources used, enable history search

🌐 External APIs

API File Purpose Key Required?
NewsAPI api_tools/news_api.py Fetch recent news articles on any topic βœ… Free at newsapi.org
Open Library api_tools/open_library_api.py Search academic books by subject ❌ No key needed
Tavily Search api_tools/tavily_api.py CRAG web-search fallback when local RAG chunks are irrelevant βœ… Free at tavily.com

🩺 Corrective RAG (CRAG)

Plain RAG trusts whatever the vector store returns. CRAG (retrieval/crag_grader.py) adds a relevance-grading step between the FAISS search and the Synthesizer, so bad or off-topic chunks never make it into the final answer.

How it works β€” after search_vector_store() returns the top-K chunks, the CRAGGrader sends each chunk to an LLM individually with a yes/no relevance prompt, then classifies the batch into one of three quality levels:

Quality Meaning What happens
βœ… Correct Every chunk is relevant All chunks are passed to the Synthesizer as-is
⚠️ Ambiguous Some chunks relevant, some not Irrelevant chunks are filtered out; only the relevant ones are kept
❌ Incorrect No chunks are relevant (or none were retrieved) Local RAG is abandoned entirely β€” the Retriever Agent calls Tavily for a live web search instead, and the Synthesizer is told to cite [Web] sources

The grading result is stored as context["retrieval_quality"] and surfaced end-to-end: the FastAPI /query response includes a retrieval_quality field, and the Streamlit UI renders it as a color-coded badge (βœ…/⚠️/❌) right next to the source tags on every answer.

FAISS search ──▢ CRAG Grader ──▢ correct/ambiguous ──▢ Synthesizer (uses filtered chunks)
                     β”‚
                     └──▢ incorrect ──▢ Tavily web search ──▢ Synthesizer (uses [Web] results)

πŸ“Š RAGAS Quality Evaluation

After the Synthesizer writes an answer, the RAGAS Evaluator (evaluation/ragas_evaluator.py) automatically scores it against the retrieved context β€” turning "does this look right?" into a measurable number. Scores are computed once per query and never block the response: if RAGAS itself fails (bad key, library error, etc.) the query still returns normally with an empty ragas_scores dict.

Metric What it measures
Faithfulness Whether every claim in the answer is actually supported by the retrieved context β€” the core hallucination check
Answer Relevancy Whether the answer actually addresses the question asked, rather than drifting off-topic
Context Precision Whether the retrieved chunks that mattered were ranked/used well, i.e. how much of the retrieved context was actually useful

Each score is a float between 0.0 and 1.0, rounded to 2 decimals. They're returned in the /query response's ragas_scores field and rendered in the Streamlit UI as color-coded progress bars (🟒 β‰₯0.7, 🟠 β‰₯0.4, πŸ”΄ below) inside a "πŸ“Š View Quality Scores" expander under each answer.


πŸ’Ύ Data Sources

Source Type Location
Local .txt / .md / .pdf documents Local data/docs/
SQLite research session database Local data/local/knowledge.db
NewsAPI live articles Remote REST API
Open Library book catalog Remote REST API

🧠 Memory System

Memory Type Class Purpose
Buffer Memory ChatMessageHistory Stores exact recent conversation turns
Summary Memory GPT-4o-mini summarization Compresses long histories into a running summary

Both are managed by memory/memory_manager.py and injected into every agent prompt.


πŸ“ Project Structure

research-assistant/
β”‚
β”œβ”€β”€ main.py                    ← CLI entry point
β”‚
β”œβ”€β”€ api/
β”‚   └── server.py              ← FastAPI REST backend
β”‚
β”œβ”€β”€ ui/
β”‚   └── app.py                 ← Streamlit frontend
β”‚
β”œβ”€β”€ agents/
β”‚   β”œβ”€β”€ planner_agent.py       ← Agent 1: Query planning
β”‚   β”œβ”€β”€ retriever_agent.py     ← Agent 2: Multi-source retrieval
β”‚   └── synthesizer_agent.py   ← Agent 3: Answer synthesis
β”‚
β”œβ”€β”€ graph/
β”‚   β”œβ”€β”€ state.py               ← LangGraph ResearchState TypedDict
β”‚   └── workflow.py            ← State graph with nodes & edges
β”‚
β”œβ”€β”€ memory/
β”‚   └── memory_manager.py      ← Buffer + Summary memory
β”‚
β”œβ”€β”€ retrieval/
β”‚   β”œβ”€β”€ document_loader.py     ← Loads .txt / .md / .pdf files
β”‚   β”œβ”€β”€ chunker.py             ← RecursiveCharacterTextSplitter
β”‚   β”œβ”€β”€ vector_store.py        ← FAISS build / load / search
β”‚   └── crag_grader.py         ← CRAG: grades chunk relevance
β”‚
β”œβ”€β”€ evaluation/
β”‚   └── ragas_evaluator.py     ← RAGAS: scores answer quality
β”‚
β”œβ”€β”€ mcp_tools/
β”‚   β”œβ”€β”€ filesystem_mcp.py      ← MCP #1: Local file operations
β”‚   └── sqlite_mcp.py          ← MCP #2: Session database
β”‚
β”œβ”€β”€ api_tools/
β”‚   β”œβ”€β”€ news_api.py            ← NewsAPI client
β”‚   β”œβ”€β”€ open_library_api.py    ← Open Library client
β”‚   └── tavily_api.py          ← Tavily client (CRAG web fallback)
β”‚
β”œβ”€β”€ config/
β”‚   └── settings.py            ← Env loading & typed constants
β”‚
β”œβ”€β”€ data/
β”‚   β”œβ”€β”€ docs/                  ← Your documents for RAG
β”‚   β”œβ”€β”€ local/                 ← Auto-generated notes & SQLite DB
β”‚   └── vector_store/          ← FAISS index (auto-generated)
β”‚
β”œβ”€β”€ utils/
β”‚   └── helpers.py             ← Rich terminal formatting
β”‚
β”œβ”€β”€ requirements.txt
β”œβ”€β”€ .env.example
└── .gitignore

βœ… Bootcamp Requirements Checklist

Requirement Implementation File
ChatOpenAI GPT-4o-mini for all agents agents/*.py
PromptTemplate LCEL prompt chains agents/*.py
LLM Chain prompt | llm | parser agents/*.py
Buffer Memory ChatMessageHistory memory/memory_manager.py
Summary Memory GPT summarization loop memory/memory_manager.py
Embeddings OpenAIEmbeddings retrieval/vector_store.py
Chunking RecursiveCharacterTextSplitter retrieval/chunker.py
Vector DB FAISS retrieval/vector_store.py
Agent 1 Planner Agent agents/planner_agent.py
Agent 2 Retriever Agent agents/retriever_agent.py
Agent 3 Synthesizer Agent agents/synthesizer_agent.py
LangGraph StateGraph + conditional edges graph/workflow.py
MCP Server #1 Filesystem MCP mcp_tools/filesystem_mcp.py
MCP Server #2 SQLite MCP mcp_tools/sqlite_mcp.py
External API #1 NewsAPI api_tools/news_api.py
External API #2 Open Library api_tools/open_library_api.py
External API #3 Tavily Search (CRAG fallback) api_tools/tavily_api.py
Local Data Source TXT docs + SQLite data/
Remote Data Source NewsAPI + Open Library + Tavily api_tools/
FastAPI REST backend api/server.py
Streamlit UI Full chat interface ui/app.py
Corrective RAG (CRAG) Relevance grading + web fallback retrieval/crag_grader.py
RAGAS Evaluation Faithfulness / relevancy / precision scoring evaluation/ragas_evaluator.py

πŸš€ Quick Start

# 1. Clone
git clone https://github.com/YOUR_USERNAME/ai-research-assistant.git
cd ai-research-assistant

# 2. Virtual environment
python -m venv .venv
source .venv/bin/activate        # Windows: .venv\Scripts\activate

# 3. Install
pip install -r requirements.txt

# 4. Configure
cp .env.example .env
# Edit .env and add your OPENAI_API_KEY

# 5a. Run CLI only
python main.py

# 5b. Run full UI (two terminals)
uvicorn api.server:app --reload --port 8000   # Terminal 1
streamlit run ui/app.py                        # Terminal 2
# Open http://localhost:8501

πŸ–₯️ UI Features

Tab Feature
πŸ’¬ Chat Bubble chat UI, source tags per answer, Planner Agent plan viewer
πŸ“œ History All past sessions from SQLite, searchable, re-ask button
πŸ“‹ Summary Live AI summary + recent turns side by side
πŸ“ Upload Drag & drop docs into the knowledge base, auto-rebuilds FAISS
πŸ’‘ Sidebar Quick example queries, session stats, API status indicator

πŸ“„ License

MIT License β€” free to use, modify, and distribute.

About

πŸ”¬ An advanced Multi-Agent AI Research Assistant built with LangGraph and Python. Orchestrates independent Planner, Retriever, and Synthesizer agents using a decoupled FastAPI backend and a beautiful Streamlit web chat UI. Features Corrective RAG (FAISS + CRAG), RAGAS evaluation, MCP tool servers, custom web APIs, and dual-layer memory.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages