<?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: Rayulu Mukku</title>
    <description>The latest articles on DEV Community by Rayulu Mukku (@rayulumukku).</description>
    <link>https://dev.to/rayulumukku</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%2F3890614%2F5502d04b-48d9-4d6f-8d23-1f143c8d4d92.jpeg</url>
      <title>DEV Community: Rayulu Mukku</title>
      <link>https://dev.to/rayulumukku</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/rayulumukku"/>
    <language>en</language>
    <item>
      <title>I Built a RAG System, But I Didn't Want It to Be Another "Chat With Your PDF" App</title>
      <dc:creator>Rayulu Mukku</dc:creator>
      <pubDate>Sun, 30 Aug 2026 20:05:06 +0000</pubDate>
      <link>https://dev.to/rayulumukku/i-built-a-rag-system-but-i-didnt-want-it-to-be-another-chat-with-your-pdf-app-3lg5</link>
      <guid>https://dev.to/rayulumukku/i-built-a-rag-system-but-i-didnt-want-it-to-be-another-chat-with-your-pdf-app-3lg5</guid>
      <description>&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%2Fmcp8adlgzixqgd129dlx.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%2Fmcp8adlgzixqgd129dlx.png" alt=" " width="800" height="450"&gt;&lt;/a&gt;# I Built a RAG Workstation Instead of Another "&lt;strong&gt;Chat With Your PDF&lt;/strong&gt;" App&lt;/p&gt;

&lt;p&gt;I've been building &lt;strong&gt;RAG Studio&lt;/strong&gt; for a while, and I wanted to do something a little different from the usual RAG projects.&lt;/p&gt;

&lt;p&gt;Most RAG demos are basically:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Upload a document → ask a question → get an answer.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That's fine for demonstrating the concept.&lt;/p&gt;

&lt;p&gt;But when I started building my own, I kept thinking about everything happening &lt;strong&gt;between those three steps&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;What happens to the document before it's embedded?&lt;/p&gt;

&lt;p&gt;What if the document contains an API key or someone's email address?&lt;/p&gt;

&lt;p&gt;Which chunks actually get retrieved?&lt;/p&gt;

&lt;p&gt;Does a high similarity score mean the retrieved content is actually useful?&lt;/p&gt;

&lt;p&gt;And what happens when the answer isn't in the local documents at all?&lt;/p&gt;

&lt;p&gt;I didn't want all of that to be hidden behind a single "Ask AI" button.&lt;/p&gt;

&lt;p&gt;So I built &lt;strong&gt;&lt;a href="https://rayulumukku.com/projects/rag-studio/" rel="noopener noreferrer"&gt;RAG Studio&lt;/a&gt;&lt;/strong&gt; — an interactive RAG workstation where I can actually see and inspect what's happening throughout the pipeline.&lt;/p&gt;

&lt;p&gt;The project ended up combining three areas I wanted to explore:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;ETL + DAG orchestration + Corrective RAG (CRAG).&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;And one other thing was important to me:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Generate embeddings locally in the browser whenever possible.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  What does the architecture look like?
&lt;/h2&gt;

&lt;p&gt;At a high level, the pipeline 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;Document
   ↓
ETL / Cleaning
   ↓
Chunking
   ↓
Local Embedding
   ↓
Vector Index
   ↓
Query Embedding
   ↓
Cosine Similarity
   ↓
CRAG Evaluation
   ↓
 ┌───────────────┐
 │               │
Relevant      Not Relevant
 │               │
 ↓               ↓
Local Context   Web Search
 │               │
 └───────┬───────┘
         ↓
      LLM
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Quite a few things are happening here, so I'll go through the interesting parts.&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%2Fczegc78s0168lu2bwem3.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%2Fczegc78s0168lu2bwem3.png" alt=" " width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  1. I started with ETL because documents are messy
&lt;/h1&gt;

&lt;p&gt;A lot of RAG tutorials start with clean text.&lt;/p&gt;

&lt;p&gt;Real documents don't.&lt;/p&gt;

