How to build a production RAG application from scratch

Building a prototype RAG script takes twenty lines of code; building one that works reliably in production requires careful document ingestion, chunking boundaries, and vector indexing.

JobsDart Editorial4 min read

Key takeaways

  • A production RAG architecture consists of four distinct stages: parsing, chunking, indexing, and synthesis.
  • Embeddings convert semantic meaning into high-dimensional geometric coordinates where related concepts cluster together.
  • Fixed character splitting is the leading cause of low retrieval accuracy; semantic and markdown-aware chunking preserve thought integrity.
  • PostgreSQL with pgvector provides production-grade vector storage without needing a separate standalone vector cluster.
  • Always separate retrieval evaluation from generation evaluation to identify whether errors stem from missing chunks or model hallucination.

The core architecture: the four invariant steps of RAG

At its architectural core, Retrieval-Augmented Generation (RAG) is straightforward: it bridges the gap between what an LLM memorized during training and the proprietary or dynamic documents you want it to know.

While tutorials often combine the process into a single library call, a production-grade system decouples into four distinct, independent stages: Document Ingestion & Cleaning, Chunking & Semantic Splitting, Vector Embedding & Storage, and the Retrieval & Generation Loop.

Understanding how data flows between these stages is what separates fragile hackathon prototypes from resilient enterprise software.

Step 1 & 2: Parsing, cleaning, and chunking strategy

The ingestion stage converts raw documents — such as PDFs, markdown files, HTML pages, or database records — into clean plain text. PDFs are particularly treacherous: headers, footers, page numbers, and multi-column layouts frequently corrupt sentence flow if parsed with basic text extractors.

Once text is cleaned, it must be chunked. A common beginner mistake is splitting text every 500 characters. If a critical definition starts at character 480 and finishes at character 550, the definition is cleaved in two, destroying its semantic vector representation.

Modern RAG architectures use recursive character splitting or semantic boundary chunking. They split by markdown headers (`#`, `##`), then by double line breaks (paragraphs), and finally by sentence punctuation. This ensures each chunk represents a complete, cohesive thought.

  • Target chunk sizes between 250 and 500 tokens for optimal embedding model density
  • Incorporate a 10% to 15% sliding window overlap between chunks to prevent boundary information loss
  • Attach metadata (document ID, section title, page number, created date) to every chunk for query-time filtering
  • Filter out boilerplate: remove repetitive copyright notices and navigation menus prior to embedding
Common chunking strategies compared
StrategyBest ForProsCons
Fixed-Token SplittingRaw unformatted textSimple and fastSplits sentences and paragraphs mid-thought
Markdown / Header-AwareTechnical docs, wikis, blogsPreserves hierarchical section contextRequires structured markdown source
Semantic ChunkingNarrative prose, transcriptsGroups text by semantic shiftRequires additional embedding calls during ingestion
Parent-Document RetrievalComplex legal & academic papersPrecise search with wide contextRequires managing dual-layer chunk hierarchies

Step 3: Generating embeddings and vector indexing with pgvector

An embedding model takes a text chunk and transforms it into a vector — an array of floating-point numbers (e.g. 1,536 dimensions) representing its location in semantic space. Words and phrases with similar meanings end up geometrically close to one another.

To store and search these vectors, you do not need an esoteric, expensive cloud vector service. PostgreSQL with the `pgvector` extension is one of the most reliable and battle-tested vector stores available.

With pgvector, you store your text chunks, metadata, and embeddings in standard relational tables. You can index vectors using HNSW (Hierarchical Navigable Small World) for sub-millisecond approximate nearest neighbor searches, while combining vector similarity with standard SQL `WHERE` clauses.

Step 4: The retrieval, prompt construction, and synthesis loop

When a user submits a question, the application passes the prompt through the identical embedding model used during ingestion to obtain a query vector.

The system searches the vector database for the top-k (usually 3 to 5) most similar chunks. These chunks are formatted into a clean context block and injected into the LLM system prompt alongside strict grounding instructions.

The system prompt explicitly commands the model: "Answer the question using ONLY the provided context. If the answer cannot be determined from the context, state that the information is unavailable."

  • Always include citation instructions: require the model to cite chunk indices or source document titles
  • Stream LLM output tokens to the client to keep perceived latency low
  • Log the retrieved chunks alongside user questions to monitor retrieval quality over time
  • Implement defensive prompt guardrails to prevent user prompts from overriding retrieval constraints

Frequently asked questions

How do you build a RAG application from scratch?

To build RAG from scratch: 1) Parse and extract text from your source documents, 2) Split text into semantic chunks with overlap, 3) Generate vector embeddings using an embedding model and store them in a vector database, and 4) On user query, embed the question, retrieve the top matching chunks, and inject them into an LLM prompt.

What are the main components of a RAG system?

The four main components are: 1) Document Parser (extracts text), 2) Embedding Model (converts text to vectors), 3) Vector Database (indexes and retrieves vectors), and 4) Generative LLM (synthesizes answers from retrieved context).

Do you need a specialized vector database to build RAG?

No. You can build high-performance production RAG using PostgreSQL with the open-source pgvector extension, SQLite with sqlite-vec, or in-memory search libraries like Faiss.

What is the ideal chunk size for RAG?

For most general English text, chunk sizes between 300 and 500 tokens with a 50-token overlap provide the best balance between semantic specificity and surrounding narrative context.

Why should I separate retrieval evaluation from answer evaluation?

If your RAG system gives a wrong answer, it could be because the retrieval failed to find the right chunk, or because the LLM hallucinated despite having the right chunk. Measuring both independently tells you whether to tune your search or adjust your prompt.

Further reading

Check this against your own resume

Scan your CV against a real job description, or build a parse-safe one from scratch. Your first scan costs nothing.

Keep reading

Referenced in these guides

All career guides