Back to Projects
AI Automation / RAG System

Enterprise RAG Assistant

Independent Development

A full-stack enterprise RAG application demonstrating document processing, semantic search, and AI-powered conversational QA over uploaded documents.

Industry: AI / Enterprise Software / Developer Tools

Purpose

Full-stack Retrieval-Augmented Generation application with document processing, semantic search, hybrid retrieval, and AI-powered chat with streaming responses

Timeline: July 2026

Tech Stack

Next.js 16 (App Router)React 19Express 5TypeScriptSupabase (PostgreSQL + pgvector + Auth + Storage)OpenAI API (gpt-5-nano, text-embedding-3-small)DockerGitHub Actions CI/CDPrometheus MetricsPino Structured Logging

Overview

Enterprise documents — policy manuals, technical specifications, compliance guides, and operational procedures — are scattered across departments in PDF format. Employees spend hours manually searching through hundreds of pages to find specific answers, while traditional keyword search returns too many irrelevant results. This project builds a full-stack Retrieval-Augmented Generation system that makes institutional knowledge instantly queryable through natural language conversation. Users upload PDFs, which are processed through a pipeline of text extraction, intelligent chunking, and vector embedding, then ask questions and receive streaming AI answers grounded in their actual documents with citations back to source chunks.

Developmental Challenges

The development involved orchestrating a complex multi-stage pipeline where each phase — PDF extraction, text chunking, vector embedding, hybrid retrieval, and streaming chat — had its own failure modes. Chunk size calibration was particularly tricky: too small and critical context was lost between paragraphs; too large and retrieval precision dropped. Follow-up question handling required an entirely separate AI call just to rewrite queries before retrieval. Docker container-to-host networking, OpenAI rate limits during batch embedding, and coordinating async database writes after SSE streams all demanded careful error handling and resilience patterns.

Tailored Software Solution

The solution is a full-stack RAG application with a Next.js 16 frontend and Express 5 backend, backed by Supabase for PostgreSQL, pgvector, auth, and file storage. Documents are uploaded as PDFs, extracted to text with language detection, chunked using a hierarchical paragraph → sentence → word strategy (1000 token max, 200 token overlap), and embedded via OpenAI's text-embedding-3-small model into pgvector with an IVFFlat index. Retrieval uses a hybrid approach combining 60% vector cosine similarity with 40% tsvector keyword search, catching both semantic meaning and exact-match terms. The chat pipeline loads conversation history, rewrites follow-up questions into standalone queries using OpenAI, assembles context with token-aware deduplication, and streams responses via SSE with citation extraction. The entire system is protected by Supabase RLS on all 9 tables, JWT auth, Helmet security headers, rate limiting, and circuit breakers on external service calls.

System & Workflow Architecture

System Architecture

Client & API Gateway
Client (Browser) — Next.js 16 · React 19 · Tailwind
API Gateway (Express 5) — Auth · Rate Limiting · Metrics · Error Handling
Service Layer — Chat · Ingest · Search · Admin · Health
Data Layer & External Services
Supabase — PostgreSQL + pgvector · Auth · Storage · RLS
OpenAI — Embeddings + Chat
File Storage — PDF uploads

Data Processing Pipeline

Data Processing Pipeline
Upload: PDF → Multer → Supabase Storage → Document Metadata
Process: PDF Parse → Language Detection → Text Extraction → DB
Index: Chunk Text (1000 token max, 200 overlap) → OpenAI Embeddings (batch 100) → pgvector IVFFlat
Chat: User Query → Query Rewrite (follow-ups) → Hybrid Retrieval → Context Assembly → Prompt Build
Stream: OpenAI Responses API → SSE Tokens → Persist Messages + Citations → Done Event

System Design Document

Architecture Decisions

  • Monorepo with separate frontend (Next.js) and backend (Express) services — clear separation of concerns, independent deployment, and team scalability
  • Supabase as unified platform for PostgreSQL, pgvector, Auth, and Storage — eliminates the need to wire together 4+ separate services and provides built-in RLS
  • In-process job queue over Redis/BullMQ — sufficient for single-server deployment with simpler operational complexity; can migrate to external queue later for horizontal scaling
  • Server-Sent Events (SSE) for chat streaming over WebSockets — simpler implementation, native HTTP compatibility, no connection management overhead for a request-response chat pattern
  • Hybrid retrieval (vector + keyword) as default strategy — neither pure semantic search nor pure keyword search provides adequate recall alone across technical documents

