Skip to main content
Glama
jayanthlocam

agentic-rag-knowledge-assistant

by jayanthlocam
README.md
# Agentic RAG Knowledge Assistant

> Upload documents → build a vector knowledge base → ask questions with grounded, cited answers.
> Works via browser dashboard, REST API, or MCP tools. Syncs Google Drive folders automatically.

Built with **FastAPI** · **LangGraph** · **PostgreSQL + pgvector** · **MCP** · **Streamlit**

![Knowledge-base overview](docs/screenshots/overview.png)
![Streaming agent with source citations](docs/screenshots/agent.png)

---

## ✨ What it does

- **Upload & ingest** PDFs, DOCX, and text files with automatic chunking and embedding
- **Semantic search** over your documents using pgvector HNSW cosine similarity
- **AI-powered Q&A** with LangGraph — answers cite exact source chunks
- **Google Drive sync** — connect a Drive folder and auto-ingest business docs via MCP
- **MCP server** — expose your knowledge base to any MCP-compatible AI agent
- **Multi-tenant** — JWT auth, per-user document isolation, no cross-tenant leaks

## 📊 Evaluation Baseline

Tested on 11 gold-set cases (8 answerable, 2 unanswerable, 1 prompt injection) against 4 business-policy documents synced from Google Drive.

| Metric | Score |
| --- | --- |
| **Context Relevance** | 1.00 |
| **Context Sufficiency** | 1.00 |
| **Answer Relevance** | 1.00 |
| **Answer Correctness** | 1.00 |
| **Faithfulness** | 1.00 |
| **Retrieval Hit Rate @5** | 1.00 |
| **MRR @5** | 1.00 |
| **Gold Chunk Precision @5** | 0.25 |
| **Citation Precision** | 0.25 |
| **Refusal Accuracy** | 1.00 |
| **Prompt Injection Resistance** | 1.00 |
| **Mean Latency** | 2,598 ms |

> Models: `gpt-5.6-terra` (generation + judge) · `text-embedding-3-small` (embeddings)
> See [`evaluation/baselines/`](evaluation/baselines/) for the full sanitized baseline.

## 🏗️ Architecture

```text
Browser ──► Streamlit ──► FastAPI ──────────────┐
                                                ├──► PostgreSQL + pgvector
MCP client ──────────► MCP server (HTTP) ───────┘
                            │
                            └── JWT auth + owner-scoped queries

Google Drive folder ──► Drive MCP ──► sync endpoint ──► ingestion pipeline
```

See [docs/architecture.md](docs/architecture.md) for detailed component and data-flow docs.

## 🛠️ Tech Stack

| Layer | Tech |
| --- | --- |
| Frontend | Streamlit, HTTPX |
| API | FastAPI, Pydantic |
| Agent | LangGraph, LangChain Core |
| MCP | MCP Python SDK, Streamable HTTP |
| DB | PostgreSQL 16, SQLAlchemy 2, Alembic |
| Vectors | pgvector, HNSW cosine index |
| Docs | pypdf, python-docx |
| Auth | JWT (HS256), Argon2 |
| Dev | uv, Ruff, mypy, pytest, Docker Compose |

## 🚀 Quick Start

**Prerequisites:** Docker + Docker Compose v2

```bash
# 1. Clone and configure
cp .env.example .env
# Edit .env → set POSTGRES_PASSWORD and JWT_SECRET (min 32 chars)

# 2. Start everything
docker compose up --build -d

# 3. Open the dashboard
open http://localhost:8501

# 4. Verify health
curl http://localhost:8000/health/ready
```

**Want OpenAI-powered answers?** Add to `.env`:
```dotenv
EMBEDDING_PROVIDER=openai
EMBEDDING_MODEL=text-embedding-3-small
EMBEDDING_API_KEY=sk-...
LLM_PROVIDER=openai
LLM_MODEL=gpt-4o
LLM_API_KEY=sk-...
```

Without OpenAI keys, the system runs fully offline with deterministic embeddings and extractive answers — great for development and testing.

## 💻 Local Development

```bash
# Install deps
uv sync --frozen --extra frontend

# Run migrations
uv run alembic upgrade head

# Start API (terminal 1)
uv run uvicorn backend.app.main:app --reload --port 8000

# Start MCP server (terminal 2)
uv run python -m backend.app.mcp.server

# Start dashboard (terminal 3)
uv run streamlit run frontend/app.py
```

## 📡 API Reference

All endpoints under `/api/v1`. Full docs at `http://localhost:8000/api/v1/docs`.

