Spaces:
Paused
Paused
feat: Update .gitignore and README for improved documentation and clarity; enhance error handling in answer generation with retry logic
Browse files- .gitignore +2 -1
- README.md +70 -34
- app/rag.py +19 -12
- streamlit_app.py +1 -1
.gitignore
CHANGED
|
@@ -1,4 +1,5 @@
|
|
| 1 |
venv
|
| 2 |
|
| 3 |
.env
|
| 4 |
-
__pycache__/
|
|
|
|
|
|
| 1 |
venv
|
| 2 |
|
| 3 |
.env
|
| 4 |
+
__pycache__/
|
| 5 |
+
data/
|
README.md
CHANGED
|
@@ -1,53 +1,89 @@
|
|
| 1 |
-
---
|
| 2 |
-
title: RAG QA System
|
| 3 |
-
emoji: "🧠"
|
| 4 |
-
colorFrom: indigo
|
| 5 |
-
colorTo: blue
|
| 6 |
-
sdk: streamlit
|
| 7 |
-
sdk_version: "1.37.1"
|
| 8 |
-
python_version: "3.10"
|
| 9 |
-
app_file: streamlit_app.py
|
| 10 |
-
pinned: false
|
| 11 |
-
---
|
| 12 |
-
|
| 13 |
# RAG-Based Question Answering System
|
| 14 |
|
| 15 |
## Overview
|
| 16 |
-
This project
|
| 17 |
|
| 18 |
## Features
|
| 19 |
-
-
|
| 20 |
-
-
|
| 21 |
-
- FAISS vector store
|
| 22 |
-
- Background
|
| 23 |
-
- FastAPI
|
| 24 |
-
-
|
| 25 |
-
-
|
| 26 |
-
- Metrics logging (latency)
|
| 27 |
|
| 28 |
## Architecture Diagram
|
| 29 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 30 |
|
| 31 |
## Setup
|
| 32 |
-
1.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 33 |
- `pip install -r requirements.txt`
|
| 34 |
-
|
| 35 |
-
- `
|
| 36 |
-
|
| 37 |
-
3. Run the API:
|
| 38 |
- `uvicorn app.main:app --reload`
|
| 39 |
-
|
| 40 |
- `streamlit run streamlit_app.py`
|
| 41 |
|
| 42 |
## API Endpoints
|
| 43 |
-
- `POST /documents/upload` (multipart file) →
|
| 44 |
-
- `GET /documents/status/{job_id}` →
|
| 45 |
-
- `POST /qa` →
|
| 46 |
-
- `GET /health`
|
| 47 |
|
| 48 |
## Usage
|
| 49 |
-
1.
|
| 50 |
-
2.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 51 |
3. Ask questions via the UI or `POST /qa`.
|
| 52 |
|
| 53 |
## Mandatory Explanations
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
# RAG-Based Question Answering System
|
| 2 |
|
| 3 |
## Overview
|
| 4 |
+
This project implements a Retrieval-Augmented Generation (RAG) system using FastAPI for the backend API, FAISS for vector storage, Sentence Transformers for embeddings, and Google Gemini for answer generation. It supports PDF and TXT document ingestion with OCR fallback for scanned PDFs, background job processing, and a Streamlit UI for user interaction.
|
| 5 |
|
| 6 |
## Features
|
| 7 |
+
- Document ingestion (PDF, TXT) with OCR support for scanned PDFs
|
| 8 |
+
- Text chunking and embedding using Sentence Transformers
|
| 9 |
+
- FAISS vector store for similarity search
|
| 10 |
+
- Background job processing for ingestion
|
| 11 |
+
- FastAPI with Pydantic validation and basic rate limiting
|
| 12 |
+
- Streamlit UI for document upload and question answering
|
| 13 |
+
- Metrics logging (latency, chunks, etc.)
|
|
|
|
| 14 |
|
| 15 |
## Architecture Diagram
|
| 16 |
+
```
|
| 17 |
+
User (Browser) <-> Streamlit UI <-> FastAPI Backend
|
| 18 |
+
|
|
| 19 |
+
v
|
| 20 |
+
Document Ingestion (Background)
|
| 21 |
+
|
|
| 22 |
+
v
|
| 23 |
+
Text Extraction (PyPDF2 + OCR)
|
| 24 |
+
|
|
| 25 |
+
v
|
| 26 |
+
Chunking (size: 800, overlap: 120)
|
| 27 |
+
|
|
| 28 |
+
v
|
| 29 |
+
Embedding (SentenceTransformers)
|
| 30 |
+
|
|
| 31 |
+
v
|
| 32 |
+
FAISS Index + Metadata Storage
|
| 33 |
+
|
|
| 34 |
+
v
|
| 35 |
+
Query Processing -> Retrieval -> Generation (Gemini)
|
| 36 |
+
```
|
| 37 |
+
|
| 38 |
+
## Mandatory Explanations
|
| 39 |
+
|
| 40 |
+
### Why Chunk Size of 800 with 120 Overlap?
|
| 41 |
+
- **Chunk Size (800)**: Balances providing sufficient context for coherent answers while keeping chunks small enough for precise retrieval. Larger chunks (e.g., 2000) might include irrelevant information, reducing retrieval accuracy; smaller chunks (e.g., 500) may lose context across sentences.
|
| 42 |
+
- **Overlap (120)**: Ensures continuity between chunks, preventing loss of information at boundaries. This helps maintain semantic flow, especially for questions spanning multiple sentences.
|
| 43 |
+
|
| 44 |
+
### One Retrieval Failure Case Observed
|
| 45 |
+
- **Case**: When querying "who is krish kumar" on a resume PDF, the top retrieved chunks had low similarity scores (e.g., 0.087), leading to potential irrelevant or incomplete answers. This occurred because the query embedding didn't closely match the document chunks due to variations in phrasing or focus on specific sections.
|
| 46 |
+
- **Mitigation**: Increasing top_k from 1 to 4 improved coverage, and the system falls back to showing excerpts if LLM fails.
|
| 47 |
+
|
| 48 |
+
### One Metric Tracked
|
| 49 |
+
- **Latency**: Tracked for both ingestion (time to process and embed document) and QA (time for retrieval + generation). Logged in `metrics.jsonl` for monitoring performance. For example, ingestion latency averaged 5-10 seconds for PDFs, QA around 2-10 seconds depending on API response.
|
| 50 |
|
| 51 |
## Setup
|
| 52 |
+
1. Clone the repository:
|
| 53 |
+
- `git clone <repo-url>`
|
| 54 |
+
2. Create a virtual environment:
|
| 55 |
+
- `python -m venv venv`
|
| 56 |
+
- `source venv/bin/activate` (Linux/Mac) or `venv\Scripts\activate` (Windows)
|
| 57 |
+
3. Install dependencies:
|
| 58 |
- `pip install -r requirements.txt`
|
| 59 |
+
4. Set up environment variables (create `.env` file):
|
| 60 |
+
- `GOOGLE_API_KEY=<your-gemini-api-key>`
|
| 61 |
+
5. Run the FastAPI backend:
|
|
|
|
| 62 |
- `uvicorn app.main:app --reload`
|
| 63 |
+
6. Run the Streamlit UI:
|
| 64 |
- `streamlit run streamlit_app.py`
|
| 65 |
|
| 66 |
## API Endpoints
|
| 67 |
+
- `POST /documents/upload` (multipart file upload) → Returns `job_id` for background ingestion
|
| 68 |
+
- `GET /documents/status/{job_id}` → Job status and details
|
| 69 |
+
- `POST /qa` → Answer and retrieved contexts (JSON body: `{"question": "str", "top_k": 4}`)
|
| 70 |
+
- `GET /health` → Health check
|
| 71 |
|
| 72 |
## Usage
|
| 73 |
+
1. Start the backend and UI as above.
|
| 74 |
+
2. In the Streamlit UI, upload a PDF or TXT file.
|
| 75 |
+
3. Wait for ingestion (check job status if needed).
|
| 76 |
+
4. Ask questions; the system retrieves relevant chunks and generates answers using Gemini.
|
| 77 |
+
|
| 78 |
+
## Evaluation Criteria Alignment
|
| 79 |
+
- **Chunking Strategy**: 800/120 overlap for optimal context vs. precision.
|
| 80 |
+
- **Retrieval Quality**: FAISS similarity search with normalized embeddings; fallback to excerpts.
|
| 81 |
+
- **API Design**: RESTful with Pydantic, background jobs, rate limiting.
|
| 82 |
+
- **Metrics Awareness**: Latency logging for performance monitoring.
|
| 83 |
+
- **System Explanation**: Detailed in this README and code comments.
|
| 84 |
+
|
| 85 |
+
## GitHub Repository
|
| 86 |
+
[Link to GitHub Repo](https://github.com/your-username/rag-qa-system) # Replace with actual link
|
| 87 |
3. Ask questions via the UI or `POST /qa`.
|
| 88 |
|
| 89 |
## Mandatory Explanations
|
app/rag.py
CHANGED
|
@@ -192,18 +192,25 @@ def generate_answer(question: str, contexts: List[Dict]) -> str:
|
|
| 192 |
f"Context:\n{context_text}\n\nQuestion: {question}\nAnswer:"
|
| 193 |
)
|
| 194 |
|
| 195 |
-
|
| 196 |
-
|
| 197 |
-
|
| 198 |
-
|
| 199 |
-
|
| 200 |
-
|
| 201 |
-
|
| 202 |
-
|
| 203 |
-
|
| 204 |
-
|
| 205 |
-
|
| 206 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 207 |
|
| 208 |
|
| 209 |
def ingest_document(path: Path, doc_id: str, source: str) -> Dict:
|
|
|
|
| 192 |
f"Context:\n{context_text}\n\nQuestion: {question}\nAnswer:"
|
| 193 |
)
|
| 194 |
|
| 195 |
+
max_retries = 3
|
| 196 |
+
for attempt in range(max_retries):
|
| 197 |
+
try:
|
| 198 |
+
client = genai.Client(api_key=GOOGLE_API_KEY)
|
| 199 |
+
response = client.models.generate_content(
|
| 200 |
+
model="gemini-2.0-flash",
|
| 201 |
+
contents=prompt,
|
| 202 |
+
)
|
| 203 |
+
return response.text.strip() or context_text
|
| 204 |
+
except Exception as e:
|
| 205 |
+
error_str = str(e)
|
| 206 |
+
if "429" in error_str and attempt < max_retries - 1:
|
| 207 |
+
wait_time = 2 ** attempt # Exponential backoff
|
| 208 |
+
time.sleep(wait_time)
|
| 209 |
+
continue
|
| 210 |
+
return (
|
| 211 |
+
f"LLM generation failed: {error_str}. Here are the most relevant excerpts:\n\n"
|
| 212 |
+
+ context_text
|
| 213 |
+
)
|
| 214 |
|
| 215 |
|
| 216 |
def ingest_document(path: Path, doc_id: str, source: str) -> Dict:
|
streamlit_app.py
CHANGED
|
@@ -12,7 +12,7 @@ from app.rag import ingest_document, search, generate_answer
|
|
| 12 |
|
| 13 |
st.set_page_config(page_title="RAG QA", page_icon="🧠", layout="wide")
|
| 14 |
|
| 15 |
-
API_BASE = os.getenv("API_BASE", "http://localhost:
|
| 16 |
|
| 17 |
st.title("RAG-Based Question Answering")
|
| 18 |
|
|
|
|
| 12 |
|
| 13 |
st.set_page_config(page_title="RAG QA", page_icon="🧠", layout="wide")
|
| 14 |
|
| 15 |
+
API_BASE = os.getenv("API_BASE", "http://localhost:8001")
|
| 16 |
|
| 17 |
st.title("RAG-Based Question Answering")
|
| 18 |
|