<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[digitpatrox]]></title><description><![CDATA[digitpatrox]]></description><link>https://digitpatrox.hashnode.dev</link><image><url>https://cdn.hashnode.com/uploads/logos/6a02cc83937b84f7791fe235/f927d16a-0b95-4ede-9d85-21b92b66f52b.png</url><title>digitpatrox</title><link>https://digitpatrox.hashnode.dev</link></image><generator>RSS for Node</generator><lastBuildDate>Fri, 18 Sep 2026 05:26:31 GMT</lastBuildDate><atom:link href="https://digitpatrox.hashnode.dev/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[How to Build a RAG System with pgvector and LangChain: The Production Architecture]]></title><description><![CDATA[Most production AI failures are not model failures. They are retrieval failures.
If you want to understand why your Retrieval-Augmented Generation (RAG) system is hallucinating, stop looking at your p]]></description><link>https://digitpatrox.hashnode.dev/how-to-build-a-rag-system-with-pgvector-and-langchain-the-production-architecture</link><guid isPermaLink="true">https://digitpatrox.hashnode.dev/how-to-build-a-rag-system-with-pgvector-and-langchain-the-production-architecture</guid><category><![CDATA[AI]]></category><category><![CDATA[Databases]]></category><category><![CDATA[RAG ]]></category><category><![CDATA[langgraph]]></category><category><![CDATA[pgvector]]></category><category><![CDATA[PgVector for RAG models]]></category><dc:creator><![CDATA[Digit Patrox]]></dc:creator><pubDate>Tue, 12 May 2026 06:58:51 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/6a02cc83937b84f7791fe235/32f7a615-ce45-4ce3-8eee-754b4fc3eeb0.webp" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Most production AI failures are not model failures. They are retrieval failures.</p>
<p>If you want to understand why your <strong>Retrieval-Augmented Generation (RAG)</strong> system is hallucinating, stop looking at your prompt. A perfect prompt with the wrong data yields a confident hallucination. An average prompt with the correct data yields a useful answer.</p>
<p>The distance between a database returning a conceptually similar chunk and returning a <em>factually useful</em> chunk is the hardest engineering problem in modern AI. We call it the <strong>Retrieval Gap</strong>.</p>
<p>High semantic similarity does not guarantee factual usefulness. To bridge the gap, you need a hybrid pipeline that combines vector search, BM25 keyword retrieval, reranking, and metadata filtering.</p>
<h2>The RAG Maturity Curve</h2>
<p>Understanding where your architecture currently sits is the only way to anticipate the next bottleneck.</p>
<ul>
<li><p><strong>Stage 1: The Toy.</strong> Jupyter notebook + ChromaDB + 1 PDF. (~10% Retrieval Recall)</p>
</li>
<li><p><strong>Stage 2: Persistent Infrastructure.</strong> Python scripts + PostgreSQL (<code>pgvector</code>) + Docker.</p>
</li>
<li><p><strong>Stage 3: Operational Reality.</strong> FastAPI + PgBouncer + HNSW Indexing.</p>
</li>
<li><p><strong>Stage 4: Hybrid Retrieval.</strong> Vector search + Keyword search (BM25) + Reranking APIs. (~90%+ Retrieval Recall)</p>
</li>
<li><p><strong>Stage 5: The Semantic Microservice.</strong> Autonomous ingestion, Reciprocal Rank Fusion, Semantic Caching, and continuous Recall evaluation.</p>
</li>
</ul>
<p>Production <a href="https://digitpatrox.com/rag-explained-why-retrieval-quality-wins-over-ai-model-size/">RAG systems</a> evolve through multiple infrastructure stages. Retrieval quality improves not through <a href="https://digitpatrox.com/ai-agents-vs-traditional-automation-managing-the-hidden-decay-of-intelligent-systems/">prompt engineering</a> alone, but through indexing, hybrid retrieval, and AI observability.</p>
<hr />
<h2>The Minimal Deployment Topology</h2>
<p>Before diving into the failures, you need to understand where the pieces actually live. A Stage 4/5 production architecture completely separates the orchestration wrapper from the data persistence layer.</p>
<h3>The Stack:</h3>
<ul>
<li><p><strong>Orchestration:</strong> FastAPI + <a href="https://www.google.com/search?q=https://digitpatrox.com/what-is-langchain-and-langgraph-why-ai-agents-need-stateful-orchestration/">LangGraph</a> (Stateless async workers).</p>
</li>
<li><p><strong>Connection Pooler:</strong> PgBouncer (Protects the DB from connection exhaustion).</p>
</li>
<li><p><strong>System of Record:</strong> PostgreSQL + <code>pgvector</code> (Stores metadata, BM25 text, and HNSW embeddings).</p>
</li>
<li><p><strong>Semantic Cache:</strong> Redis (Bypasses the DB for repeated queries).</p>
</li>
<li><p><strong>External Compute:</strong> OpenAI (Embeddings) + Cohere (Cross-Encoder Reranking).</p>
</li>
</ul>
<hr />
<h2>The PDF Lie and Ingestion Contamination</h2>
<p>Most RAG failures happen before the embedding model ever sees the text. Your PDF loader is lying to you. It does not “see” a cleanly formatted manual; it sees a fragmented mess of text, hidden whitespace, and floating footers.</p>
<p>If a document has “Confidential – Internal Use Only” on every page, a naive character splitter will attach that string to every single chunk. Embedding models amplify repeated noise. If your ingestion layer is dirty, retrieval quality collapses.</p>
<pre><code class="language-python">import re
from langchain_text_splitters import RecursiveCharacterTextSplitter

# Remove repeating headers BEFORE chunking to prevent semantic poisoning
cleaned_text = re.sub(r"Confidential - Internal Use Only", "", raw_text)

