Production-ready RAG chatbot with hybrid retrieval (dense vector + BM25 sparse), grounded answers with citations, document ingestion, and a full SaaS-style UI.
┌─────────────────────────────────────────────────────────────────────────┐
│ Browser (index.html) │
│ Upload docs · Stream chat · Citations · Session analytics │
└─────────────────────────────────┬───────────────────────────────────────┘
│ HTTP / SSE
┌─────────────────────────────────▼───────────────────────────────────────┐
│ FastAPI (api/main.py) │
│ /ingest · /chat · /chat/stream · /documents · /health · /feedback │
└──────┬──────────────┬──────────────┬──────────────┬─────────────────────┘
│ │ │ │
▼ ▼ ▼ ▼
┌────────────┐ ┌─────────────┐ ┌──────────┐ ┌─────────────────────────┐
│ Ingestion │ │ Retrieval │ │Generation│ │ Memory │
│ load/chunk │ │ dense+BM25 │ │ RAG chain│ │ PostgreSQL + sessions │
│ embed │ │ RRF hybrid │ │ citations│ │ chat_history, docs │
└─────┬──────┘ └──────┬──────┘ └────┬─────┘ └─────────────────────────┘
│ │ │
▼ ▼ ▼
┌──────────────┐ ┌──────────┐ ┌─────────────┐
│ Vector Store │ │ BM25 idx │ │ LLM Layer │
│ Pinecone / │ │ in-memory│ │ Ollama / HF │
│ FAISS fallback│ │ │ │ fallback │
└──────────────┘ └──────────┘ └─────────────┘
- Python 3.11+
- PostgreSQL 16 (via Docker or local install)
- Ollama with
llama3.2model (optional — HF fallback available) - Pinecone free tier API key (optional — FAISS fallback built-in)
cd ragchat
docker-compose up -dcp .env .env.local # edit as neededMinimum required for local dev (FAISS + Ollama, no Pinecone):
DATABASE_URL=postgresql+asyncpg://user:password@localhost:5432/ragchat
OLLAMA_BASE_URL=http://localhost:11434
LLM_MODEL=llama3.2python -m venv .venv
# Windows
.venv\Scripts\activate
# macOS/Linux
source .venv/bin/activate
pip install -r requirements.txt
python -c "import nltk; nltk.download('punkt'); nltk.download('punkt_tab')"ollama pull llama3.2cd ragchat
uvicorn api.main:app --host 0.0.0.0 --port 8000 --reload- Sign up at https://app.pinecone.io
- Create a project and copy your API key
- Set in
.env:PINECONE_API_KEY=your_key_here PINECONE_INDEX_NAME=ragchat PINECONE_ENVIRONMENT=us-east-1-aws
- The index is auto-created on first startup (384 dimensions, cosine metric)
If Pinecone is unavailable, the system silently falls back to a local FAISS index at ./data/faiss_index.
cd ragchat
pytest tests/ -vcurl http://localhost:8000/healthcurl -X POST http://localhost:8000/ingest \
-F "file=@document.pdf" \
-F "strategy=fixed"curl -X POST http://localhost:8000/ingest/text \
-H "Content-Type: application/json" \
-d '{"text": "Your content here.", "source_name": "notes", "strategy": "sentence"}'curl -X POST http://localhost:8000/chat \
-H "Content-Type: application/json" \
-d '{"query": "What is this document about?", "session_id": "my-session-1", "top_k": 5}'curl -N "http://localhost:8000/chat/stream/my-session-1?query=Summarize%20the%20document&top_k=5"curl http://localhost:8000/documentscurl -X DELETE http://localhost:8000/documents/{document_id}curl http://localhost:8000/history/my-session-1curl -X POST http://localhost:8000/feedback \
-H "Content-Type: application/json" \
-d '{"chat_history_id": "uuid-here", "rating": 1, "comment": "Helpful answer"}'ragchat/
├── api/ FastAPI app, routes, schemas
├── config/ pydantic-settings
├── core/ LLM + vector store abstractions
├── ingestion/ load, chunk, embed, ingest pipeline
├── retrieval/ dense, sparse, hybrid RRF
├── generation/ prompt builder, chain, citations
├── memory/ PostgreSQL + session store
├── static/ index.html frontend
├── tests/ pytest suite
└── data/ FAISS index (gitignored)
- Hybrid retrieval: Pinecone/FAISS dense search + BM25 sparse, fused via RRF (k=60)
- Chunking strategies: fixed, sentence, semantic
- Local embeddings: sentence-transformers
all-MiniLM-L6-v2 - LLM fallback: Ollama primary → HuggingFace Inference API
- Conversation memory: last 3 exchanges prepended to queries
- Streaming: SSE token-by-token responses
- Citations:
[Source N]parsing mapped to chunk metadata - Admin: document list, delete, ingest status tracking
MIT