&lt;p&gt;A document can contain:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;headers and footers&lt;/li&gt;
&lt;li&gt;duplicated text&lt;/li&gt;
&lt;li&gt;HTML boilerplate&lt;/li&gt;
&lt;li&gt;advertisements&lt;/li&gt;
&lt;li&gt;inconsistent whitespace&lt;/li&gt;
&lt;li&gt;irrelevant navigation content&lt;/li&gt;
&lt;li&gt;personal information&lt;/li&gt;
&lt;li&gt;API keys and other secrets&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If that content goes straight into chunking and embedding, you're already starting with a problem.&lt;/p&gt;

&lt;p&gt;So RAG Studio has a dedicated &lt;strong&gt;ETL Studio&lt;/strong&gt; before the actual RAG pipeline.&lt;/p&gt;

&lt;h3&gt;
  
  
  Extract
&lt;/h3&gt;

&lt;p&gt;The ingestion layer supports:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;PDF&lt;/li&gt;
&lt;li&gt;TXT&lt;/li&gt;
&lt;li&gt;Markdown&lt;/li&gt;
&lt;li&gt;JSON&lt;/li&gt;
&lt;li&gt;HTML/article content&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The goal is to turn different input formats into a normalized text representation.&lt;/p&gt;

&lt;h3&gt;
  
  
  Transform
&lt;/h3&gt;

&lt;p&gt;The cleaning stage then removes unnecessary content and looks for sensitive patterns.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;alex.vance@ai-research.org
            ↓
[REDACTED_EMAIL]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;+1-555-019-2834
            ↓
[REDACTED_PHONE]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sk-live-xxxxxxxx
            ↓
[REDACTED_API_KEY]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;192.168.1.104
            ↓
[REDACTED_IP]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There is also whitespace normalization and HTML/boilerplate cleanup.&lt;/p&gt;

&lt;p&gt;The important part is the ordering:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;redaction happens before vectorization.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I wanted sensitive information removed before it ever became part of the embedding/indexing pipeline.&lt;/p&gt;




&lt;h1&gt;
  
  
  2. I didn't want the ETL process to be invisible
&lt;/h1&gt;

&lt;p&gt;Normally, document cleaning happens somewhere in the backend and the user never sees it.&lt;/p&gt;

&lt;p&gt;I wanted the opposite.&lt;/p&gt;

&lt;p&gt;RAG Studio has a &lt;strong&gt;side-by-side diff viewer&lt;/strong&gt; showing:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;RAW DOCUMENT          CLEANED DOCUMENT

Original text    →    Normalized text
PII              →    [REDACTED]
HTML noise       →    Removed
Extra whitespace →    Cleaned
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It also shows information such as the original size, cleaned size and compression ratio.&lt;/p&gt;

&lt;p&gt;So you can actually see what the ETL stage did before loading the cleaned corpus into the RAG pipeline.&lt;/p&gt;

&lt;p&gt;This turned out to be more useful than I initially expected.&lt;/p&gt;

&lt;p&gt;When retrieval produces strange results, I can go back and check whether the problem started with the source data.&lt;/p&gt;




&lt;h1&gt;
  
  
  3. Then I turned the RAG pipeline into a DAG
&lt;/h1&gt;

&lt;p&gt;Once the document is clean, there are still quite a few stages involved.&lt;/p&gt;

&lt;p&gt;Instead of hiding them behind a function like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;runRAG&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I represented the pipeline as a &lt;strong&gt;Directed Acyclic Graph&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The current pipeline contains nodes such as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;etl_hub
doc_source
chunking
vectorizer
vector_index
query_source
query_embed
cosine_ranker
crag_grader
web_fallback
context_injector
llm_synthesizer
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Visually, it becomes something like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ETL Hub
   ↓
Document Source
   ↓
Chunking
   ↓
Vectorizer
   ↓
Vector Index
   ↓
Query Source
   ↓
Query Embedding
   ↓
Cosine Ranker
   ↓
CRAG Grader
   ↓
Web Fallback
   ↓
Context Injector
   ↓
LLM Synthesizer
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The graph isn't just there for visualization.&lt;/p&gt;