text_splitter = RecursiveCharacterTextSplitter(
    chunk_size=1000,
    chunk_overlap=200, 
    separators=["\n\n", "\n", " ", ""]
)
</code></pre>
<hr />
<h2>Postmortem: The Night the Index Rebuild Killed Production</h2>
<p>Everyone talks about the magic of vector search. Nobody talks about the database locks.</p>
<p>In <code>pgvector</code>, the distance between vectors is often calculated using <strong>Cosine Distance</strong>. The formula for the distance \(d\) between vectors \(\mathbf{A}\) and \(\mathbf{B}\) is:</p>
<p>$$d(\mathbf{A}, \mathbf{B}) = 1 - \frac{\mathbf{A} \cdot \mathbf{B}}{|\mathbf{A}| |\mathbf{B}|}$$</p>
<p>Six months into a deployment, we hit 5 million vectors. A nightly cron job triggered a mass re-ingestion, which forced the <strong>HNSW (Hierarchical Navigable Small World)</strong> graph to recalculate. HNSW index builds are incredibly RAM and CPU intensive. The rebuild starved the database of resources, locking out our read traffic.</p>
<h3>The Unresolved Tension: HNSW vs. IVFFlat</h3>
<table>
<thead>
<tr>
<th>Index Type</th>
<th>The Strength</th>
<th>The Weakness</th>
<th>Best For</th>
</tr>
</thead>
<tbody><tr>
<td><strong>HNSW</strong></td>
<td>Highest recall, lowest query latency.</td>
<td>Massive RAM footprint, slow index builds.</td>
<td>Read-heavy, static production systems.</td>
</tr>
<tr>
<td><strong>IVFFlat</strong></td>
<td>Fast build times, low RAM overhead.</td>
<td>Lower recall, requires table scans to train.</td>
<td>High-churn datasets, memory-constrained DBs.</td>
</tr>
</tbody></table>
<p>We eventually traded a 3% precision drop (switching to IVFFlat) for database stability. Real engineering is choosing which tradeoff you can survive.</p>
<hr />
<h2>The Latency Budget Breakdown</h2>
<p>Once you move to a Stage 4 architecture (Hybrid Search), you are stacking API calls. Here is what a realistic P95 latency budget looks like:</p>
<table>
<thead>
<tr>
<th>Pipeline Stage</th>
<th>P95 Latency</th>
<th>Operational Note</th>
</tr>
</thead>
<tbody><tr>
<td><strong>Embedding (OpenAI)</strong></td>
<td>120ms</td>
<td>Network dependent.</td>
</tr>
<tr>
<td><strong>pgvector Retrieval</strong></td>
<td>45ms</td>
<td>Assumes warm ANN index.</td>
</tr>
<tr>
<td><strong>Keyword (BM25)</strong></td>
<td>18ms</td>
<td>Standard Postgres FTS.</td>
</tr>
<tr>
<td><strong>Cross-Encoder Rerank</strong></td>
<td>380ms</td>
<td>The heaviest architectural tax.</td>
</tr>
<tr>
<td><strong>LLM Generation</strong></td>
<td>2.4s</td>
<td>Streaming helps perceived latency.</td>
</tr>
</tbody></table>
<hr />
<h2>The Tenant Leakage Problem</h2>
<p>In enterprise RAG, searching the “whole database” is a security incident. A vector search returning another customer’s document is a massive security risk.</p>
<p>You must implement strict metadata filtering <em>before</em> the vector distance is calculated, or rely on Postgres <strong>Row-Level Security (RLS)</strong>.</p>
<pre><code class="language-python"># The Metadata Filter is your security boundary
retriever = db.as_retriever(
    search_kwargs={
        "k": 5,
        "filter": {"tenant_id": current_user.tenant_id} # Mandatory security boundary
    }
)
</code></pre>
<hr />
<h2>How to Measure Retrieval Quality (Evaluation Science)</h2>
<p>You cannot fix what you do not measure. If you judge your RAG system by reading the LLM’s final output, you are flying blind. You must isolate retrieval failure from generation failure.</p>
<p>We measure <strong>Recall@10</strong> (Did the correct, ground-truth chunk appear in the top 10 results?).</p>
<h3>The Benchmark Results:</h3>
<ul>
<li><p><strong>Pure Vector Search:</strong> Recall@10 = 74%.</p>
</li>
<li><p><strong>Hybrid (Vector + BM25) + Reranker:</strong> Recall@10 = 96%.</p>
</li>
</ul>
<p>If your Recall@10 is 74%, that means 1 out of 4 times, the LLM is physically incapable of answering the user’s question because the vector database didn’t hand it the right text.</p>
<hr />
<h2>Scaling with Semantic Caching</h2>
<p>As you scale, you will notice users ask the same questions 80% of the time. Running the embedding model and Postgres query for “How do I reset my password?” is a waste of compute.</p>
<p>By putting a caching layer (like Redis) in front of your pipeline, you can store previous responses keyed to the vector embedding of the question. If a new query has a 0.99 cosine similarity to a cached query, you bypass the entire RAG pipeline and return the result in 15ms.</p>
<p>Models will change. Abstractions like <a href="https://digitpatrox.com/what-is-langchain-and-langgraph/">LangChain</a> will evolve. But the fundamental physics of database connections, latency budgets, and index rebuilds remain the same. The plumbing is what matters.</p>
<p>For the full implementation guide, architecture breakdown, and production deployment walkthrough, read the original article here:</p>
<p><a href="https://digitpatrox.com/how-to-build-a-rag-system-with-pgvector-and-langchain/">How to Build a RAG System with PGVector and LangChain</a></p>
]]></content:encoded></item></channel></rss>