<?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: Gabaoun</title>
    <description>The latest articles on DEV Community by Gabaoun (@gabaoun).</description>
    <link>https://dev.to/gabaoun</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%2F4066040%2F1405d5c5-aa24-4ae2-b949-6a919d205d95.png</url>
      <title>DEV Community: Gabaoun</title>
      <link>https://dev.to/gabaoun</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/gabaoun"/>
    <language>en</language>
    <item>
      <title>I built a RAG engine that refuses to answer with garbage context (and it cost me way more debugging than expected)</title>
      <dc:creator>Gabaoun</dc:creator>
      <pubDate>Thu, 06 Aug 2026 14:40:09 +0000</pubDate>
      <link>https://dev.to/gabaoun/i-built-a-rag-engine-that-refuses-to-answer-with-garbage-context-and-it-cost-me-way-more-debugging-3k21</link>
      <guid>https://dev.to/gabaoun/i-built-a-rag-engine-that-refuses-to-answer-with-garbage-context-and-it-cost-me-way-more-debugging-3k21</guid>
      <description>&lt;p&gt;Most RAG demos on GitHub do the same thing: embed some chunks, cosine-similarity search, stuff the top-k into a prompt, done. I built one of those first too. Then I actually tried to use it on messy real-world docs and it fell apart in three specific ways. So I rebuilt it as an event-driven pipeline instead of a linear script, and that's what became &lt;a href="https://github.com/gabaoun/Project-Aether" rel="noopener noreferrer"&gt;Project Aether&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  The three things that broke my first RAG attempt
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;1. Semantic cache that returned stale garbage.&lt;/strong&gt; Caching LLM responses by exact query string is useless, nobody types the same question twice. I needed similarity-based caching (is this new query "close enough" to a cached one?), but that opens a nastier problem: what threshold counts as "close enough" without returning a wrong-but-plausible cached answer for a subtly different question? I ended up tuning this against a small eval set instead of guessing a cosine threshold and hoping.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Naive vector search missing exact-match terms.&lt;/strong&gt; Pure dense embeddings are bad at exact keywords, part numbers, error codes, that kind of thing. Someone searches for a specific SKU and dense-only search returns five semantically-similar-but-wrong products. Fixed this with hybrid dense+sparse retrieval instead of dense-only.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Ingesting real documents means someone's PII ends up in your vector DB.&lt;/strong&gt; This is the one nobody talks about in RAG tutorials. If you're chunking and embedding real support tickets or contracts, you're embedding whatever PII is in them too, permanently, in a vector store you may not fully control access to. So ingestion needed a PII-masking pass before anything gets chunked, not after.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I ended up with
&lt;/h2&gt;

&lt;p&gt;Project Aether is an event-driven RAG search engine:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;FastAPI&lt;/strong&gt; for the API layer, async end to end&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;LlamaIndex Workflows&lt;/strong&gt; to model ingestion and query as actual event graphs instead of a call-this-then-that script, which made retries and partial failures way saner to reason about&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Redis&lt;/strong&gt; for semantic caching (this is the piece from problem #1 above)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Chroma Cloud&lt;/strong&gt; for hybrid dense+sparse vector search (server-side Qwen/Splade embeddings), which is the fix for problem #2&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;RQ&lt;/strong&gt; for background job processing on the ingestion side&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Groq&lt;/strong&gt; running Llama 3.3 70B for generation, mostly because I wanted fast inference without paying for GPU idle time&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The ingestion pipeline masks PII before chunking, enriches chunks with LLM-generated metadata, and the query side does query transformation plus a relevance-judgment refinement loop before generation, with an optional cross-encoder reranking stage (BAAI/bge-reranker-v2-m3) that I keep off by default because it's too heavy for memory-constrained hosts.&lt;/p&gt;

&lt;h2&gt;
  
  
  The honest part
&lt;/h2&gt;

&lt;p&gt;The &lt;a href="https://project-aether-izd2.onrender.com" rel="noopener noreferrer"&gt;live demo&lt;/a&gt; runs on Render's free tier, so it's query-only (ingestion runs as a local job, not exposed) and it WILL cold-start slow on first request. I'm not going to pretend otherwise, that's just what free hosting gets you. If you hit it and the first response takes a while, that's the container waking up, not the RAG pipeline being slow.&lt;/p&gt;

&lt;p&gt;This is a solo project, no team, so if something looks half-finished it's because I built it around my own time constraints, not because I ran out of ideas.&lt;/p&gt;

&lt;h2&gt;
  
  
  If you want to poke at it
&lt;/h2&gt;

&lt;p&gt;Repo's here: &lt;a href="https://github.com/gabaoun/Project-Aether" rel="noopener noreferrer"&gt;https://github.com/gabaoun/Project-Aether&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I'd genuinely rather get a "this retrieval approach is wrong because X" comment than a star with no feedback, but if you find it useful a star helps other people find it too. Curious if anyone else has fought with semantic-cache threshold tuning, feels like the least-discussed hard problem in RAG right now.&lt;/p&gt;

</description>
      <category>python</category>
      <category>ai</category>
      <category>rag</category>
      <category>machinelearning</category>
    </item>
  </channel>
</rss>
