<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Nagachaitanya Inamdar</title>
    <description>The latest articles on DEV Community by Nagachaitanya Inamdar (@nagachaitanya_inamdar_56c).</description>
    <link>https://dev.to/nagachaitanya_inamdar_56c</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3803966%2Fb9d5dc19-454b-4da3-bb61-8fcb798d743f.png</url>
      <title>DEV Community: Nagachaitanya Inamdar</title>
      <link>https://dev.to/nagachaitanya_inamdar_56c</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/nagachaitanya_inamdar_56c"/>
    <language>en</language>
    <item>
      <title>The AI Brain: SageMaker BGE Cross-Encoder Reranking &amp; Sub-100ms Bedrock ConverseStream SSE (Part 4)</title>
      <dc:creator>Nagachaitanya Inamdar</dc:creator>
      <pubDate>Sun, 08 Nov 2026 05:50:33 +0000</pubDate>
      <link>https://dev.to/nagachaitanya_inamdar_56c/the-ai-brain-sagemaker-bge-cross-encoder-reranking-sub-100ms-bedrock-conversestream-sse-part-4-2pm3</link>
      <guid>https://dev.to/nagachaitanya_inamdar_56c/the-ai-brain-sagemaker-bge-cross-encoder-reranking-sub-100ms-bedrock-conversestream-sse-part-4-2pm3</guid>
      <description>&lt;p&gt;In &lt;a href="https://dev.to/"&gt;Part 3 of this series&lt;/a&gt;, we built our Storage Vault in PostgreSQL 16: co-locating dense &lt;code&gt;pgvector&lt;/code&gt; embeddings and full-text BM25 indexes, using Reciprocal Rank Fusion (RRF) to merge candidate chunks into a unified pool of 15 relevant documents.&lt;/p&gt;

&lt;p&gt;At this stage, most engineering teams stop and immediately forward all 15 chunks into their Large Language Model (LLM).&lt;/p&gt;

&lt;p&gt;Doing that introduces three major production hazards:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;The "Lost in the Middle" Effect:&lt;/strong&gt; Research shows LLM recall degrades significantly when the most relevant facts are buried in the middle of a massive 15-chunk prompt context.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Context Bloat &amp;amp; Cost:&lt;/strong&gt; Sending 15 chunks (roughly 6,000–8,000 tokens) on every user turn drives up token inference costs and increases latency.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The Bi-Encoder Ceiling:&lt;/strong&gt; Even after RRF, vector similarity scores often fail to capture subtle semantic nuances, leading the LLM to hallucinate or generate vague answers.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;To achieve production reliability, an enterprise platform requires a dedicated &lt;strong&gt;Stage-2 Precision Reranker&lt;/strong&gt;. &lt;/p&gt;

&lt;p&gt;In this article, we cover:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;The Architecture Ceiling:&lt;/strong&gt; Why Bi-Encoders fail and how Cross-Encoders achieve 0.9970 confidence.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The Two-Stage Serving Architecture:&lt;/strong&gt; Reducing 100,000 documents to 3 pristine chunks in under 60ms.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Production SageMaker Deployment:&lt;/strong&gt; Running &lt;code&gt;BAAI/bge-reranker-base&lt;/code&gt; on provisioned &lt;code&gt;ml.m5.large&lt;/code&gt; for deterministic sub-50ms inference.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Real-World Benchmark Results:&lt;/strong&gt; How cross-encoders rescued ambiguous queries in our live DocLab tests.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Streaming Generation:&lt;/strong&gt; Delivering real-time Server-Sent Events (SSE) tokens via AWS Bedrock ConverseStream with sub-400ms Time-to-First-Token (TTFT).&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  1. The Accuracy Ceiling: Bi-Encoder vs Cross-Encoder
&lt;/h2&gt;

&lt;p&gt;To understand why a reranker is necessary, we must examine the difference between &lt;strong&gt;Bi-Encoders&lt;/strong&gt; and &lt;strong&gt;Cross-Encoders&lt;/strong&gt;:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fvikd1bh3rbcrkavnkenw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fvikd1bh3rbcrkavnkenw.png" alt="BI-ENCODER VS CROSS-ENCODER ARCHITECTURE" width="785" height="680"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  How Bi-Encoders Work (Stage 1)
&lt;/h3&gt;

&lt;p&gt;All vector embedding models (Amazon Titan v2, OpenAI text-embedding-3, Cohere) are &lt;strong&gt;Bi-Encoders&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The User Query $Q$ is passed through the neural network independently to generate vector $E(Q)$.&lt;/li&gt;
&lt;li&gt;The Document Passage $D$ is passed through the neural network independently during ingestion to generate vector $E(D)$.&lt;/li&gt;
&lt;li&gt;At retrieval time, the system computes the cosine dot product: $\text{Sim}(Q, D) = E(Q) \cdot E(D)$.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;The Fundamental Flaw:&lt;/strong&gt; Query tokens and document tokens &lt;strong&gt;never interact&lt;/strong&gt; in the neural network's attention layers. The model compresses an entire 512-token document into a single fixed vector. Nuanced relationships (e.g., subtle conditionality, negation, or multi-step logic) are lost during compression.&lt;/p&gt;

&lt;h3&gt;
  
  
  How Cross-Encoders Work (Stage 2)
&lt;/h3&gt;

&lt;p&gt;A Cross-Encoder (such as &lt;code&gt;BAAI/bge-reranker-base&lt;/code&gt;) does not produce isolated vector coordinates. Instead, it passes both the Query and the Candidate Chunk together as a single input pair:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Input: [CLS] User Query [SEP] Document Passage [EOS]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Inside the Cross-Encoder's transformer layers:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Full Cross-Attention:&lt;/strong&gt; Every single token in the query performs self-attention with every single token in the document passage.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Deep Alignment:&lt;/strong&gt; The model evaluates how words in the query depend on words in the document, capturing semantic subtleties that Bi-Encoders miss entirely.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Output:&lt;/strong&gt; A single calibrated relevance probability between &lt;code&gt;0.0000&lt;/code&gt; and &lt;code&gt;1.0000&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  2. The Two-Stage Serving Pattern: Cost vs Precision
&lt;/h2&gt;

&lt;p&gt;If Cross-Encoders are vastly superior, why not use them for the entire search pipeline?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Compute Complexity:&lt;/strong&gt; Running full cross-attention across 100,000 documents for a single user query would take dozens of seconds and require tens of thousands of dollars in GPU compute.&lt;/p&gt;