&lt;p&gt;It gives me a way to &lt;strong&gt;inspect the actual pipeline state&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%2Fohvus39m682odfilyhok.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%2Fohvus39m682odfilyhok.png" alt=" " width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  4. Why I wanted an interactive DAG
&lt;/h1&gt;

&lt;p&gt;Let's say the final answer is wrong.&lt;/p&gt;

&lt;p&gt;It's very easy to immediately think:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"The LLM hallucinated."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;But maybe the LLM wasn't the problem.&lt;/p&gt;

&lt;p&gt;Maybe the document wasn't cleaned properly.&lt;/p&gt;

&lt;p&gt;Maybe the chunk boundaries were bad.&lt;/p&gt;

&lt;p&gt;Maybe the relevant chunk wasn't retrieved.&lt;/p&gt;

&lt;p&gt;Maybe the retrieved chunk was semantically similar but didn't actually answer the question.&lt;/p&gt;

&lt;p&gt;Maybe the system should have searched the web instead.&lt;/p&gt;

&lt;p&gt;With the DAG, I can work through those stages.&lt;/p&gt;

&lt;p&gt;Each node can expose things like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;input payload&lt;/li&gt;
&lt;li&gt;output payload&lt;/li&gt;
&lt;li&gt;execution state&lt;/li&gt;
&lt;li&gt;vector dimensions&lt;/li&gt;
&lt;li&gt;storage mechanism&lt;/li&gt;
&lt;li&gt;latency&lt;/li&gt;
&lt;li&gt;memory information&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I also added the ability to jump directly from a node in the graph to the corresponding stage in the application.&lt;/p&gt;

&lt;p&gt;So the graph becomes something closer to a &lt;strong&gt;debugger for the RAG pipeline&lt;/strong&gt;.&lt;/p&gt;




&lt;h1&gt;
  
  
  5. The embedding pipeline runs locally
&lt;/h1&gt;

&lt;p&gt;This was another part of the project I really wanted to experiment with.&lt;/p&gt;

&lt;p&gt;Instead of sending every document chunk to an external embedding API, RAG Studio uses &lt;strong&gt;&lt;a href="https://huggingface.co/docs/transformers.js/" rel="noopener noreferrer"&gt;Transformers.js&lt;/a&gt;&lt;/strong&gt; and &lt;strong&gt;&lt;a href="https://onnxruntime.ai/" rel="noopener noreferrer"&gt;ONNX Runtime&lt;/a&gt;&lt;/strong&gt; to generate embeddings locally in the browser.&lt;/p&gt;

&lt;p&gt;The model produces &lt;strong&gt;384-dimensional embeddings&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The basic flow is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Text
 ↓
Tokenizer
 ↓
Transformer Model
 ↓
384D Vector
 ↓
Local Vector Index
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The model execution uses &lt;strong&gt;WebAssembly&lt;/strong&gt;, while the heavier inference work runs inside &lt;strong&gt;Web Workers&lt;/strong&gt; so the main UI thread doesn't get blocked.&lt;/p&gt;

&lt;p&gt;So the browser is doing a lot more of the work:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Document
   ↓
Browser
   ↓
Embedding Model
   ↓
Vector
   ↓
Local Index
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;instead of:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Document
   ↓
Server
   ↓
Embedding API
   ↓
Vector Database
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For a large production system, I'd obviously consider a different architecture.&lt;/p&gt;

&lt;p&gt;But for this project, running the embedding pipeline locally was an interesting trade-off.&lt;/p&gt;

&lt;p&gt;It reduces the need for a separate embedding service and keeps more of the processing on the client.&lt;/p&gt;




&lt;h1&gt;
  
  
  6. Then comes the math
&lt;/h1&gt;

&lt;p&gt;Once everything is embedded, retrieval is essentially a vector similarity problem.&lt;/p&gt;

&lt;p&gt;The query is converted into a vector, and that vector is compared against the document vectors.&lt;/p&gt;

&lt;p&gt;The main calculation is cosine similarity:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;cosine_similarity(A, B)
=
(A · B) / (||A|| ||B||)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So a retrieval result might look like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Chunk A    0.91
Chunk B    0.86
Chunk C    0.72
Chunk D    0.31
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The higher the score, the more similar the vectors are.&lt;/p&gt;