Technology Choices

Vector Database

Supabase pgvector

Avoids separate vector DB service; PostgreSQL RLS provides per-user data isolation for free; IVFFlat index handles cosine similarity at scale

Embedding Model

OpenAI text-embedding-3-small

Best cost/quality ratio at $0.02/1M tokens; 1536 dimensions sufficient for semantic search without excessive storage

LLM

OpenAI gpt-5-nano via Responses API

Fast streaming via SSE, structured output support, cost-effective for RAG Q&A where context is provided

Backend Framework

Express 5 + TypeScript

Lightweight, no framework overhead, full control over middleware pipeline; TypeScript provides type safety across the service layer

Frontend Framework

Next.js 16 App Router

Server components for initial load performance, proxy.ts for auth middleware, consistent with portfolio tech stack

Authentication

Supabase Auth

JWT-based with SSR cookie management (@supabase/ssr), auto-creates user profiles via DB triggers, integrates with RLS policies

Trade-offs

  • In-process job queue vs Redis/BullMQ: Chose in-process for simplicity — sacrifices durability on server crash and prevents horizontal scaling of workers, but eliminates Redis dependency and operational complexity
  • Supabase managed pgvector vs self-hosted: Chose managed for zero-ops — trades fine-grained index tuning control for automatic backups, connection pooling, and integrated auth
  • Hybrid search vs pure vector: Chose hybrid for better recall — adds ~50ms latency from parallel keyword search but catches exact-match terms that embeddings miss (acronyms, codes, proper nouns)
  • 1000-token chunk size vs smaller: Larger chunks preserve more context for the LLM — trades retrieval precision (smaller chunks are more targeted) for answer quality (more surrounding context)
  • SSE vs WebSockets: SSE is unidirectional and simpler — trades bidirectional capability (not needed for chat) for automatic reconnection and no connection state management

Scaling Considerations

  • IVFFlat index with 100 lists — performs well up to ~1M vectors; beyond that, HNSW index would be more appropriate for sub-linear search
  • Embedding batch processing (100/batch) with exponential backoff — respects OpenAI rate limits while maximizing throughput during document indexing
  • Context assembly with token budget (8000 tokens max) — prevents prompt overflow regardless of how many chunks are retrieved
  • Circuit breakers on OpenAI and Supabase calls — prevents cascade failures when external services degrade; 5-failure threshold with 30s/15s reset windows
  • Docker Compose with health checks — enables orchestrator-level restart and dependency ordering for future Kubernetes migration

Security Model

  • 🔒Supabase Auth with JWT Bearer tokens — all API endpoints (except health/ping) require valid session
  • 🔒Row-Level Security (RLS) on all 9 database tables — users can only access their own documents, chunks, conversations, and feedback
  • 🔒Role-based access control via profiles.role column — admin endpoints restricted to admin role for user management and system stats
  • 🔒Helmet HTTP security headers — CSP, HSTS, X-Frame-Options configured for Supabase and OpenAI origins
  • 🔒Per-endpoint rate limiting — general (100/min), auth (10/min), chat (20/min) with user-based keys
  • 🔒Input sanitization middleware — strips XSS vectors (script tags, javascript:, event handlers) from all request bodies and query params
  • 🔒Service role key server-side only — Supabase service role key never exposed to the frontend; anon key used for client-side auth only

Technical Case Study

Problem Statement

Enterprise documents — policy manuals, technical specifications, compliance guides, and operational procedures — are locked in PDF format across departments. Employees spend hours manually searching through hundreds of pages to find specific answers. Traditional keyword search returns too many irrelevant results, while asking a colleague means waiting hours or days for a response. The core problem is making institutional knowledge instantly queryable through natural language conversation.

Why This Approach