&lt;p&gt;The industry gold standard is the &lt;strong&gt;Two-Stage Retrieval Pattern&lt;/strong&gt;:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fnf3toxwhhew0p1qehc5p.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fnf3toxwhhew0p1qehc5p.png" alt="TWO-STAGE RETRIEVAL &amp;amp; SSE STREAMING PIPELINE" width="800" height="144"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Stage 1 (High Recall, Low Cost):&lt;/strong&gt; PostgreSQL 16 executes hybrid HNSW cosine + BM25 tsvector search, using Reciprocal Rank Fusion (RRF) to filter 100,000+ candidates down to the &lt;strong&gt;Top 15 candidates&lt;/strong&gt; in &lt;strong&gt;~15ms&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Stage 2 (High Precision, Deep Attention):&lt;/strong&gt; AWS SageMaker evaluates those 15 candidate pairs in parallel, returning the &lt;strong&gt;Top 3 chunks&lt;/strong&gt; with calibrated confidence scores (&amp;gt;0.99) in &lt;strong&gt;~40ms&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Total retrieval time: &lt;strong&gt;under 60ms&lt;/strong&gt; with zero loss in precision!&lt;/p&gt;




&lt;h2&gt;
  
  
  3. Deploying &lt;code&gt;BAAI/bge-reranker-base&lt;/code&gt; on AWS SageMaker
&lt;/h2&gt;

&lt;p&gt;For our real-time reranking engine, we deployed the state-of-the-art open-source &lt;code&gt;BAAI/bge-reranker-base&lt;/code&gt; model onto an &lt;strong&gt;AWS SageMaker Real-Time Endpoint&lt;/strong&gt; named &lt;code&gt;bge-reranker-endpoint&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F8x6j56hnf4jwyzcje17d.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F8x6j56hnf4jwyzcje17d.png" alt="SAGEMAKER REAL-TIME ENDPOINT ARCHITECTURE" width="447" height="1406"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Why Provisioned &lt;code&gt;ml.m5.large&lt;/code&gt; Over Serverless?
&lt;/h3&gt;

&lt;p&gt;Many engineers attempt to deploy SageMaker Serverless Inference to cut baseline idle costs. In production RAG platforms, this is a dangerous trap:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Serverless Cold Starts:&lt;/strong&gt; PyTorch container initialization and model weight loading introduce &lt;strong&gt;5 to 10 second cold-start delays&lt;/strong&gt; when endpoints scale from zero.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Provisioned Determinism:&lt;/strong&gt; An &lt;code&gt;ml.m5.large&lt;/code&gt; provisioned instance costs approximately ~$0.115/hour (~$80/month). In return, it guarantees &lt;strong&gt;deterministic sub-50ms inference latency&lt;/strong&gt; under all traffic spikes, with zero cold starts.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  The SageMaker Endpoint Input/Output Contract
&lt;/h3&gt;

&lt;p&gt;DocLab communicates with SageMaker using a compact JSON contract:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Request Payload:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"query"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"What are the scaling policies defined for checkout-service?"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"texts"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"Chunk A: Service catalog for checkout-service specifying horizontal pod autoscaler..."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"Chunk B: Payment gateway timeout and retry threshold definitions..."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"Chunk C: ADR-025 Ingestion Factory architectural decision record..."&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;SageMaker Response Payload:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"index"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"score"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;0.9970&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"index"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"score"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;0.4120&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"index"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"score"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;0.0381&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The Spring Boot backend immediately sorts the results by score descending and extracts the top 3 chunks (filtering out any chunk with confidence below a configurable threshold, such as 0.60).&lt;/p&gt;




&lt;h2&gt;
  
  
  4. Real-World Benchmark Results from Live AWS Infrastructure
&lt;/h2&gt;

&lt;p&gt;We executed live comparative benchmark evaluations on our deployed DocLab architecture. &lt;/p&gt;