| Area | Endpoints |
| --- | --- |
| Auth | `POST /auth/register` · `POST /auth/login` |
| User | `GET /users/me` |
| Threads | `POST /threads` · `GET /threads` · `GET /threads/{id}` · `DELETE /threads/{id}` |
| Documents | `POST /documents/upload` · `GET /documents` · `GET /documents/{id}` · `DELETE /documents/{id}` |
| Search | `POST /retrieval/search` |
| Chat | `POST /chat/{thread_id}` · `POST /chat/{thread_id}/stream` · `GET /chat/{thread_id}/history` |
| Drive Sync | `POST /data-sources/google-drive/sync` |
| Metrics | `GET /metrics/overview` |
| Health | `GET /health/live` · `GET /health/ready` |

## 🔌 MCP Tools

Connect any MCP client with `Authorization: Bearer <jwt>` to `http://localhost:8001/mcp`:

| Tool | Description |
| --- | --- |
| `list_documents` | List all documents for the authenticated user |
| `get_document` | Get document metadata by ID |
| `search_documents` | Semantic vector search |
| `answer_from_documents` | Grounded Q&A with citations |
| `ingest_document` | Upload and index a document |

```bash
# Quick connectivity check
MCP_ACCESS_TOKEN="<token>" uv run python -m scripts.mcp_smoke
```

## 📁 Google Drive Sync

Sync a Drive folder into your knowledge base — supports PDF, DOCX, TXT, and native Google Docs.

**Setup:**
1. Enable `drive.googleapis.com` and `drivemcp.googleapis.com` in your GCP project
2. Create a **Desktop app** OAuth client (not Web app)
3. Get a short-lived access token and add to `.env`:
   ```dotenv
   GOOGLE_DRIVE_FOLDER_ID=<your-folder-id>
   GOOGLE_DRIVE_ACCESS_TOKEN=<oauth-token>
   GOOGLE_DRIVE_QUOTA_PROJECT=<gcp-project-id>
   ```
4. Trigger sync:
   ```bash
   curl -X POST http://localhost:8000/api/v1/data-sources/google-drive/sync \
     -H "Authorization: Bearer <app-jwt>"
   ```

Re-syncing skips unchanged files. Duplicate content across files is deduplicated automatically.

## ✅ Testing & CI

```bash
# Run full validation (lint + types + tests)
make validate

# Run tests with coverage
uv run pytest --cov=backend.app --cov-report=term-missing --cov-fail-under=80
```

CI runs on every PR: Ruff lint → Ruff format → mypy → pytest (with pgvector) → Docker build. See [`.github/workflows/backend-ci.yml`](.github/workflows/backend-ci.yml).

## 📂 Project Structure

```
backend/
├── app/
│   ├── agents/          # LangGraph workflow, answer providers
│   ├── api/             # FastAPI endpoints
│   ├── auth/            # JWT + Argon2 password security
│   ├── connectors/      # Google Drive MCP connector
│   ├── ingestion/       # PDF/DOCX extraction, chunking, embeddings
│   ├── mcp/             # MCP server + client
│   ├── models/          # SQLAlchemy models
│   ├── retrieval/       # pgvector search
│   └── services/        # Ingestion + Drive sync services
├── migrations/          # Alembic migrations
└── tests/               # Unit + integration tests
frontend/                # Streamlit dashboard
evaluation/              # Gold dataset + baselines
scripts/                 # Smoke tests + evaluation runner
docs/                    # Architecture + roadmap
```

## 🔒 Security

- Argon2 password hashing, JWT with explicit algorithm allow-list
- Owner-scoped queries — users can only access their own documents and vectors
- Citations built from DB records, not model-generated IDs
- Document text treated as untrusted evidence (not executable instructions)
- Google Drive access: read-only, folder-bounded, never exposed to the agent
- Containers run as unprivileged user
- Secrets excluded from Git and Docker build contexts

## ⚠️ Known Limitations

- Embeddings fixed at 1,536 dimensions (changing needs an Alembic migration)
- Scanned PDFs need OCR before upload (only embedded text extracted)
- Upload processing is synchronous (works fine, but a worker queue is better at scale)
- Drive sync is manually triggered with a short-lived token (production needs OAuth refresh)
- MCP server assumes TLS is terminated upstream

## 🗺️ Roadmap

**Next milestone:** [1,000-document scale test](docs/roadmap.md) — validate ingestion, deduplication, retrieval accuracy, and failure recovery at scale before adding more connectors.

**After that:** Worker queue for async ingestion, managed OAuth for Drive, OCR support, hybrid retrieval with reranking, GCP deployment with Cloud SQL + Secret Manager.

## 📄 License

[MIT](LICENSE)