ma-rag / README.md
DannyTeo
Drop Fly.io deploy path; document HF Spaces as Option 2
8e2022e
|
Raw
History Blame Contribute Delete
6.66 kB
---
title: Ma Rag
emoji: πŸ€–
colorFrom: indigo
colorTo: purple
sdk: docker
app_port: 8000
pinned: false
license: mit
short_description: Multi-agent RAG chat over Dell product / policy / tech docs
---
# LangGraph Multi-Agent RAG β€” Mobile Web App
A mobile-responsive chat app that turns a LangGraph collaborative multi-agent
pipeline (planner β†’ product / policy / tech / general specialists with RAG +
DuckDuckGo β†’ synthesizer) into a production-style web application. The
bundled knowledge base is a set of **Dell laptop product / policy / tech
support documents**, but the ingestion pipeline is generic β€” swap the files
in `data/source/` to retarget the app to any domain.
- **Backend:** FastAPI + LangGraph + LangChain + Chroma + Groq LLM
- **Frontend:** Mobile-first responsive HTML + Tailwind (CDN) + vanilla JS
- **Features:**
- Planner picks one or more specialist agents for a single question
- Per-domain RAG collections (`product`, `policy`, `tech`, `general`)
- DuckDuckGo search tool for fresh external info
- Final synthesizer merges specialist answers
- Collapsible **Planner Trace** and **Specialist Outputs** in the UI
- File upload endpoint to extend the knowledge base at runtime
- Single-container deploy (Docker) with Hugging Face Spaces / Cloud Run /
Render / Railway recipes in [`DEPLOY.md`](./DEPLOY.md)
## Project layout
```
.
β”œβ”€β”€ backend/
β”‚ β”œβ”€β”€ app/
β”‚ β”‚ β”œβ”€β”€ config.py # env + paths
β”‚ β”‚ β”œβ”€β”€ llm.py # LLM, embeddings, Chroma singletons
β”‚ β”‚ β”œβ”€β”€ tools.py # RAG tools + DuckDuckGo tool
β”‚ β”‚ β”œβ”€β”€ graph.py # LangGraph: planner β†’ agents β†’ synthesizer
β”‚ β”‚ β”œβ”€β”€ ingest.py # classify + chunk + index documents
β”‚ β”‚ └── main.py # FastAPI app + routes + static frontend
β”‚ └── requirements.txt
β”œβ”€β”€ frontend/
β”‚ β”œβ”€β”€ index.html # mobile-first chat UI
β”‚ β”œβ”€β”€ app.js # chat logic, upload, health
β”‚ └── styles.css
β”œβ”€β”€ data/
β”‚ β”œβ”€β”€ source/ # drop your .txt/.md/.pdf/.docx here
β”‚ └── chroma/ # persistent vector store (gitignored)
β”œβ”€β”€ scripts/
β”‚ └── push-to-hf.sh # deploy to a Hugging Face Space
β”œβ”€β”€ Dockerfile
β”œβ”€β”€ docker-compose.yml
β”œβ”€β”€ DEPLOY.md # per-platform deploy recipes
β”œβ”€β”€ .env.example
└── run.sh
```
## Quick start
```bash
cp .env.example .env
# edit .env: set GROQ_API_KEY=...
./run.sh
# then open http://localhost:8000
```
On first run it will:
1. Install dependencies into `.venv/`
2. Drop sample documents into `data/source/` if empty
3. Build the Chroma index lazily on the first `/api/ingest` call (or auto on upload)
To trigger the initial ingest, open the app, click **Settings β†’ Re-index existing
docs**, or call:
```bash
curl -X POST http://localhost:8000/api/ingest
```
## Endpoints
| Method | Path | Purpose |
| ------ | --------------- | -------------------------------------------------------------- |
| GET | `/` | Serves the mobile chat UI |
| GET | `/api/health` | Backend + API-key status |
| POST | `/api/chat` | `{ "message": "..." }` β†’ final answer + trace + per-agent logs |
| POST | `/api/ingest` | Rebuild Chroma collections from `data/source/` |
| POST | `/api/upload` | `multipart/form-data` files[] β†’ saves + re-indexes |
### Chat response shape
```json
{
"response": "synthesized final answer (markdown)",
"selected_agents": ["product_agent", "policy_agent"],
"agent_labels": { "product_agent": "Product Agent", "...": "..." },
"agent_outputs": { "product_agent": "...", "policy_agent": "..." },
"debug_log": "Planner selected: ...\nProduct Agent contributed.\n..."
}
```
## Configuration
All settings live in `.env` (see `.env.example`). Key ones:
- `GROQ_API_KEY` β€” required
- `GROQ_MODEL` β€” default `openai/gpt-oss-120b`
- `EMBEDDING_MODEL` β€” default `sentence-transformers/all-mpnet-base-v2`
- `SOURCE_DATA_DIR`, `CHROMA_DB_PATH` β€” override storage locations
- `CHUNK_SIZE`, `CHUNK_OVERLAP` β€” splitter tuning
## How document classification works
Files are routed into a specialist collection by **filename keyword**:
| Keywords in filename | Collection |
| ---------------------------------------------------- | --------------------- |
| `product`, `catalog`, `inventory`, `price` | `product_collection` |
| `policy`, `return`, `shipping`, `warranty`, `faq` | `policy_collection` |
| `tech`, `repair`, `support`, `troubleshoot` | `tech_collection` |
| anything else | `general_collection` |
Name your uploaded files accordingly to keep specialists sharp.
## Mobile responsiveness notes
- `100dvh` layout (no iOS URL-bar jump)
- Safe-area padding on the input footer
- Tap-sized buttons and single-column chat stream
- Textarea auto-grows and submits on Enter (Shift+Enter = newline)
- Collapsible trace / specialist panes so the screen stays clean on small devices
## Deployment
For production deploys (Docker, Hugging Face Spaces, Cloud Run, Render,
Railway, or a plain VPS), see **[DEPLOY.md](./DEPLOY.md)**. TL;DR for any
Docker host:
```bash
cp .env.example .env # set GROQ_API_KEY=gsk_...
docker compose up -d --build
curl -X POST http://localhost:8000/api/ingest
```
### Hugging Face Spaces
This repo is Spaces-ready β€” the YAML frontmatter at the top of this README
configures it as a Docker Space on port 8000. A helper script handles the
one-off push:
```bash
# 1. Create an empty Docker Space at https://huggingface.co/new-space
# 2. In the Space's Settings β†’ Variables and secrets, add:
# Secret: GROQ_API_KEY = gsk_...
# 3. Push the code:
HF_USER=your_hf_username \
HF_SPACE=your_space_name \
HF_TOKEN=hf_your_write_token \
./scripts/push-to-hf.sh
```
The first build takes ~5–8 minutes (it pre-downloads the embedding model into
the image). After it's "Running", the first request triggers auto-ingest of
the documents in `data/source/` (~30 s) before answering. Subsequent deploys
are just a regular `git push hf HEAD:main`.
## Extending
The code mirrors the notebook structure so the extensions suggested there
(memory, confidence scoring, weighted voting, guardrails, more tools) slot in
cleanly β€” add new tools to `tools.py`, new nodes/edges in `graph.py`.