A production-grade, distributed Retrieval-Augmented Generation (RAG) platform built with FastAPI, Celery, PostgreSQL (pgvector + tsvector), FlashRank, and Groq LPUs.
This system addresses common bottlenecks in naive RAG pipelines, such as event-loop starvation during parsing, semantic blind spots in pure dense retrieval, and bi-encoder scoring inaccuracy. It combines asynchronous ingestion, two-stage hybrid search with Reciprocal Rank Fusion (RRF), deep cross-encoder re-ranking, and grounded generation with chunk-level attribution.
- Architecture Overview
- Key Engineering Highlights
- Tech Stack
- Project Structure
- Local Setup
- API Reference
- Architectural Decisions & Trade-Offs
- License
π View the Architecture Diagram
- Non-Blocking Ingestion: Document uploads return an immediate
HTTP 202 Acceptedresponse with an ingestion tracking UUID. - Worker Isolation: Heavy PDF text extraction, sliding-window chunking (
chunk_size=300,chunk_overlap=50), and batch embedding computations are offloaded to Celery workers over a Redis message queue. - Failure Safety: Database updates run within explicit session boundaries, rolling back cleanly and marking the document
FAILEDif errors occur mid-stream.
-
Branch A (Dense Semantic Retrieval): Uses
pgvectorto run cosine distance scans (<=>) over 384-dimensional dense vectors generated byall-MiniLM-L6-v2. -
Branch B (Sparse Lexical Retrieval): Uses PostgreSQL native
tsvectorwithts_rank_cdto execute cover-density keyword matching for exact IDs, technical terms, and code snippets. -
Reciprocal Rank Fusion (RRF): Merges dense and sparse outputs using positional rankings rather than unstable, uncalibrated score weights:
$$RRF_Score(d) = \sum_{m \in {\text{dense}, \text{sparse}}} \frac{1}{k + \text{rank}_m(d)} \quad (k = 60)$$ -
Cross-Encoder Re-Ranking: The top 10 RRF candidates pass through
flashrank(ms-marco-MiniLM-L-12-v2). The model scores full query-document token interactions simultaneously via self-attention, pruning false positives before the context hits the LLM.
- Ultra-Low Latency: Feeds the top 3 cross-encoder re-ranked chunks to Groq's LPU infrastructure running
llama-3.3-70b-versatile. - Strict Context Boundary: Enforces a system prompt that mandates direct source citations (
[Source Chunk X]) and suppresses hallucinations.
| Domain | Technology | Purpose |
|---|---|---|
| API Runtime | Python 3.11+, FastAPI, Uvicorn | Asynchronous web interface with dependency-injected database sessions |
| Task Queue | Celery 5, Redis 7 | Distributed worker orchestration and task serialization |
| Database & Search | PostgreSQL 16, pgvector, Full-Text Search |
Unified relational storage, vector indices, and inverted lexical search |
| Embeddings & Re-ranking | sentence-transformers (all-MiniLM-L6-v2), flashrank |
Bi-encoder vector representations and cross-encoder attention re-ranking |
| LLM Inference | Groq Cloud API (llama-3.3-70b-versatile) |
Fast, grounded answer synthesis |
| Frontend | React 18, Vite, Lucide Icons, React-Markdown | Single-page UI with real-time status polling, metric visualization, and Markdown support |
Hybrid_RAG_Engine/
βββ backend/
β βββ app/
β β βββ core/
β β β βββ config.py # Pydantic BaseSettings environment parsing
β β β βββ database.py # Async (asyncpg) and Sync (psycopg) database session pools
β β βββ models/
β β β βββ schema.py # SQLAlchemy ORM models (Document, DocumentChunk)
β β βββ services/
β β β βββ embedding.py # embedding service
β β β βββ generator.py # Groq LLM synthesis client with strict prompt constraints
β β β βββ parser.py # PyPDF text extraction and sliding-window chunker
β β β βββ reranker.py # FlashRank cross-encoder service
β β β βββ search.py # Hybrid search engine (pgvector + tsvector + RRF)
β β βββ workers/
β β β βββ celery_worker.py # Background ingestion pipeline task
β β βββ main.py # FastAPI application with modern lifespan management
β βββ Dockerfile # Container configuration
β βββ entrypoint.sh # Container entrypoint orchestrator
β βββ requirements.txt # Pinned Python package dependencies
βββ frontend/
β βββ src/
β β βββ App.jsx # React dashboard with live polling and search
β β βββ index.css # Dark-mode responsive styling
β β βββ main.jsx # React application entry point
β βββ package.json # Node.js dependencies
β βββ vite.config.js # Vite build and dev-server configuration
βββ docker-compose.yml # Local infrastructure setup (PostgreSQL 16 + Redis 7)
βββ README.md # System documentation
Make sure the following are installed:
- Docker & Docker Compose
- Python 3.11+
- Node.js v18+ and npm
- A free Groq Cloud API key
Start the PostgreSQL container (with pgvector enabled) and the Redis broker from the project root:
docker compose up -dVerify the containers are running:
docker ps| Service | Address |
|---|---|
| PostgreSQL | localhost:5435 |
| Redis | localhost:6379 |
a. Create and activate a virtual environment
cd backend
python -m venv .venv
source .venv/bin/activate # On Windows: .venv\Scripts\activateb. Install dependencies
pip install --upgrade pip
pip install -r requirements.txtc. Configure environment variables
Create a .env file in the backend/ directory:
# PostgreSQL Connection Strings
DATABASE_URL=postgresql+asyncpg://rag_user:rag_password@localhost:5435/rag_engine
SYNC_DATABASE_URL=postgresql+psycopg://rag_user:rag_password@localhost:5435/rag_engine
# Redis Configuration
REDIS_URL=redis://localhost:6379/0
# Vector Settings
VECTOR_DIMENSION=384
# Groq Cloud API
GROQ_API_KEY=gsk_your_actual_groq_api_key_here
GROQ_MODEL=llama-3.3-70b-versatiled. Start the Celery background worker
Open a dedicated terminal, navigate to backend/, activate your .venv, and run:
celery -A app.workers.celery_worker.celery_app worker --loglevel=infoe. Start the FastAPI gateway
Open a separate terminal, navigate to backend/, activate your .venv, and run:
uvicorn app.main:app --reload --port 8000Interactive API documentation is available at http://localhost:8000/docs.
a. Install dependencies
cd frontend
npm installb. Configure environment variables
Create a .env file in the frontend/ directory:
VITE_API_BASE=http://localhost:8000/api/v1c. Start the Vite development server
npm run devOpen your browser at http://localhost:5173.
Tip: You need three processes running at the same time: the Celery worker, the FastAPI server, and the Vite dev server. Docker must also be up for PostgreSQL and Redis.
Base URL: http://localhost:8000/api/v1
| Endpoint | POST /api/v1/documents/upload |
| Content-Type | multipart/form-data |
| Response Status | 202 Accepted |
Response
{
"document_id": "c7a4b123-5e82-4f3b-b27e-8531d044f51a",
"filename": "microservices_architecture.pdf",
"status": "PENDING",
"message": "Document accepted for asynchronous background processing."
}| Endpoint | GET /api/v1/documents/{document_id}/status |
| Response Status | 200 OK |
Response
{
"document_id": "c7a4b123-5e82-4f3b-b27e-8531d044f51a",
"filename": "microservices_architecture.pdf",
"status": "COMPLETED",
"total_chunks": 42,
"created_at": "2026-10-02T18:32:00.124510"
}| Endpoint | POST /api/v1/query |
| Content-Type | application/json |
Request Payload
{
"query": "How is the circuit breaker pattern implemented in distributed systems?",
"top_candidates": 10,
"final_top_k": 3
}Response Payload
{
"query": "How is the circuit breaker pattern implemented in distributed systems?",
"answer": "The circuit breaker pattern wraps calls to remote services to prevent cascading failures [Source Chunk 1]. When the failure threshold is exceeded, the circuit trips to an OPEN state, failing fast without hitting the degraded dependency [Source Chunk 2]...",
"sources": [
{
"chunk_id": "89b70b55-d365-4f4b-84a1-b8efd806a38f",
"document_id": "c7a4b123-5e82-4f3b-b27e-8531d044f51a",
"content": "A circuit breaker monitors for failures. Once failures reach a predefined threshold, the breaker trips...",
"chunk_index": 12,
"rrf_score": 0.032522,
"dense_rank": 2,
"sparse_rank": 1,
"cross_encoder_score": 0.9412
}
]
}- Atomic Consistency: Using PostgreSQL as both the relational and vector store allows cascading deletes (
ON DELETE CASCADE). Deleting a document atomically purges its metadata, raw text, embeddings, and inverted indexes inside a single ACID transaction, preventing orphaned vectors. - Operational Simplicity: Avoids managing external synchronization pipelines, network overhead, and separate access controls for external vector databases (e.g., Pinecone or Milvus).
-
Unbounded Score Variance: Cosine distance is strictly bounded (
$[0, 2]$ ), whereas BM25 /ts_rank_cdscores are unbounded ($[0, \infty)$) and change based on document collection size. - Scale-Invariance: RRF scores candidates purely by their relative rank order, making it stable across diverse document types without manual weight tuning.
- Performance: Running cross-attention across thousands of chunks introduces severe latency bottlenecks. A bi-encoder (
all-MiniLM-L6-v2) handles initial candidate retrieval across the entire database in under 20 ms, while the cross-encoder (flashrank) evaluates only the top 10 candidates with full token-to-token attention.
This project is licensed under the MIT License.