An intelligent, multi-format document Q&A system powered by a fully agentic Retrieval-Augmented Generation (RAG) pipeline β built with FAISS, SentenceTransformers, Groq (LLaMA 3.3-70B), and Streamlit.
Agentic RAG Q&A is an AI-powered chatbot that lets you upload documents in multiple formats and ask natural language questions about them. Unlike a simple RAG pipeline, this system uses a Planner Agent that dynamically decides how to answer each query β whether to retrieve from your documents, rewrite the query, answer from general knowledge, or ask for clarification.
Agents communicate through a structured MCP (Message Control Protocol) β every handoff between agents is a typed, traceable message with a sender, receiver, payload, and unique trace ID.
All LLM calls go through Groq's API using llama-3.3-70b-versatile for fast, high-quality responses.
- π Multi-format document support β PDF, DOCX, PPTX, CSV, TXT, and Markdown
- π Semantic search using FAISS +
all-MiniLM-L6-v2SentenceTransformer embeddings - π Smart chunking β 400 token chunks with 80 token overlap, handled by the Retrieval Agent
- π Semantic reranking β top-8 FAISS candidates reranked by entity match + keyword overlap before returning top-3
- π§ LLM-powered answers via Groq API (LLaMA 3.3-70B) β fast and accurate
- ποΈ Planner Agent β dynamically selects the best tool for every query
- π Query rewriting β automatically rewrites failed queries for better retrieval
- π General knowledge fallback β answers questions even without uploaded documents
- π Source attribution β shows exactly which document chunks backed the answer
- π¨ MCP message passing β all agents communicate via structured, traceable messages
- π Session reset β clears all uploads, FAISS index, and conversation in one click
- π₯οΈ Clean Streamlit UI β easy to use for anyone
The system uses a four-agent + tools architecture. The Planner Agent sits at the center, deciding which tool to invoke on each attempt (up to 3 retries). All inter-agent communication uses MCP messages.
User Query
β
βΌ
βββββββββββββββββββββββββββββββββββββββββββββββ
β Ingestion Agent β
β file_parser.py β extracts raw text β
ββββββββββββββββββββββ¬βββββββββββββββββββββββββ
β MCP Message (raw text + source)
βΌ
ββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Retrieval Agent β
β chunk_text() β 400 token chunks, 80 overlap β
β embedding_utils.py (all-MiniLM-L6-v2) β
β β FAISS index β top-8 search β rerank β top-3 β
ββββββββββββββββββββββ¬ββββββββββββββββββββββββββββββ
β MCP Message (chunks + score)
βΌ
βββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Planner Agent β
β β
β attempt 0 β retrieval_tool β
β attempt 1 β rewrite_tool | llm_general_tool β
β attempt 2 β llm_general_tool (final fallback) β
β β
β Tools: retrieval | rewrite | llm_general | clarify β
ββββββββββββββββββββββ¬βββββββββββββββββββββββββββββββββ
β
βΌ
βββββββββββββββββββββββββββββββββββββββββββββββ
β LLM Response Agent β
β Groq β LLaMA 3.3-70B β answer + sources β
ββββββββββββββββββββββ¬βββββββββββββββββββββββββ
β MCP Message
βΌ
Answer + Source Chunks
| Agent | File | Responsibility |
|---|---|---|
| Ingestion Agent | agents/ingestion_agent.py |
Uses file_parser.py to parse all documents in data/ into text chunks with source metadata, returned as an MCP message |
| Retrieval Agent | agents/retrieval_agent.py |
Chunks raw text (400 tokens, 80 overlap), embeds with all-MiniLM-L6-v2, indexes in FAISS, fetches top-8 candidates, then reranks by entity match + keyword overlap before returning top-3 |
| Planner Agent | agents/planner_agent.py |
Selects the best tool per attempt using rule-based logic and LLaMA; also rewrites failed queries via a dedicated Groq prompt |
| LLM Response Agent | agents/llm_response_agent.py |
Scores and deduplicates retrieved chunks, constructs a strict context-grounded prompt, calls Groq (LLaMA 3.3-70B), and returns a structured MCP answer |
Defined in agents/tools.py, these are the callable actions the Planner Agent selects from:
| Tool | Function | What it does |
|---|---|---|
retrieval |
retrieval_tool() |
Runs semantic search over the FAISS index and returns matching chunks with their score |
llm_general |
llm_general_tool() |
Answers directly from LLaMA's general knowledge β no documents needed |
rewrite |
rewrite_tool() |
Prompts LLaMA to rewrite a failed query into a more retrieval-friendly form |
clarify |
clarify_tool() |
Returns a clarification message when the query is too vague to process |
All agents communicate using structured MCP (Message Control Protocol) messages defined in mcp/protocol.py:
{
"sender": "LLMResponseAgent",
"receiver": "User",
"type": "FINAL_RESPONSE",
"trace_id": "uuid-...", # unique per request for traceability
"payload": { ... } # agent-specific data
}Every handoff β ingestion β retrieval β planner β LLM β passes through this format, making the pipeline easy to debug, extend, and trace.
The Planner Agent runs up to 3 attempts per query:
- Attempt 0 β always tries
retrievalfirst - Attempt 1 β if retrieval score is low (
< 0.3), triesrewrite; otherwise asks LLaMA to pick the best tool based on the query and score - Attempt 2 β falls back to
llm_generalunconditionally
If the LLM answers "not found in the document" even after a successful retrieval, the planner automatically escalates to general knowledge. If the LLM confidence is below 0.35, it selects clarify instead. This loop ensures the user always gets an answer.
Agentic-RAG-QnA/
β
βββ agents/
β βββ ingestion_agent.py # Document parsing agent
β βββ retrieval_agent.py # FAISS embedding & retrieval agent
β βββ planner_agent.py # Tool selection & query rewriting agent
β βββ llm_response_agent.py # Groq LLaMA answer generation agent
β βββ tools.py # retrieval, llm_general, rewrite, clarify tools
β
βββ mcp/
β βββ protocol.py # create_mcp_message() β structured agent messaging
β
βββ utils/
β βββ file_parser.py # File type dispatcher (PDF, DOCX, PPTX, CSV, TXT, MD)
β βββ embedding_utils.py # EmbeddingModel wrapper (all-MiniLM-L6-v2)
β
βββ vector_store/ # FAISS index + metadata persistence
β
βββ app.py # Streamlit web application
βββ main.py # CLI entry point
βββ requirements.txt # Python dependencies
βββ README.md
- Python 3.9 or higher
- A Groq API key (free tier available)
git clone https://github.com/aqeeel02/Agentic-RAG-QnA.git
cd Agentic-RAG-QnACreate a .env file in the root directory:
GROQ_API_KEY=your_groq_api_key_here
pip install -r requirements.txtstreamlit run app.pyThe app will open in your browser at http://localhost:8501.
| Technology | Purpose |
|---|---|
| Streamlit | Web UI |
| Groq API | Fast LLM inference |
| LLaMA 3.3-70B | Answer generation & planning |
| FAISS | Vector similarity search |
SentenceTransformers (all-MiniLM-L6-v2) |
Text embeddings |
| PyMuPDF | PDF parsing |
| python-docx | DOCX parsing |
| python-pptx | PPTX parsing |
| Pandas | CSV parsing |
streamlit
python-docx
python-pptx
PyMuPDF
pandas
markdown
faiss-cpu
sentence-transformers
transformers
groq
python-dotenv
Install all with:
pip install -r requirements.txtA file-type dispatcher that routes each uploaded file to the correct parser β fitz for PDFs, python-docx for Word files, python-pptx for PowerPoint, pandas for CSV, and plain file reading for TXT and Markdown. Returns raw extracted text.
A thin wrapper around SentenceTransformers that loads all-MiniLM-L6-v2 and exposes an embed_texts() method. Used by the Retrieval Agent to encode both document chunks and incoming queries into the same vector space.
Scans the data/ directory, calls file_parser.py for each file, and wraps the resulting raw text in an MCP message with source metadata. It does not chunk β it hands off the full text to the Retrieval Agent.
The most complex agent in the pipeline. It first chunks the raw text into 400-token windows with 80-token overlap using chunk_text(). Each chunk is embedded using EmbeddingModel.embed_texts() and indexed in FAISS with metadata (source, chunk_id, entities). On a query, it fetches the top-8 candidates from FAISS, then reranks them using a scoring formula that boosts named entity matches (+2.5 per entity) and keyword overlap (+0.2 per word hit) on top of the base similarity score. The top-3 reranked chunks and their average score are returned via MCP message.
The brain of the system. On each attempt it evaluates the query and retrieval score to pick the most appropriate tool. Uses LLaMA itself to reason about tool selection when the decision isn't purely rule-based. Also rewrites failed queries by prompting LLaMA for a more retrieval-friendly version.
Receives retrieved chunks, scores and deduplicates them by relevance, builds a strict context-grounded prompt (max 2000 characters of context), and calls LLaMA 3.3-70B via Groq. Returns a final MCP message with the answer and the top 2 most relevant source snippets with their chunk IDs.
This project is open source. Feel free to use, modify, and distribute it.
β If you found this project useful, consider giving it a star!