&lt;p&gt;The test corpus consisted of diverse enterprise files: Architectural Decision Records (ADRs), Excel sheets, and complex JSON service catalogs.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Evaluation Metric&lt;/th&gt;
&lt;th&gt;Pure Dense (Titan v2)&lt;/th&gt;
&lt;th&gt;Pure Sparse (BM25)&lt;/th&gt;
&lt;th&gt;Hybrid (RRF k=60)&lt;/th&gt;
&lt;th&gt;Two-Stage (RRF + SageMaker BGE)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;ADR-025 Decision Query&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;0.8841 (Rank #1)&lt;/td&gt;
&lt;td&gt;0.0000 (Miss)&lt;/td&gt;
&lt;td&gt;0.01639 (Rank #2)&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;0.9970 (Confidence)&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;ADR-025 Consequences&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;0.8120 (Rank #2)&lt;/td&gt;
&lt;td&gt;12.41 (Rank #1)&lt;/td&gt;
&lt;td&gt;0.03226 (Rank #1)&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;0.9957 (Confidence)&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Technical JSON Schema&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;0.7910 (Rank #3)&lt;/td&gt;
&lt;td&gt;0.0000 (Miss)&lt;/td&gt;
&lt;td&gt;0.01587 (Rank #3)&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;0.6219 (Promoted to #1)&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Candidate Reduction&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;N/A&lt;/td&gt;
&lt;td&gt;N/A&lt;/td&gt;
&lt;td&gt;15 candidates&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;3 pristine chunks&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Retrieval Stage Latency&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;~8ms&lt;/td&gt;
&lt;td&gt;~3ms&lt;/td&gt;
&lt;td&gt;~14ms&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;~54ms total&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Key Findings:
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Confidence Calibration:&lt;/strong&gt; Pure dense cosine similarities often cluster tightly between 0.75 and 0.85, making it impossible to set an absolute threshold. The SageMaker Cross-Encoder provided crystal-clear discrimination: true answers scored &lt;strong&gt;&lt;code&gt;&amp;gt; 0.99&lt;/code&gt;&lt;/strong&gt;, while irrelevant chunks plummeted to &lt;strong&gt;&lt;code&gt;&amp;lt; 0.05&lt;/code&gt;&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Context Compression:&lt;/strong&gt; By feeding only the top 3 high-confidence chunks to the LLM instead of 15 noisy chunks, we reduced prompt token consumption by &lt;strong&gt;72%&lt;/strong&gt; and completely eliminated the "Lost in the Middle" hallucination phenomenon.&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  5. Streaming Generation via AWS Bedrock ConverseStream
&lt;/h2&gt;

&lt;p&gt;With our top 3 chunks validated, we construct our guarded prompt and initiate streaming inference via &lt;strong&gt;Amazon Bedrock ConverseStream&lt;/strong&gt;:&lt;/p&gt;

&lt;h3&gt;
  
  
  The Fail-Closed Guardrail Prompt
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;System:
You are an enterprise AI assistant for DocLab. 
Rules:
1. Answer strictly and exclusively using the provided context chunks below.
2. If the answer cannot be found in the context, explicitly say:
   "I cannot answer this question based on the verified enterprise documentation."
3. Cite the Chunk ID for every factual assertion made.
4. Do not speculate or extrapolate beyond the text.

Context Chunks:
[Chunk #1 - Confidence: 0.9970]
Service Catalog: checkout-service auto-scales when CPU &amp;gt; 75%...

User Query:
What is the scaling policy for checkout-service?
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Low-Latency Server-Sent Events (SSE)
&lt;/h3&gt;

&lt;p&gt;Instead of forcing the user to wait 4–6 seconds for the full response to generate, DocLab exposes a streaming endpoint:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;GET /api/v1/chat/stream?query=...&amp;amp;allowedRoles=ROLE_ANALYST
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Spring Boot bridges Bedrock's asynchronous reactive stream directly to the client's browser using standard HTTP &lt;code&gt;text/event-stream&lt;/code&gt; (Server-Sent Events):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;event: delta
data: {"token": "The "}

event: delta
data: {"token": "checkout-service "}

event: delta
data: {"token": "scales "}

event: delta
data: {"token": "automatically..."}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  End-to-End Latency Waterfall
&lt;/h3&gt;

&lt;p&gt;Here is the measured latency breakdown on live AWS infrastructure:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;┌──────────────────────────────────────────────┐
│ Stage 1: PostgreSQL Hybrid RRF (15ms)        │
├──────────────────────────────────────────────┤
│ Stage 2: SageMaker BGE Reranker (40ms)       │
├──────────────────────────────────────────────┤
│ Bedrock TTFT (Time to First Token) (325ms)   │
└──────────────────────────────────────────────┘
Total Time-to-First-Token on Screen: ~380ms
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To the end-user, the chat interface feels instantaneous. Words begin appearing on screen in under 400 milliseconds, fully backed by verified, cited enterprise documentation.&lt;/p&gt;




&lt;h2&gt;
  
  
  6. What's Next in Part 5 (The Grand Finale)?
&lt;/h2&gt;

&lt;p&gt;Our platform now parses multi-format files at zero cost, stores them in a unified hybrid vault, reranks candidates to 0.9970 confidence, and streams responses in under 400ms.&lt;/p&gt;

&lt;p&gt;In &lt;strong&gt;Part 5 (The Grand Finale)&lt;/strong&gt;, we tie the entire enterprise platform together:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Zero-Trust Cache Partitioning:&lt;/strong&gt; Implementing ElastiCache Redis multi-tenant session isolation.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Fail-Closed Guardrails:&lt;/strong&gt; Catching prompt injections and preventing data exfiltration.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The Complete Retrospective:&lt;/strong&gt; Architectural trade-offs, budget review (running this on under $200 in AWS credits), and key takeaways for enterprise architects.&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;em&gt;Built with AWS Bedrock, Amazon SageMaker, Amazon RDS PostgreSQL 16 pgvector, and Spring Boot.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>aws</category>
      <category>sagemaker</category>
      <category>architecture</category>
      <category>rag</category>
    </item>
    <item>
      <title>The Storage &amp; Hybrid Vault: Dense pgvector + Sparse BM25 + Mathematical Reciprocal Rank Fusion (Part 3)</title>
      <dc:creator>Nagachaitanya Inamdar</dc:creator>
      <pubDate>Sun, 08 Nov 2026 05:21:51 +0000</pubDate>
      <link>https://dev.to/nagachaitanya_inamdar_56c/the-storage-hybrid-vault-dense-pgvector-sparse-bm25-mathematical-reciprocal-rank-fusion-2b73</link>
      <guid>https://dev.to/nagachaitanya_inamdar_56c/the-storage-hybrid-vault-dense-pgvector-sparse-bm25-mathematical-reciprocal-rank-fusion-2b73</guid>
      <description>&lt;p&gt;In &lt;a href="https://dev.to/nagachaitanya_inamdar_56c/the-ingestion-factory-decoupling-document-uploads-with-sqs-claim-check-100-in-memory-parsers-2lln"&gt;Part 2 of this series&lt;/a&gt;, we built our asynchronous Ingestion Factory, transforming raw files into canonical JSON representations and queuing them for vector activation.&lt;/p&gt;

&lt;p&gt;Now comes the critical question that determines whether an enterprise RAG system succeeds or hallucinates in production: &lt;strong&gt;How do we store and retrieve knowledge accurately?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Most introductory tutorials tell you to spin up a standalone vector database (Pinecone, Qdrant, Milvus), compute cosine distance, and return the top 5 matches. &lt;/p&gt;

&lt;p&gt;In this article, we’ll explore:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Why &lt;strong&gt;pure dense vector search silently fails&lt;/strong&gt; on enterprise queries.&lt;/li&gt;
&lt;li&gt;Why &lt;strong&gt;co-locating dense vectors and full-text search inside PostgreSQL 16&lt;/strong&gt; beats external vector clusters.&lt;/li&gt;
&lt;li&gt;How &lt;strong&gt;Reciprocal Rank Fusion (RRF)&lt;/strong&gt; mathematically combines sparse keywords and semantic vectors into a unified retrieval engine.&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  1. The Fatal Blind Spot of Pure Dense Vector Search
&lt;/h2&gt;

&lt;p&gt;Dense embedding models (like Amazon Titan Text Embeddings v2, OpenAI text-embedding-3, or Cohere Embed) compress text into high-dimensional geometric spaces (e.g., 1,024 float dimensions). They are exceptional at matching &lt;strong&gt;semantic meaning&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Query: &lt;em&gt;"How do we increase system capacity during flash sales?"&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;Matches Chunk: &lt;em&gt;"Auto-scaling policies dynamically provision read replicas under load."&lt;/em&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Even though neither "capacity" nor "flash sales" appeared in the document, vector similarity correctly scored it high.&lt;/p&gt;

&lt;h3&gt;
  
  
  Where Dense Search Collapses: Exact Identifiers
&lt;/h3&gt;

&lt;p&gt;Enterprise systems run on precision: part numbers, customer UUIDs, Kubernetes configuration keys, and exact error codes.&lt;/p&gt;

&lt;p&gt;Consider this user query:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;"What is the scaling policy defined for checkout-service in microservices.json?"&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;To an embedding model, &lt;code&gt;checkout-service&lt;/code&gt; is broken down into sub-word tokens. The geometric distance between &lt;code&gt;checkout-service&lt;/code&gt; and &lt;code&gt;payment-gateway&lt;/code&gt; or &lt;code&gt;inventory-service&lt;/code&gt; is minuscule—they all inhabit the same semantic cluster of "e-commerce backend components." &lt;/p&gt;

&lt;p&gt;As a result, pure vector search frequently retrieves the &lt;em&gt;wrong&lt;/em&gt; service configuration with high confidence.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Inverse Problem: BM25 Lexical Fragility
&lt;/h3&gt;

&lt;p&gt;Conversely, pure keyword search (BM25) fails when users ask questions using synonyms:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Query: &lt;em&gt;"steps to resolve connection drops"&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;Document: &lt;em&gt;"remediation procedure for transient socket reset errors"&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;BM25 Score: 0.0000 (Zero matches)&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Neither pure dense search nor pure sparse search is sufficient on its own. Enterprise retrieval requires &lt;strong&gt;Hybrid Search&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  2. The Unified Vault: Why PostgreSQL 16 Beats Standalone Vector DBs
&lt;/h2&gt;

&lt;p&gt;When teams decide to implement hybrid search, they often create an architectural mess:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A relational database (PostgreSQL) for business metadata.&lt;/li&gt;
&lt;li&gt;A standalone vector database (Pinecone/Milvus) for embeddings.&lt;/li&gt;
&lt;li&gt;An Elasticsearch / OpenSearch cluster for BM25 text queries.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ffjb7j6nenfc8yqhyu4ki.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ffjb7j6nenfc8yqhyu4ki.png" alt="Dual-Lane Hybrid Retrieval Architecture" width="799" height="711"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Maintaining three distributed clusters creates three severe operational penalties:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Distributed Dual-Write Inconsistency:&lt;/strong&gt; If a document is updated or deleted in PostgreSQL but the sync worker dies before updating Pinecone, your RAG pipeline serves ghost data.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Network Hop Latency:&lt;/strong&gt; Orchestrating round-trip network calls across three distinct services adds 80–150ms of network overhead per user query.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cluster Cost Churn:&lt;/strong&gt; Running dedicated managed search instances when your workload easily fits on existing database infrastructure is an unnecessary budget drain.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Co-Locating Dense &amp;amp; Sparse in PostgreSQL 16 &lt;code&gt;pgvector&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;PostgreSQL 16 combined with the official &lt;code&gt;pgvector&lt;/code&gt; extension natively hosts both search paradigms in a &lt;strong&gt;single table&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;document_chunks&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;chunk_id&lt;/span&gt;        &lt;span class="n"&gt;UUID&lt;/span&gt; &lt;span class="k"&gt;PRIMARY&lt;/span&gt; &lt;span class="k"&gt;KEY&lt;/span&gt; &lt;span class="k"&gt;DEFAULT&lt;/span&gt; &lt;span class="n"&gt;gen_random_uuid&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="n"&gt;doc_id&lt;/span&gt;          &lt;span class="n"&gt;UUID&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;version&lt;/span&gt;         &lt;span class="nb"&gt;INTEGER&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;chunk_index&lt;/span&gt;     &lt;span class="nb"&gt;INTEGER&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;content&lt;/span&gt;         &lt;span class="nb"&gt;TEXT&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;

    &lt;span class="c1"&gt;-- Dense 1024-dimensional semantic embedding&lt;/span&gt;
    &lt;span class="n"&gt;embedding&lt;/span&gt;       &lt;span class="n"&gt;vector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1024&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;

    &lt;span class="c1"&gt;-- Sparse lexical full-text search vector (Generated automatically)&lt;/span&gt;
    &lt;span class="n"&gt;tsv&lt;/span&gt;             &lt;span class="n"&gt;tsvector&lt;/span&gt; &lt;span class="k"&gt;GENERATED&lt;/span&gt; &lt;span class="n"&gt;ALWAYS&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
                        &lt;span class="n"&gt;to_tsvector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'english'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                    &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;STORED&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;

    &lt;span class="c1"&gt;-- Multi-tenant RBAC roles array&lt;/span&gt;
    &lt;span class="n"&gt;allowed_roles&lt;/span&gt;   &lt;span class="nb"&gt;TEXT&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;

    &lt;span class="c1"&gt;-- Atomic activation status&lt;/span&gt;
    &lt;span class="n"&gt;status&lt;/span&gt;          &lt;span class="nb"&gt;VARCHAR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt; &lt;span class="k"&gt;DEFAULT&lt;/span&gt; &lt;span class="s1"&gt;'PENDING'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;created_at&lt;/span&gt;      &lt;span class="n"&gt;TIMESTAMPTZ&lt;/span&gt; &lt;span class="k"&gt;DEFAULT&lt;/span&gt; &lt;span class="n"&gt;NOW&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  High-Performance Indexes Side-by-Side
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;HNSW Vector Index (pgvector):&lt;/strong&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;   &lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;INDEX&lt;/span&gt; &lt;span class="n"&gt;idx_chunks_hnsw_cosine&lt;/span&gt; 
   &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;document_chunks&lt;/span&gt; 
   &lt;span class="k"&gt;USING&lt;/span&gt; &lt;span class="n"&gt;hnsw&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;embedding&lt;/span&gt; &lt;span class="n"&gt;vector_cosine_ops&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
   &lt;span class="k"&gt;WITH&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;m&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;16&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ef_construction&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;64&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;HNSW (Hierarchical Navigable Small World) builds a multi-layer graph index that enables sub-10ms approximate nearest neighbor search without sequential table scans.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;GIN Inverted Index (Full-Text Search):&lt;/strong&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;   &lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;INDEX&lt;/span&gt; &lt;span class="n"&gt;idx_chunks_tsv_gin&lt;/span&gt; 
   &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;document_chunks&lt;/span&gt; 
   &lt;span class="k"&gt;USING&lt;/span&gt; &lt;span class="n"&gt;gin&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tsv&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;GIN (Generalized Inverted Index) maps every stemmed word to its physical row locations for instantaneous boolean and keyword lookups.&lt;/p&gt;




&lt;h2&gt;
  
  
  3. The Dual-Lane Hybrid Retrieval Architecture
&lt;/h2&gt;

&lt;p&gt;When a user submits a question to DocLab, the retrieval engine executes two concurrent query lanes against the same PostgreSQL database:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fmj1zhn98dh2flzek2nkc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fmj1zhn98dh2flzek2nkc.png" alt="PostgreSQL 16 Unified Dual-Engine Schema" width="735" height="1671"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Lane A: Dense Cosine Similarity
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;chunk_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
       &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;embedding&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;queryEmbedding&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;dense_score&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;document_chunks&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'ACTIVE'&lt;/span&gt;
  &lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="n"&gt;allowed_roles&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;userRoles&lt;/span&gt;
&lt;span class="k"&gt;ORDER&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;embedding&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;queryEmbedding&lt;/span&gt; &lt;span class="k"&gt;ASC&lt;/span&gt;
&lt;span class="k"&gt;LIMIT&lt;/span&gt; &lt;span class="mi"&gt;15&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;(Notice the pre-filtering: &lt;code&gt;allowed_roles &amp;amp;&amp;amp; :userRoles&lt;/code&gt; enforces zero-trust security before similarity is calculated).&lt;/em&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Lane B: Sparse BM25-Equivalent Lexical Search
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;chunk_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
       &lt;span class="n"&gt;ts_rank_cd&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tsv&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;plainto_tsquery&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'english'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;userQuery&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;bm25_score&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;document_chunks&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'ACTIVE'&lt;/span&gt;
  &lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="n"&gt;allowed_roles&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;userRoles&lt;/span&gt;
  &lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="n"&gt;tsv&lt;/span&gt; &lt;span class="o"&gt;@@&lt;/span&gt; &lt;span class="n"&gt;plainto_tsquery&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'english'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;userQuery&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;ORDER&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;bm25_score&lt;/span&gt; &lt;span class="k"&gt;DESC&lt;/span&gt;
&lt;span class="k"&gt;LIMIT&lt;/span&gt; &lt;span class="mi"&gt;15&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  4. Fusing the Lanes: Mathematical Reciprocal Rank Fusion (RRF)
&lt;/h2&gt;

&lt;p&gt;We now have two candidate lists with incompatible scoring scales:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Dense scores are cosine similarities between &lt;code&gt;-1.0&lt;/code&gt; and &lt;code&gt;+1.0&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Sparse scores are &lt;code&gt;ts_rank_cd&lt;/code&gt; relevance weights between &lt;code&gt;0.0&lt;/code&gt; and unbounded positives.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;You cannot simply average these raw scores together.&lt;/strong&gt; A single BM25 outlier would completely overpower cosine distances.&lt;/p&gt;

&lt;p&gt;Instead of score-averaging, we implement &lt;strong&gt;Reciprocal Rank Fusion (RRF)&lt;/strong&gt;, an algorithm proven in information retrieval research:&lt;/p&gt;

&lt;p&gt;$$\text{RRF}(d) = \sum_{m \in M} \frac{1}{k + r_m(d)}$$&lt;/p&gt;

&lt;p&gt;Where:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;$M$ is the set of retrieval lanes (Dense and Sparse).&lt;/li&gt;
&lt;li&gt;$r_m(d)$ is the rank position (1, 2, 3...) of document $d$ within lane $m$. If the document did not appear in that lane, its contribution is $0$.&lt;/li&gt;
&lt;li&gt;$k$ is a smoothing constant, conventionally set to $60$ to prevent high ranks from creating extreme score cliffs.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fzpaznlobc2htqnfr6ud2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fzpaznlobc2htqnfr6ud2.png" alt="Mathematical Reciprocal Rank Fusion" width="799" height="536"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  A Concrete Production Example
&lt;/h3&gt;

&lt;p&gt;In our benchmark runs on a complex system architecture catalog:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Candidate Chunk&lt;/th&gt;
&lt;th&gt;Dense Rank&lt;/th&gt;
&lt;th&gt;Dense Score&lt;/th&gt;
&lt;th&gt;Sparse Rank&lt;/th&gt;
&lt;th&gt;BM25 Score&lt;/th&gt;
&lt;th&gt;RRF Calculation ($k=60$)&lt;/th&gt;
&lt;th&gt;Final Score&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;&lt;code&gt;checkout-service&lt;/code&gt; Schema&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;#3&lt;/td&gt;
&lt;td&gt;0.791&lt;/td&gt;
&lt;td&gt;#1&lt;/td&gt;
&lt;td&gt;14.21&lt;/td&gt;
&lt;td&gt;$\frac{1}{60+3} + \frac{1}{60+1} = 0.01587 + 0.01639$&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;0.03226 (Rank #1)&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;ADR-025 System Overview&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;#1&lt;/td&gt;
&lt;td&gt;0.884&lt;/td&gt;
&lt;td&gt;Unranked&lt;/td&gt;
&lt;td&gt;0.000&lt;/td&gt;
&lt;td&gt;$\frac{1}{60+1} + 0 = 0.01639$&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;0.01639 (Rank #2)&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Auto-Scaling Runbook&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Unranked&lt;/td&gt;
&lt;td&gt;&amp;lt; 0.60&lt;/td&gt;
&lt;td&gt;#2&lt;/td&gt;
&lt;td&gt;9.84&lt;/td&gt;
&lt;td&gt;$0 + \frac{1}{60+2} = 0.01613$&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;0.01613 (Rank #3)&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Why This is Powerful:
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Agreement Promotion:&lt;/strong&gt; &lt;code&gt;checkout-service&lt;/code&gt; appeared in both pools and was promoted to &lt;strong&gt;Rank #1&lt;/strong&gt;, because both search lanes agreed on its relevance.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Dense Rescue:&lt;/strong&gt; &lt;code&gt;ADR-025&lt;/code&gt; completely failed BM25 because the query used different phrasing, but the Dense vector rescued it into &lt;strong&gt;Rank #2&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Keyword Rescue:&lt;/strong&gt; The exact runbook missed the semantic cutoff but had the exact error code, so BM25 rescued it into &lt;strong&gt;Rank #3&lt;/strong&gt;.&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  5. What's Next in Part 4?
&lt;/h2&gt;

&lt;p&gt;RRF produces an exceptional, highly resilient candidate pool (Top 15 chunks). However, &lt;strong&gt;Bi-Encoder embeddings calculate similarity independently&lt;/strong&gt; without cross-attending between the query and candidate passages.&lt;/p&gt;

&lt;p&gt;In &lt;strong&gt;Part 4&lt;/strong&gt;, we will introduce the ultimate precision layer:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Why Bi-Encoders reach a hard accuracy ceiling.&lt;/li&gt;
&lt;li&gt;Deploying a live &lt;strong&gt;SageMaker Cross-Encoder Reranker (&lt;code&gt;BAAI/bge-reranker-base&lt;/code&gt;)&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;How two-stage reranking promoted true relevant context from 0.79 to &lt;strong&gt;0.9970 confidence&lt;/strong&gt;, achieving sub-100ms deterministic inference.&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;em&gt;Built with AWS Bedrock, Amazon RDS PostgreSQL 16 pgvector, and Spring Boot.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>database</category>
      <category>llm</category>
      <category>postgres</category>
      <category>rag</category>
    </item>
    <item>
      <title>The Ingestion Factory: Decoupling Document Uploads with SQS Claim-Check &amp; 100% In-Memory Parsers (Part 2)</title>
      <dc:creator>Nagachaitanya Inamdar</dc:creator>
      <pubDate>Sat, 07 Nov 2026 19:46:44 +0000</pubDate>
      <link>https://dev.to/nagachaitanya_inamdar_56c/the-ingestion-factory-decoupling-document-uploads-with-sqs-claim-check-100-in-memory-parsers-2lln</link>
      <guid>https://dev.to/nagachaitanya_inamdar_56c/the-ingestion-factory-decoupling-document-uploads-with-sqs-claim-check-100-in-memory-parsers-2lln</guid>
      <description>&lt;h2&gt;
  
  
  Series: Designing an Enterprise RAG Platform from Scratch (Part 2 of 5)
&lt;/h2&gt;




&lt;h2&gt;
  
  
  1. The Trap: Synchronous Document Parsing
&lt;/h2&gt;

&lt;p&gt;In &lt;a href="https://dev.to/nagachaitanya_inamdar_56c/beyond-naive-rag-building-an-enterprise-grade-zero-hallucination-architecture-on-aws-4l2k"&gt;Part 1&lt;/a&gt;, I shared why I set out to build &lt;strong&gt;DocLab&lt;/strong&gt;—an enterprise-grade RAG platform on AWS using Java 21, Spring Boot 3, and pgvector. &lt;/p&gt;

&lt;p&gt;Today, let’s talk about the first big hurdle every RAG engineer faces: &lt;strong&gt;Document Ingestion&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;In most hobby tutorials, document ingestion looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;User uploads file ──&amp;gt; Web Server parses file in-process ──&amp;gt; Generates vectors ──&amp;gt; Saves to DB ──&amp;gt; Returns 200 OK
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When you try this in production, you encounter two immediate disasters:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;The Web Thread Bottleneck:&lt;/strong&gt; If an engineer uploads a 40MB PDF or a 100-page architectural specification, parsing that file can take 15 to 45 seconds. Synchronous worker threads in Tomcat or Netty get blocked waiting on CPU-intensive parsing. Under moderate load, your API gateway runs out of threads and returns &lt;code&gt;504 Gateway Timeout&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The Cloud OCR Cost Explosion:&lt;/strong&gt; Many teams blindly wire up cloud OCR services like AWS Textract or vision models for every incoming file. Textract charges &lt;strong&gt;\$1.50 per 1,000 pages for standard tables, and up to \$15.00 per 1,000 pages for complex queries&lt;/strong&gt;. If your company processes 50,000 pages of internal documentation, you’ve blown your entire monthly cloud budget before a single user has asked a question.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;I needed an ingestion architecture that was &lt;strong&gt;completely asynchronous&lt;/strong&gt;, &lt;strong&gt;scale-resilient&lt;/strong&gt;, and &lt;strong&gt;100% cost-free on document parsing&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  2. Decoupling Intake via the SQS Claim-Check Pattern
&lt;/h2&gt;

&lt;p&gt;To prevent web thread starvation, I implemented the enterprise &lt;strong&gt;Claim-Check Pattern&lt;/strong&gt; using Amazon S3 and Amazon SQS.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fshvntck4a1j8ylzihse8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fshvntck4a1j8ylzihse8.png" alt="Ingestion Factory" width="800" height="194"&gt;&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[Client POST /upload] ──&amp;gt; [Spring Boot Gateway]
                                │
        ┌───────────────────────┴───────────────────────┐
        ▼                                               ▼
 [Amazon S3: Raw Bucket]                     [SQS: doclab-parsing-queue]
 (Stores raw unparsed bytes)                 (Lightweight metadata envelope)
        │                                               │
        └───────────────────────┬───────────────────────┘
                                ▼
                   [UniversalParserWorker]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  How the Ingestion Flow Works:
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Instant Acceptance:&lt;/strong&gt; When a client sends a &lt;code&gt;multipart/form-data&lt;/code&gt; request with a document (PDF, Word, Excel, JSON) along with tenant and role metadata, the Spring Boot controller writes the raw binary stream directly to an Amazon S3 raw bucket (&lt;code&gt;raw/doc-{uuid}-v1.ext&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The Claim-Check Envelope:&lt;/strong&gt; The controller never touches or parses the file content. Instead, it emits a lightweight JSON message (a claim-check) to an Amazon SQS queue (&lt;code&gt;doclab-parsing-queue&lt;/code&gt;):
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
     &lt;/span&gt;&lt;span class="nl"&gt;"document_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"doc-eb677320"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
     &lt;/span&gt;&lt;span class="nl"&gt;"version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
     &lt;/span&gt;&lt;span class="nl"&gt;"document_title"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Production Service Catalog"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
     &lt;/span&gt;&lt;span class="nl"&gt;"source_format"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"JSON"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
     &lt;/span&gt;&lt;span class="nl"&gt;"s3_bucket"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"doclab-docs-19530061"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
     &lt;/span&gt;&lt;span class="nl"&gt;"s3_raw_key"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"raw/doc-eb677320-v1-service-catalog.json"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
     &lt;/span&gt;&lt;span class="nl"&gt;"tenant_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"engineering-dept"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
     &lt;/span&gt;&lt;span class="nl"&gt;"allowed_roles"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"ENGINEERING"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
   &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Sub-50ms Response:&lt;/strong&gt; The API returns &lt;code&gt;HTTP 202 ACCEPTED&lt;/code&gt; with the &lt;code&gt;documentId&lt;/code&gt; and status &lt;code&gt;QUEUED&lt;/code&gt; in under &lt;strong&gt;50 milliseconds&lt;/strong&gt;. The web server thread is immediately released back to the connection pool.&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  3. Zero-Cost Universal Parsing (100% In-Memory)
&lt;/h2&gt;

&lt;p&gt;Now that the file is safely in S3 and queued in SQS, background workers handle the heavy lifting. But how do we parse multiple enterprise formats without paying for cloud OCR APIs?&lt;/p&gt;

&lt;p&gt;By building &lt;strong&gt;purely in-memory format-specific extractors&lt;/strong&gt; using proven, industrial-strength open-source Java libraries:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;PDF Documents:&lt;/strong&gt; Parsed via &lt;strong&gt;Apache PDFBox 3.0.2&lt;/strong&gt;. Extracts raw text streams, structural paragraphs, and page boundaries without spinning up heavy headless browsers.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Word Documents (&lt;code&gt;.docx&lt;/code&gt;):&lt;/strong&gt; Parsed via &lt;strong&gt;Apache POI 5.2.5 (XWPF)&lt;/strong&gt;. Traverses OpenXML document structures, preserving heading hierarchies, bolded section titles, and nested paragraph blocks.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Excel Spreadsheets (&lt;code&gt;.xlsx&lt;/code&gt;):&lt;/strong&gt; Parsed via &lt;strong&gt;Apache POI (XSSF)&lt;/strong&gt;. Converts multi-column tabular data into readable key-value contexts (e.g., &lt;code&gt;Cluster Node: worker-node-01 | Instance Type: c5.xlarge | vCPU: 4 | Memory: 16GB&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Raw JSON Technical Schemas:&lt;/strong&gt; Parsed via &lt;strong&gt;Jackson&lt;/strong&gt;. Flattens complex configuration catalogs, API contracts, and service schemas into discrete property chunks.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Markdown &amp;amp; Architecture Decision Records (ADRs):&lt;/strong&gt; Clean token-aware sliding window chunking with semantic heading preservation (&lt;code&gt;# Title&lt;/code&gt;, &lt;code&gt;## Decision&lt;/code&gt;, &lt;code&gt;## Consequences&lt;/code&gt;).&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Why 100% In-Memory?
&lt;/h3&gt;

&lt;p&gt;Each extractor receives the raw &lt;code&gt;byte[]&lt;/code&gt; from S3 directly into a memory stream, extracts text chunks with token metadata, and produces clean text blocks without writing a single temporary file to disk.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Economic Impact:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Cost of processing 10,000 internal documents via cloud OCR: &lt;strong&gt;~\$150.00&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Cost of processing 10,000 internal documents via our in-memory engine: &lt;strong&gt;\$0.00&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  4. The Canonical Intermediate Representation (IR)
&lt;/h2&gt;

&lt;p&gt;One of the biggest mistakes in RAG architectures is coupling file parsing directly to embedding generation. If you change your embedding model (e.g., moving from OpenAI &lt;code&gt;text-embedding-3&lt;/code&gt; to Amazon Titan Text v2), you shouldn't have to re-extract raw PDF bytes from scratch.&lt;/p&gt;

&lt;p&gt;To solve this, our parser outputs a format-agnostic &lt;strong&gt;Canonical Intermediate Representation (IR)&lt;/strong&gt; stored on S3 (&lt;code&gt;ir/doc-{uuid}-v1.json&lt;/code&gt;):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"document_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"doc-eb677320"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"title"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Production Service Catalog"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"source_format"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"JSON"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"content_hash"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"chunks"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"chunk_index"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"section_title"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Property: checkout-service"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"content"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"{&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;  &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;tier&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt; : &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;Tier-1&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;,&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;  &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;database&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt; : &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;aurora-postgresql&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;,&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;  &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;scaling_policy&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt; : &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;Target tracking CPU at 70%&lt;/span&gt;&lt;span class="se"&gt;\"\n&lt;/span&gt;&lt;span class="s2"&gt;}"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once the Canonical IR is written to S3, the parser emits a secondary claim-check to &lt;code&gt;doclab-indexing-queue&lt;/code&gt;. The indexing worker simply reads the clean, normalized JSON IR and generates 1024-dimensional embeddings via Amazon Titan Text v2, completely isolated from raw file parsing.&lt;/p&gt;




&lt;h2&gt;
  
  
  5. Dual-Mode Execution: Automatic Backgrounding vs. Batch Draining
&lt;/h2&gt;

&lt;p&gt;In real enterprise systems, you need flexibility:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;When the Spring Boot server runs, documents must be processed &lt;strong&gt;automatically within 3 seconds&lt;/strong&gt; without human intervention.&lt;/li&gt;
&lt;li&gt;During high-volume batch loads or offline testing, operators need to &lt;strong&gt;drain queues manually via CLI&lt;/strong&gt; without keeping web servers running.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I implemented a &lt;strong&gt;Dual-Mode Worker Architecture&lt;/strong&gt;:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Automated Mode:&lt;/strong&gt; A Spring &lt;code&gt;@Scheduled&lt;/code&gt; worker (&lt;code&gt;BackgroundPipelineWorker&lt;/code&gt;) polls SQS every 3 seconds, automatically draining intake and indexing queues with thread-safe &lt;code&gt;AtomicBoolean&lt;/code&gt; locks.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Manual / CLI Mode:&lt;/strong&gt; Standalone workers featuring auto-draining loops with &lt;code&gt;maxNumberOfMessages(10)&lt;/code&gt; that drain entire backlogs until the queues are completely empty, then cleanly exit.&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  What’s Next in Part 3
&lt;/h2&gt;

&lt;p&gt;Now that raw files are asynchronously parsed into a structured, canonical vector index on AWS, how do we retrieve them accurately?&lt;/p&gt;

&lt;p&gt;In &lt;strong&gt;Part 3&lt;/strong&gt;, we will dive into the &lt;strong&gt;Storage &amp;amp; Hybrid Retrieval Vault&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Why pure vector search is dangerous on technical error codes and schema keys.&lt;/li&gt;
&lt;li&gt;Combining PostgreSQL 16 &lt;code&gt;pgvector&lt;/code&gt; (Cosine HNSW) with native &lt;code&gt;tsvector&lt;/code&gt; (BM25).&lt;/li&gt;
&lt;li&gt;The exact mathematics of &lt;strong&gt;Reciprocal Rank Fusion (RRF)&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;Stay tuned for Part 3! If you have questions about handling multi-format parsing or SQS claim-check architectures, drop them in the comments below!&lt;/em&gt;&lt;/p&gt;




</description>
      <category>architecture</category>
      <category>aws</category>
      <category>java</category>
      <category>rag</category>
    </item>
    <item>
      <title>Beyond Naive RAG: Building an Enterprise-Grade, Near-Zero-Hallucination Architecture on AWS</title>
      <dc:creator>Nagachaitanya Inamdar</dc:creator>
      <pubDate>Sat, 07 Nov 2026 18:48:56 +0000</pubDate>
      <link>https://dev.to/nagachaitanya_inamdar_56c/beyond-naive-rag-building-an-enterprise-grade-zero-hallucination-architecture-on-aws-4l2k</link>
      <guid>https://dev.to/nagachaitanya_inamdar_56c/beyond-naive-rag-building-an-enterprise-grade-zero-hallucination-architecture-on-aws-4l2k</guid>
      <description>&lt;h2&gt;
  
  
  Series: Designing an Enterprise RAG Platform from Scratch (Part 1 of 5)
&lt;/h2&gt;




&lt;h2&gt;
  
  
  1. How This Journey Started: Being Real About RAG
&lt;/h2&gt;

&lt;p&gt;I’ve been working as a software developer for 3 years. Over the last year, like many engineers, I watched the generative AI wave take over tech. &lt;/p&gt;

&lt;p&gt;Everywhere I looked, I saw tutorials titled &lt;em&gt;"Build a Production RAG App in 5 Minutes!"&lt;/em&gt;. But when you open them up, they are almost always the same thing:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A 25-line Python script.&lt;/li&gt;
&lt;li&gt;An in-memory vector database (like ChromaDB or FAISS) running locally on a laptop.&lt;/li&gt;
&lt;li&gt;A single hardcoded API key making synchronous calls.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;As a developer working on real-world systems, I kept asking myself:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;"Is this really how companies run RAG in production?"&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The answer is &lt;strong&gt;no&lt;/strong&gt;. In real enterprise environments:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Companies build on cloud ecosystems like &lt;strong&gt;AWS&lt;/strong&gt;, utilizing managed services, IAM security, and decoupled messaging.&lt;/li&gt;
&lt;li&gt;Backends are predominantly built on enterprise frameworks like &lt;strong&gt;Java / Spring Boot&lt;/strong&gt; rather than transient notebook scripts.&lt;/li&gt;
&lt;li&gt;You can't just dump a 50MB architecture PDF or an Excel sheet into a web thread without crashing your API.&lt;/li&gt;
&lt;li&gt;You can't spend \$10,000 on cloud OCR APIs like Textract for everyday documents.&lt;/li&gt;
&lt;li&gt;You can't tolerate hallucinations—if an engineer asks about an architectural decision (ADR) or incident policy, the answer must be &lt;strong&gt;100% grounded and verifiable&lt;/strong&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;I had an AWS account with around &lt;strong&gt;\$200 in promotional credits&lt;/strong&gt; expiring in a few months. Instead of letting those credits go to waste on idle EC2 instances, I decided to challenge myself: &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Can I design and build a universal, decoupled, enterprise-grade RAG architecture from scratch that is accurate, fail-safe, and truly production-ready?&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That was the birth of &lt;strong&gt;DocLab&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  2. What Does "Production-Ready" Actually Mean for RAG?
&lt;/h2&gt;

&lt;p&gt;Before writing a single line of Terraform or Java, I defined the non-negotiables for what this platform had to achieve:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Universal Multi-Format Ingestion (Zero Cloud OCR Fees):&lt;/strong&gt; Real companies don't just use clean &lt;code&gt;.txt&lt;/code&gt; files. They have complex PDFs, Word &lt;code&gt;.docx&lt;/code&gt; documents, multi-tab Excel &lt;code&gt;.xlsx&lt;/code&gt; spreadsheets, and raw JSON service catalogs. The platform had to parse all of them &lt;strong&gt;100% in-memory&lt;/strong&gt; with \$0 extra AWS OCR fees.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Asynchronous Decoupling:&lt;/strong&gt; Ingestion had to use the enterprise &lt;strong&gt;Claim-Check Pattern&lt;/strong&gt; over SQS queues so that heavy document parsing never blocks user-facing HTTP threads.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Two-Stage Retrieval (Beyond Simple Cosine Similarity):&lt;/strong&gt; Vector search alone is notorious for missing exact technical keys or confusing negations. The system needed hybrid search (BM25 + pgvector RRF) paired with a real-time &lt;strong&gt;Cross-Encoder on AWS SageMaker&lt;/strong&gt; to achieve &amp;gt;99% retrieval confidence.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Fail-Closed Governance:&lt;/strong&gt; If a user from Marketing queries an Engineering ADR, or if no authorized documents exist, the system must &lt;strong&gt;fail closed immediately&lt;/strong&gt;—consuming zero Bedrock tokens and producing zero hallucinations.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cost Conscious:&lt;/strong&gt; The entire infrastructure had to run efficiently within my \$200 cloud credit budget without accidental billing surprises.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  3. The 5-Pillar High-Level Architecture
&lt;/h2&gt;

&lt;p&gt;Here is the high-level design I arrived at:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fewqjexxu6z60wpdei2y9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fewqjexxu6z60wpdei2y9.png" alt="HLD of DocLab" width="797" height="85"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The system is separated into 5 clear pillars:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Client &amp;amp; Edge Access Layer:&lt;/strong&gt; A Spring Boot 3.3.5 platform providing REST endpoints, Server-Sent Events (SSE) streaming, distributed Redis rate limiting, and session memory.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Asynchronous Ingestion Plane:&lt;/strong&gt; Built with Amazon S3 and Amazon SQS using the &lt;strong&gt;Claim-Check Pattern&lt;/strong&gt;, ensuring file intake is decoupled from processing.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;100% In-Memory Universal Parsers:&lt;/strong&gt; Pluggable extractors (Apache PDFBox, Apache POI, Jackson) that transform PDF, DOCX, XLSX, and JSON into a normalized &lt;strong&gt;Canonical Intermediate Representation (IR)&lt;/strong&gt; with \$0 Textract costs.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Data Vault &amp;amp; Hybrid Storage:&lt;/strong&gt; PostgreSQL 16 on Amazon RDS with &lt;code&gt;pgvector&lt;/code&gt; (HNSW indexing) and native &lt;code&gt;tsvector&lt;/code&gt; BM25 lexical search, protected by database-level RBAC pre-filtering.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Two-Stage AI Serving Engine:&lt;/strong&gt; Reciprocal Rank Fusion (RRF) recall paired with an AWS SageMaker real-time endpoint running &lt;strong&gt;BAAI/bge-reranker-base&lt;/strong&gt; cross-encoder, synthesizing grounded responses via &lt;strong&gt;Amazon Bedrock (Nova Micro)&lt;/strong&gt;.&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  4. The Plan for This Series
&lt;/h2&gt;

&lt;p&gt;Building this taught me more about real distributed systems, cloud economics, and AI engineering than any tutorial ever did. In this 5-part series, I am sharing the exact architectural decisions, math, and benchmarks:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Part 1 (This post):&lt;/strong&gt; The Origin Story &amp;amp; High-Level Architecture.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Part 2:&lt;/strong&gt; &lt;em&gt;The Ingestion Factory:&lt;/em&gt; Decoupling uploads with the SQS Claim-Check Pattern &amp;amp; In-Memory Universal Parsers (PDF, Word, Excel, JSON).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Part 3:&lt;/strong&gt; &lt;em&gt;The Storage &amp;amp; Hybrid Vault:&lt;/em&gt; Dense pgvector + Sparse BM25 + Mathematical Reciprocal Rank Fusion (RRF).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Part 4:&lt;/strong&gt; &lt;em&gt;The AI Brain:&lt;/em&gt; Why Bi-Encoders Fail and How AWS SageMaker Cross-Encoder Reranking Achieves 0.997 Confidence.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Part 5:&lt;/strong&gt; &lt;em&gt;Enterprise Governance:&lt;/em&gt; Fail-Closed Guardrails, Zero-Trust Caching, and Real Empirical AWS Benchmarks.&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;em&gt;If you’ve tried moving RAG from a local prototype to the cloud, what was the biggest roadblock you encountered? Let’s discuss in the comments!&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>architecture</category>
      <category>aws</category>
      <category>rag</category>
    </item>
  </channel>
</rss>
