Ask a large language model about your company's refund policy, last quarter's sales, or a document you wrote yesterday, and it will do one of two things: admit it doesn't know, or -- worse -- invent a confident, plausible, completely wrong answer. LLMs only know what they saw during training, and they saw none of your data. Retrieval-Augmented Generation (RAG) is the fix, and it has quietly become the default architecture in applied AI. Instead of hoping the model memorized the right fact, you fetch the relevant information at question time and hand it to the model alongside the question. This crash course explains how RAG works, why it beats the alternatives, and how the technique has grown up in 2026.
LLMs have three built-in limitations:
You could retrain or fine-tune the model on your data, but that's expensive, slow, and has to be redone every time your data changes. RAG sidesteps all of it: leave the model as-is, and just give it the right context in the prompt. It's the difference between making someone memorize an entire library versus letting them look things up in it.
Retrieve the most relevant chunks of your data, augment the prompt with them, and let the model generate an answer grounded in that context.
That's the whole trick -- and where the name comes from. The magic is in how you find the relevant chunks, and that's where embeddings come in.
The heart of retrieval is the embedding: a model that converts a piece of text into a vector (a long list of numbers -- often 768, 1,536, or more) that captures its meaning. Texts with similar meaning land close together in this high-dimensional space -- even if they share no words.
"How do I reset my password?" -> [0.021, -0.44, 0.19, ... ]
"I forgot my login credentials" -> [0.019, -0.41, 0.22, ... ] (very close!)
"What time does the store open?" -> [0.55, 0.12, -0.30, ... ] (far away)
This is why RAG beats plain keyword search: a user asking "my card got declined" can match a document titled "payment failures" even with zero shared keywords, because the meanings are close. Measuring that closeness is usually done with cosine similarity -- the cosine of the angle between two vectors, where a smaller angle means a closer meaning.
This embedding model is a separate, purpose-built model -- not the LLM itself. It's typically small and fast, trained only to produce good vectors, not to generate text.
Before anyone asks a question, you prepare your knowledge base:
Documents -> Chunks -> Embedding model -> Vectors -> Vector store
If you already run PostgreSQL, the pgvector extension can turn it into your vector store (watch for our upcoming deep dive). Dedicated options include Pinecone, Weaviate, Qdrant, Milvus, and Chroma.
A high-leverage trick: before embedding each chunk, prepend a sentence or two describing where it sits in the larger document -- "This is from the Q3 refund-policy section, discussing international orders...". Torn out of its document, a bare chunk is often ambiguous ("it must be returned within 30 days" -- what must?); this contextual retrieval step restores what got lost. Anthropic measured it cutting failed retrievals by about a third on its own, and by roughly two-thirds when combined with keyword search and a reranker (more on both below).
When a user asks something, the live pipeline runs:
The augmented prompt looks something like this:
You are a support assistant. Answer using ONLY the context below.
If the answer isn't in the context, say you don't know.
Context:
---
[Chunk 1: "Refunds are processed within 5-7 business days..."]
[Chunk 2: "To request a refund, go to Orders > ..."]
---
Question: How long do refunds take?
The model now answers from your documented policy -- not from a fuzzy statistical memory. And because you told it to admit ignorance, hallucination drops dramatically.
# --- Indexing phase (once) ---
chunks = split_into_chunks(load_documents())
for chunk in chunks:
vector = embed(chunk) # e.g. an embedding API
vector_store.add(vector, chunk)
# --- Query phase (per question) ---
def answer(question):
q_vec = embed(question)
top_chunks = vector_store.search(q_vec, k=5) # nearest neighbors
context = "\n---\n".join(top_chunks)
prompt = f"""Answer using only this context:
{context}
Question: {question}"""
return llm.generate(prompt)
That's a working RAG system in ~15 lines of logic. Everything else is refinement.
The pipeline above -- embed once, search once, answer once -- is now called naive RAG. It's the right place to start and still handles a large share of real questions. But 2026's production systems have mostly moved past a single retrieval step.
Agentic RAG. Instead of retrieving once before answering, hand the model retrieval as a tool it can call -- the same mechanism behind MCP (which we covered recently) and function calling. Now it can rewrite a vague question into a sharper search query, look at what came back, decide it isn't enough, and search again. A multi-part question ("compare our 2024 and 2025 refund policies") becomes several targeted searches instead of one blurry one. Coding assistants like Claude Code and Cursor work exactly this way: they don't pre-load your whole repo, they go look for the files a task needs, read them, and follow the references.
Adaptive RAG (self-routing). Not every question needs the full machinery. A cheap classifier inspects the query first and picks a lane: a simple fact gets one search; a hard multi-hop question gets the agentic loop; "thanks, that helped" skips retrieval entirely. Cost and latency stay proportional to difficulty.
GraphRAG. Chunk search is great at "find me the passage about X" and bad at "how do all these things connect?" Tools like Microsoft's GraphRAG build a knowledge graph of the entities and relationships across your entire corpus, plus summaries of each cluster, and retrieve over that. It shines on multi-hop and whole-dataset questions ("what themes run through all our support tickets this quarter?"). Indexing a graph costs more -- though newer variants like LightRAG and LazyGraphRAG have cut that sharply -- so it's usually reserved for queries that genuinely need relationship reasoning. (Our pieces on PuppyGraph and Apache AGE cover the graph-database side.)
The throughline: retrieval is becoming a loop the model drives, not a single step that happens before it.
Modern models accept enormous prompts -- hundreds of thousands, sometimes millions, of tokens. Why not paste in all your documents and skip retrieval? For a small, stable set of documents, that genuinely works, and it's the simplest thing that can. But it breaks down as the corpus grows:
The 2026 view isn't "retrieval vs. a big window" -- it's retrieval feeding the window. Long context lets you be generous: pull the top 20 chunks instead of agonizing over the top 3, and let the model sort it out. A self-route step can decide, per query, whether a quick retrieval is enough or the whole document set should go in. Retrieval is the precision tool; the context window is the workbench it delivers to.
Plenty of people already keep a folder of carefully organized Markdown notes and paste the relevant one into an AI chat by hand. That is a form of retrieval -- you're just doing the retrieving. It works beautifully when the collection is small, stable, and you know which file answers which question.
RAG is what you reach for when that stops scaling:
| Hand-picked files | RAG | |
|---|---|---|
| Who finds the relevant part | You, before you ask | The system, from the question's meaning |
| How much goes in the prompt | Whole files | Just the matching passages |
| Ceiling | Everything must fit the context window | Millions of documents; only the top few chunks are sent |
| Upkeep | Edit the file | Re-embed changed chunks; run a vector store |
| Cost per question | You pay for every file you loaded, every time | A query embedding plus a handful of chunks |
The crossover point is roughly: can I still remember which document to grab, and does it all fit? If yes, organized files -- or a tool that reads your file tree on demand -- are simpler and better. If you find yourself pasting things in "just in case," or you don't know where the answer lives, or the pile changes daily, that's RAG's job.
These solve different problems and are often confused:
| RAG | Fine-Tuning | |
|---|---|---|
| Adds new facts | ✅ Yes, instantly | ⚠️ Awkwardly |
| Data changes often | ✅ Just update the store | ❌ Retrain every time |
| Teaches style/format | ⚠️ Limited | ✅ Its strength |
| Cost to update | Cheap | Expensive |
| Shows its sources | ✅ Yes | ❌ No |
| Setup complexity | Moderate | High |
Rule of thumb: use RAG to change what the model knows, and fine-tuning to change how it behaves. Many production systems use both -- and a third hybrid, RAFT, fine-tunes a model specifically to reason well over retrieved documents.
RAG is simple to prototype and surprisingly tricky to perfect. The usual failure points:
RAG has many knobs -- chunk size, overlap, k, hybrid weighting, re-ranking -- and tuning them by vibes doesn't scale. Build a small evaluation set of real questions with known-good answers and track three things:
Frameworks like RAGAS automate this scoring (often using an LLM as the judge). Fix retrieval first -- a great generator can't answer from chunks it never received.
You've almost certainly used RAG already:
You don't have to write any of the code above to get RAG's benefits today:
A good progression: start with a Project or NotebookLM, move to a framework when you need to customize retrieval, and build the pipeline yourself only when the framework gets in your way.
| Term | Meaning |
|---|---|
| Embedding | A vector representing the meaning of text |
| Chunk | A small slice of a document that gets embedded |
| Vector store | A database optimized for similarity search |
| Similarity search / k-NN | Finding the closest vectors to a query (usually approximate at scale) |
| Cosine similarity | The common measure of "how close" two vectors are |
| Context window | How much text you can fit in one prompt |
| Hybrid search | Semantic + keyword (BM25) search combined |
| Re-ranking | A second pass that reorders retrieved results by relevance |
| Contextual retrieval | Prepending a short description of each chunk's context before embedding it |
| Grounding | Tying the answer to retrieved evidence |
| Agentic RAG | The model calls retrieval as a tool, iteratively -- rewriting queries and searching again |
| Adaptive / self-route RAG | Classifying each query to decide how much retrieval it needs (or none) |
| GraphRAG | Retrieval over a knowledge graph of entities and relationships, for multi-hop questions |
| Faithfulness | Whether the answer's claims are actually supported by the retrieved evidence |
✅ Pick an embedding model (an API, or a local one via Ollama) ✅ Choose a vector store -- pgvector if you already run Postgres ✅ Chunk your docs (~300-500 words, with overlap) and embed them ✅ Wire up: embed question → search top-k → stuff prompt → generate ✅ Instruct the model to answer only from context and cite sources ✅ Build a small eval set of real Q&A; track context recall and faithfulness before you tune anything ✅ Start naive (one search, one answer); add hybrid search, then re-ranking, then an agentic loop -- only where the numbers say you need it
Conclusion: RAG turns a general-purpose LLM into an expert on your world -- without retraining anything. The recipe is timeless: convert meaning into vectors, retrieve what's relevant, and let the model reason over it. It's cheaper than fine-tuning, updates the moment your data does, and -- critically -- it can show its work, which is what makes AI trustworthy enough to deploy. The frontier in 2026 is making retrieval something the model does for itself, mid-thought, as many times as a question demands -- but that still rests on the same foundation you can build today. Start small with a folder of documents and a top-5 search, get an answer grounded in your own files, and you'll see why RAG became the default architecture for putting AI to work on real problems.
Enjoyed this article? Share it with someone who'd love it too.