Bootcamp Capstone Project β Built with LangChain, LangGraph, FAISS, MCP Servers, and Live APIs
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 | 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 |
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β 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) β
βββββββββββββββ βββββββββββββββββ
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.
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.
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.
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.
| 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 |
| 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 |
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 |
| 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 (β
/
FAISS search βββΆ CRAG Grader βββΆ correct/ambiguous βββΆ Synthesizer (uses filtered chunks)
β
ββββΆ incorrect βββΆ Tavily web search βββΆ Synthesizer (uses [Web] results)
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.
| 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 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.
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
| 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 |
# 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| 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 |
MIT License β free to use, modify, and distribute.