A Django-based Retrieval-Augmented Generation (RAG) app for uploading .txt and .pdf files, storing embeddings in PostgreSQL with pgvector, and answering questions with Groq's OpenAI-compatible chat API.
- Upload text and PDF documents through the web UI or JSON API.
- Extract raw text from each file.
- Split text into chunks with overlap for retrieval.
- Generate 384-dimensional embeddings with
sentence-transformers/all-MiniLM-L6-v2. - Store chunk embeddings in PostgreSQL using
pgvector. - Retrieve the closest chunks with vector similarity search.
- Send retrieved context to Groq's
llama-3.1-8b-instantmodel to generate an answer. - Save query history and returned sources for later viewing.
- Django 4.2
- PostgreSQL
pgvectorlanggraphsentence-transformerslangchain-text-splitters- Groq via the OpenAI Python client
PyPDF2python-dotenv
Note: the app now uses LangGraph for the initial query orchestration path, while keeping the rest of the service layer and Django views intact.
DjangoRag/
├── django_rag/ # Django project config
│ ├── settings.py
│ ├── urls.py
│ ├── asgi.py
│ └── wsgi.py
├── rag_app/ # Main app
│ ├── management/commands/
│ │ └── ingest_documents.py # Batch ingestion command
│ ├── migrations/
│ │ ├── 0000_enable_pgvector.py # Enables PostgreSQL vector extension
│ │ └── 0001_initial.py # Creates core tables
│ ├── services/
│ │ └── rag_service.py # Text processing, embeddings, retrieval, LLM calls
│ ├── forms.py
│ ├── models.py
│ ├── urls.py
│ └── views.py
├── templates/
│ ├── base.html
│ └── rag_app/ # Document, query, and result templates
├── documents/ # Uploaded files and media root
├── requirements.txt
├── README.md
└── Architecture.md
Stores uploaded file metadata, extracted text, and processing state.
titlefilecontentuploaded_atprocessed
Stores chunked document content plus a 384-dimensional embedding.
documentcontentchunk_indexembeddingcreated_at
Stores user questions, generated answers, and source snippets returned to the UI/API.
queryanswersourcescreated_at
- A user uploads a
.txtor.pdffile. DocumentUploadViewsaves the file as aDocument.RAGService.ingest_document()reads the file from disk.TextProcessorextracts raw text.- Text is chunked using
RecursiveCharacterTextSplitter. EmbeddingServicegenerates an embedding for each chunk.- Each chunk is saved as a
DocumentChunk. - The document is marked as processed.
- A user submits a question from the UI or API.
RAGService.retrieve_similar_chunks()embeds the query.- A LangGraph workflow routes either to a no-results response or to answer generation.
pgvectorL2Distanceranks the closest chunks.- Top chunks are concatenated into a prompt.
- Groq generates the answer with
llama-3.1-8b-instant. - The answer and sources are saved in
QueryHistory.
python -m venv venv
source venv/bin/activate
pip install -r requirements.txtCreate a PostgreSQL database, then make sure the vector extension can be enabled by migrations.
Example:
CREATE DATABASE rag_db;The app migration rag_app/migrations/0000_enable_pgvector.py runs:
CREATE EXTENSION IF NOT EXISTS vector;Copy .env.template to .env and set values:
DB_NAME=rag_db
DB_USER=postgres
DB_PASSWORD=postgres
DB_HOST=localhost
DB_PORT=5432
GROQ_API_KEY=your_groq_api_key_here
SECRET_KEY=your_django_secret_key_here
DEBUG=True
ALLOWED_HOSTS=localhost,127.0.0.1python manage.py migratepython manage.py runserverOpen http://127.0.0.1:8000.
You can run the app locally with Docker Compose and a dedicated PostgreSQL + pgvector container.
Copy .env.template to .env and set at least:
DB_NAME=rag_db
DB_USER=postgres
DB_PASSWORD=postgres
DB_HOST=localhost
DB_PORT=5432
GROQ_API_KEY=your_groq_api_key_here
SECRET_KEY=your_django_secret_key_here
DEBUG=True
ALLOWED_HOSTS=localhost,127.0.0.1Note: in Docker, docker-compose.yml overrides DB_HOST to db, so you can keep localhost in your local .env for non-Docker runs.
docker compose up --buildThis starts:
webon http://127.0.0.1:8000dbonlocalhost:5432
The web container runs migrations automatically on startup.
docker compose downTo also remove the Postgres data volume:
docker compose down -vRun Django tests inside the app container:
docker compose exec web python manage.py testOpen a Django shell:
docker compose exec web python manage.py shellInspect the database with psql:
docker compose exec db psql -U postgres -d rag_db- Dockerfile builds the Django app image.
- docker-compose.yml defines the app and database services.
- .dockerignore keeps the Docker build context small.
/shows uploaded documents and processing status./documents/upload/uploads and processes a document./rag/query/asks a question and shows recent query history./rag/answer/<id>/shows a saved result.
POST /api/documents/upload/
Multipart form fields:
titlefile
Returns:
{
"success": true,
"document_id": 1,
"title": "Example",
"chunks": 4
}POST /api/rag/query/
Request body:
{
"query": "What does the document say about refunds?"
}Returns:
{
"query_id": 1,
"answer": "...",
"sources": [
{
"document": "Policy",
"content": "...",
"similarity": 0.8123
}
]
}You can ingest files from disk with the management command:
python manage.py ingest_documents
python manage.py ingest_documents --directory documents
python manage.py ingest_documents --directory /absolute/path/to/files --reprocessSupported file types are .txt and .pdf.
Current defaults from django_rag/settings.py:
EMBEDDING_MODEL:sentence-transformers/all-MiniLM-L6-v2CHUNK_SIZE:1000CHUNK_OVERLAP:200MEDIA_ROOT:documents/TIME_ZONE:UTC
- Ingestion runs synchronously inside the upload request. Large files will make the upload request slow.
- Query answering makes a live external API call to Groq.
- The initial LangGraph rollout covers the query orchestration path only.
- Query results use the top 5 nearest chunks by L2 distance.
- The Docker setup is intended for local development and testing, not production hardening.
- Uploaded files are stored under the local
documents/directory. Document.contentstores the full extracted text, so database size grows with file volume.- The current tests are lightweight and do not mock external embedding or LLM calls.
- Main service logic lives in rag_app/services/rag_service.py.
- UI templates live in templates/rag_app.
- URL routing starts in django_rag/urls.py and delegates to rag_app/urls.py.
For a deeper system breakdown, see Architecture.md.