Fine-tuning a model on company documents was considered but rejected for three reasons: cost (re-training for every document update), freshness (fine-tuned models become stale as documents change), and hallucination control (fine-tuned models can still fabricate answers without source attribution). RAG solves all three — documents can be re-indexed in minutes, the model always grounds answers in retrieved context, and every response includes citations linking back to source chunks. This makes RAG the only viable approach for an enterprise knowledge system where accuracy and traceability are non-negotiable.

Challenges Encountered

  • Docker container-to-host networking — the frontend and backend containers needed proper network configuration to communicate, requiring careful Docker daemon and platform-specific networking setup
  • Chunk size calibration — too small (200 tokens) lost critical context between paragraphs; too large (2000 tokens) diluted retrieval precision; the hierarchical paragraph → sentence → word splitting with 1000-token max and 200-token overlap was the result of testing dozens of configurations
  • Follow-up question handling — "What about the second section?" is meaningless without conversation history; implemented an OpenAI-powered query rewriting step that rewrites follow-ups as standalone questions before retrieval
  • Streaming response persistence — the SSE stream sends tokens in real-time, but the complete answer, citations, and token usage must be persisted after the stream ends, requiring careful async coordination between the stream consumer and database writes
  • Embedding rate limits — indexing large document sets triggers OpenAI rate limits; solved with batch processing (100 chunks/batch), exponential backoff, and the in-process job queue with configurable concurrency

Performance Improvements

  • Hybrid retrieval (60% vector + 40% keyword) improved recall by catching exact-match terms (acronyms, model numbers, proper nouns) that pure vector search misses
  • Query rewriting eliminates the "What about it?" problem — without it, follow-up questions retrieve irrelevant chunks, degrading answer quality by 40-60%
  • Context deduplication and document-ordered assembly prevents the LLM from seeing the same chunk twice and maintains reading order for coherent answers
  • Circuit breakers prevent cascade failures — when OpenAI is slow, the system fails fast with 503 instead of accumulating timeout connections
  • Token-aware context assembly caps the prompt at 8000 tokens regardless of retrieval count, preventing prompt overflow and controlling OpenAI API costs

Lessons Learned

  • 💡Chunking strategy matters more than the embedding model — the same embeddings on poorly chunked text produce worse results than simpler embeddings on well-chunked text
  • 💡Hybrid retrieval is not optional for enterprise use cases — technical documents contain acronyms, model numbers, and proper nouns that embeddings consistently miss
  • 💡Supabase pgvector eliminates the biggest operational complexity — managing a separate vector database, its auth, and its connection pooling is avoided entirely
  • 💡Conversation memory transforms RAG from a search tool to a knowledge assistant — users expect to ask follow-ups without restating context
  • 💡Circuit breakers and structured logging are not nice-to-haves — they are essential for debugging production issues in a system with 3+ external service dependencies

Measurable Results & Impact

The system delivers a complete document-to-conversation pipeline: users upload PDFs, which are automatically extracted, chunked, embedded, and indexed — then ask questions and receive streaming AI answers grounded in their actual documents with source citations. Hybrid retrieval (60% vector + 40% keyword) achieved higher recall than either method alone, catching exact-match terms that embeddings miss. The 13-database-migration schema with RLS on all tables ensures per-user data isolation. Circuit breakers, structured logging, and Prometheus metrics provide production-grade observability. The Dockerized deployment with GitHub Actions CI/CD demonstrates a complete DevOps workflow from development to production readiness.

Key Features Implemented

  • PDF document upload with text extraction and multi-language detection (EN/ES/FR/DE/PT)
  • Intelligent text chunking with paragraph → sentence → word hierarchy and configurable token limits
  • Vector embeddings via OpenAI text-embedding-3-small with batch processing and pgvector storage
  • Hybrid retrieval combining cosine similarity vector search with PostgreSQL tsvector full-text keyword search
  • Streaming AI chat responses via OpenAI Responses API with Server-Sent Events
  • Query rewriting for follow-up question understanding using conversation history
  • Conversation memory with persistent session and message storage
  • Citation extraction linking AI responses back to source document chunks
  • Background job queue with retry logic, concurrency control, and circuit breakers
  • Admin dashboard with user management, system stats, and document oversight
  • Row-Level Security (RLS) on all database tables with role-based access control
  • Prometheus metrics, structured logging, health checks, and rate limiting