&lt;p&gt;But there is a problem.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Similarity doesn't necessarily mean relevance.&lt;/strong&gt;&lt;/p&gt;




&lt;h1&gt;
  
  
  7. A high similarity score doesn't mean the context is good
&lt;/h1&gt;

&lt;p&gt;This is where I wanted to go beyond basic RAG.&lt;/p&gt;

&lt;p&gt;Imagine the user asks a question and retrieval returns:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Chunk A → 0.91
Chunk B → 0.87
Chunk C → 0.83
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It looks great.&lt;/p&gt;

&lt;p&gt;But what if those chunks are only &lt;em&gt;related&lt;/em&gt; to the question and don't actually contain enough information to answer it?&lt;/p&gt;

&lt;p&gt;If we blindly send them to the LLM, we're basically hoping the model figures it out.&lt;/p&gt;

&lt;p&gt;I wanted another check before generation.&lt;/p&gt;

&lt;p&gt;That's where &lt;strong&gt;Corrective RAG (CRAG)&lt;/strong&gt; comes in.&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%2F0wft007h2pwcvozrsc5u.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%2F0wft007h2pwcvozrsc5u.png" alt=" " width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  8. The CRAG layer acts as a quality gate
&lt;/h1&gt;

&lt;p&gt;After retrieval, the selected chunks are passed to a relevance evaluator.&lt;/p&gt;

&lt;p&gt;The evaluator produces a structured result containing:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Verdict
Confidence
Justification
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;VERDICT: RELEVANT

CONFIDENCE: 92.4%

The retrieved chunks contain information
directly related to the user's question.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The important thing is that the system now has a decision point.&lt;/p&gt;

&lt;p&gt;Instead of:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Retrieve → Generate
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;the pipeline becomes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Retrieve
   ↓
Evaluate
   ↓
Is the context good enough?
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h1&gt;
  
  
  9. What if the local documents don't have the answer?
&lt;/h1&gt;

&lt;p&gt;This is probably my favorite part of the architecture.&lt;/p&gt;

&lt;p&gt;If the retrieved context is relevant enough, the pipeline continues normally:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Retrieved Chunks
       ↓
   CRAG Grader
       ↓
    Relevant
       ↓
 Local Context
       ↓
      LLM
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But if the evaluator decides the context isn't sufficient:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Retrieved Chunks
       ↓
   CRAG Grader
       ↓
  Not Relevant
       ↓
Optimized Search Query
       ↓
    Web Search
       ↓
 Additional Context
       ↓
      LLM
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So the local vector index isn't treated as the absolute source of truth.&lt;/p&gt;

&lt;p&gt;It's one knowledge source.&lt;/p&gt;

&lt;p&gt;If it can't provide enough evidence, the system has another route.&lt;/p&gt;

&lt;p&gt;This is the main idea behind the corrective layer:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Don't force the LLM to answer from context that the system already knows is insufficient.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h1&gt;
  
  
  10. The UI lets me watch the whole thing happen
&lt;/h1&gt;

&lt;p&gt;This is really what separates RAG Studio from a normal RAG demo for me.&lt;/p&gt;

&lt;p&gt;I can run the pipeline step by step instead of waiting for one final response.&lt;/p&gt;

&lt;p&gt;I can inspect the intermediate state.&lt;/p&gt;

&lt;p&gt;I can look at the vectors.&lt;/p&gt;

&lt;p&gt;I can see the retrieval scores.&lt;/p&gt;

&lt;p&gt;I can inspect the CRAG decision.&lt;/p&gt;

&lt;p&gt;I can see whether the web fallback was triggered.&lt;/p&gt;

&lt;p&gt;And I can inspect what eventually gets assembled into the LLM context.&lt;/p&gt;

&lt;p&gt;So instead of only seeing:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Question → Answer
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I can see:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Question
   ↓
Query Embedding
   ↓
Similarity Search
   ↓
Retrieved Chunks
   ↓
