Skip to content
All writing

RAG / Part 1

How do I get my hands dirty with RAG applications?

Understanding the pieces before putting them together.

Introduction

Yo yo yo, I'm Mohit! This blog is about my RAG journey, how I started understanding RAG, its core components, and how I'm building my first end-to-end RAG (Retrieval-Augmented Generation) application from scratch using Python, without relying on any RAG framework.

I've seen most RAG applications being built as web apps, so I decided to build something for the terminal instead.

Meet Convo, a local-first terminal user interface (TUI) application that lets you select a file from your current directory and start chatting with it. But before jumping into the implementation, I wanted to understand how RAG actually works under the hood, what components we need, and why we need them. That's what this first part is about.

What is RAG? Why is it needed?

RAG (Retrieval-Augmented Generation) is a technique that improves an AI model's answers by retrieving relevant information from external sources and supplying that information to the model as context. Rather than depending entirely on what an LLM learned during training, RAG allows it to use information from documents, databases, previous conversations, or other knowledge sources.

But why do we actually need it? Let's say you have a 100-page PDF and want to ask an LLM some questions about it. The LLM doesn't automatically know what's inside your PDF, and sending the entire document with every question isn't always practical because of context window limitations, token costs, and a lot of unnecessary information. That's where RAG comes in. Instead of sending the entire document, we can retrieve only the relevant parts of it and pass them to the LLM as context.

One important thing to understand is that RAG doesn't retrain the LLM or update its knowledge permanently. We're simply giving it additional information at the time of generating an answer.

A RAG system has two fundamental components:

  1. Retriever: Finds relevant information from an external knowledge source.
  2. Generator: Uses the retrieved information along with the user's question to generate a response.

Architecture of RAG

There are two main workflows in a RAG architecture.

1. Indexing

This is where we prepare our documents so they can be searched later.

Document Loading → Parsing → Chunking → Embedding → Vector Database

  • Document Loading: Reading documents from sources like PDFs, text files, websites, etc.
  • Parsing: Extracting and cleaning useful text from those documents.
  • Chunking: Splitting large documents into smaller, manageable pieces.
  • Embedding: Converting those chunks into numerical vectors using an embedding model.
  • Vector Database: Storing the embeddings along with their text and metadata so we can search them later.

2. Querying

This happens when the user asks a question.

User Query → Query Embedding → Retriever → Context + Prompt → LLM → Answer

When a user asks something, we convert the question into an embedding using the same compatible embedding model that we used during indexing. The retriever then searches the vector database for chunks that are semantically similar to the user's question. We take those retrieved chunks, combine them with the question and instructions, and send everything to the LLM. The LLM generates an answer using that context.

Core Components of a RAG Application

ComponentPurpose
Data SourceSupplies knowledge (PDFs, websites, documents, etc.)
Document Loader / ParserLoads documents and extracts usable content
Text ChunkerSplits large documents into smaller chunks
Embedding ModelConverts text into numerical vectors
Vector Database / IndexStores embeddings and enables similarity search
RetrieverFinds relevant document chunks for a query
Prompt / Context BuilderCombines retrieved context, user query, and instructions
Generative LLMGenerates answers using the retrieved context

Working

Hand-drawn RAG architecture: documents are parsed, chunked, and embedded into a vector database. The user query is embedded to retrieve matching documents, which pass through the prompt builder to the LLM and answer. Purple arrows show indexing; blue arrows show querying.

The important thing to notice in this workflow is that indexing and querying are two different processes. We don't need to load, parse, chunk, and embed the entire document every time a user asks a question, assuming the document has already been indexed and hasn't changed. We just embed the user's query, search for relevant chunks, and pass them to the LLM. Of course, this is just the basic RAG architecture. There are many other techniques like hybrid search, query rewriting, reranking, and so on, but we'll get into those later.

Some other components which are used in a RAG application but not in the core pipeline.

These components are not the part of core RAG pipeline , but used for making ab Full stack user friendly RAG application.

ComponentPurpose
Session ManagementCreates, tracks, resumes, and manages user conversations
Chat History (Temporary / Persistent)Stores previous messages within a session, in memory or a database
Context ManagementSelects and manages relevant conversation history, retrieved documents, and token budgets
Tool CallingAllows the LLM to interact with external tools, APIs, and services
MemoryStores and retrieves useful information across conversations
CachingReuses previously computed results to reduce latency and computational costs
Document ManagementHandles document uploads, indexing, updates, deletion, and metadata

For your initial RAG implementation, prioritize session management, chat history, context management, and document management. Add memory, caching, and tool calling as your application becomes more advanced.

Why say no to frameworks (especially if you are a beginner)

When I started learning RAG, I saw a lot of videos on YouTube saying "Build your first RAG app using LangChain", "RAG with LangGraph", "Use Mastra", "Use Vercel AI SDK", etc. But hardly any of these videos explain how a RAG application actually works under the hood, what components are involved, why we need them, and how to build them from scratch.

I'm not saying using frameworks is bad. Frameworks are just abstractions over code that someone has already written for you. They make development faster, but if you don't understand what's happening underneath, you're just connecting a bunch of prebuilt components without knowing how they work. As a beginner, I think you should first understand how document loading, chunking, embeddings, vector databases, retrieval, and context construction actually work. Try building a simple RAG pipeline yourself using Python and the necessary libraries.

Once you understand the fundamentals, frameworks will make much more sense. You'll know what they're abstracting away, when to use them, and when you don't need them. The goal shouldn't be to learn how to use a RAG framework. It should be to understand how to build a RAG system. Additionally, I personally prefer building things framework-free or with as few frameworks as possible. As I mentioned in one of my previous posts, I really love the core philosophy behind Pi and OpenCode. They're great examples of agentic software built without relying heavily on agent frameworks, which gives their engineers more control over how things work under the hood, instead of being limited by framework abstractions.

Conclusion

So that's pretty much it for Part 1. We went through what RAG actually is, why we need it, how the indexing and querying pipelines work, and the core components required to build a RAG application. One thing I've realized while learning RAG is that the basic idea isn't that complicated.The interesting part is how you build these components, connect them together, and make the whole system actually work well.

In Part 2, we're getting more into the technical implementation, tech stack, and some advanced concepts of RAG.

Check out: convo.bemohit.dev

See you in Part 2!