Relevance Evaluation
   ↓
Routing Decision
   ↓
Context Assembly
   ↓
LLM
   ↓
Answer
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That makes debugging much easier.&lt;/p&gt;




&lt;h1&gt;
  
  
  The technology behind it
&lt;/h1&gt;

&lt;p&gt;The main stack is:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Technology&lt;/th&gt;
&lt;th&gt;Where I use it&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;TypeScript 5+&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Main application, state, APIs and vector operations&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Next.js&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Application framework&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;React / JSX&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;UI&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Zustand&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Client-side state management&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Tailwind CSS&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Styling and responsive UI&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;SVG&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Interactive DAG and architecture visualizations&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Transformers.js&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Browser-side model inference&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;ONNX Runtime&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Local model execution&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;WebAssembly&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Fast browser-side inference&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Web Workers&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Running embedding workloads away from the UI thread&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;One thing I particularly enjoyed was building the DAG visualization myself rather than treating it as just an image.&lt;/p&gt;

&lt;p&gt;The nodes, connections and interactions are part of the application.&lt;/p&gt;




&lt;h1&gt;
  
  
  What I actually wanted to build
&lt;/h1&gt;

&lt;p&gt;I wasn't trying to build another chatbot.&lt;/p&gt;

&lt;p&gt;I wanted to make the &lt;strong&gt;RAG pipeline itself something you can explore&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;A normal RAG application might look like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Upload
  ↓
Embed
  ↓
Search
  ↓
LLM
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;RAG Studio is closer to:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;                 ┌──────────────┐
                 │   Document   │
                 └──────┬───────┘
                        ↓
                 ┌──────────────┐
                 │     ETL      │
                 │ Clean/Redact │
                 └──────┬───────┘
                        ↓
                    Chunking
                        ↓
                Local Embeddings
                        ↓
                   Vector Index
                        ↓
                     Query
                        ↓
                 Similarity Search
                        ↓
                  CRAG Evaluation
                    ↙        ↘
               Relevant    Insufficient
                  ↓             ↓
             Local Context   Web Search
                    ↘        ↙
                     Context
                        ↓
                       LLM
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There are more moving parts, but I think that's actually the point.&lt;/p&gt;

&lt;p&gt;When something goes wrong, there are more places to look.&lt;/p&gt;




&lt;h1&gt;
  
  
  What I learned building it
&lt;/h1&gt;

&lt;p&gt;The biggest thing I took away from this project is that &lt;strong&gt;RAG isn't just retrieval&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;There are a lot of decisions happening before the LLM ever sees the prompt.&lt;/p&gt;

&lt;p&gt;The source data matters.&lt;/p&gt;

&lt;p&gt;The cleaning matters.&lt;/p&gt;

&lt;p&gt;The chunking matters.&lt;/p&gt;

&lt;p&gt;The embeddings matter.&lt;/p&gt;

&lt;p&gt;The retrieval strategy matters.&lt;/p&gt;

&lt;p&gt;And even after retrieving something that looks relevant, you still need to ask whether it is actually sufficient.&lt;/p&gt;

&lt;p&gt;That's why I started thinking about RAG less as:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;"Search some vectors and ask an LLM."&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;and more as:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;"Build a pipeline that can inspect its own intermediate state and recover when one path isn't good enough."&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That's what I wanted RAG Studio to explore.&lt;/p&gt;

&lt;p&gt;Not just &lt;strong&gt;getting an answer&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;But being able to look at the pipeline and understand &lt;strong&gt;how the system got there&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  Try it
&lt;/h2&gt;

&lt;p&gt;I've made the project available here:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://rayulumukku.com/projects/rag-studio/" rel="noopener noreferrer"&gt;RAG Studio → rayulumukku.com/projects/rag-studio/&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If you're building RAG systems yourself, I'd be interested in hearing how you're handling retrieval evaluation, document preprocessing, and fallback strategies.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The interesting part of RAG isn't just the final answer. It's everything that happens before it.&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>rag</category>
      <category>nextjs</category>
      <category>webdev</category>
    </item>
  </channel>
</rss>
