Large language models know a lot, but they do not know your data. They cannot read your internal wiki, last week’s support tickets, or a contract signed yesterday. Retrieval-Augmented Generation (RAG) fixes this by fetching relevant documents at query time and placing them in the model’s prompt. The model then answers from your sources instead of guessing from its training data.
Quick Takeaways
- RAG retrieves relevant text chunks from your data and injects them into the prompt, so the LLM answers with grounded, current facts.
- A standard pipeline has five stages: load → chunk → embed → retrieve → generate.
- Start with ~500-token chunks, 10-15% overlap, top-k = 4-5, and temperature 0-0.2 for factual answers.
- Choose RAG for changing knowledge and citations. Choose fine-tuning for style and behavior. Choose long context for small, static corpora.
What Is Retrieval-Augmented Generation?
RAG is an architecture that combines two systems:
- A retriever that finds the passages most relevant to a question.
- A generator (an LLM) that writes an answer using only those passages as context.
The idea was formalized in the 2020 paper “Retrieval-Augmented Generation for Knowledge-Intensive NLP Tasks” by Lewis et al. Today it is the default pattern for enterprise chatbots, internal search, and document Q&A.
Without RAG, an LLM relies on parametric memory, the knowledge baked into its weights during training. That memory has a cutoff date, contains no private data, and produces confident errors known as hallucinations. RAG adds non-parametric memory: an external index you can update in seconds, with no retraining.
Why RAG Beats “Just Paste It in the Prompt”
Modern models accept large context windows, so pasting a whole corpus can look tempting. It breaks down fast:
- Cost: you pay for every input token on every call.
- Latency: time-to-first-token grows with prompt size.
- Accuracy: models can miss details buried in the middle of very long inputs.
- Scale: a company knowledge base is often millions of tokens, far beyond any window.
RAG sends only the few chunks that matter.
How a RAG Pipeline Works
Stage 1: Ingest and Chunk Your Documents
Raw files (PDF, HTML, Markdown, tickets, database rows) are parsed into plain text, then split into chunks. Chunking is the single biggest lever on retrieval quality.
| Parameter | Recommended Starting Point | Trade-off |
|---|---|---|
| Chunk size | 300-800 tokens | Small = precise but loses context; large = noisy |
| Overlap | 10-15% | Prevents cutting an idea in half |
| Strategy | Recursive / structure-aware | Respects headings and paragraphs |
| Metadata | Source, title, date, section | Enables filtering and citations |
Split on natural boundaries (headings, paragraphs, sentences) before falling back to raw character counts. Always store metadata with each chunk.
Stage 2: Create Embeddings
An embedding model converts each chunk into a vector, a list of numbers that captures meaning. Texts with similar meaning land close together in vector space, so “reset my password” matches “I can’t log in” even with no shared keywords.
Common embedding choices:
| Model | Dimensions | Type | Notes |
|---|---|---|---|
| all-MiniLM-L6-v2 | 384 | Open source | Fast, runs locally, good baseline |
| BGE / E5 families | 768-1024 | Open source | Strong retrieval benchmarks |
| OpenAI text-embedding-3-small | 1536 | API | Low cost, supports dimension reduction |
| OpenAI text-embedding-3-large | 3072 | API | Higher accuracy, higher cost |
Use the same embedding model for indexing and querying. Mixing models produces meaningless similarity scores.
Stage 3: Store Vectors in a Vector Database
Vectors go into an index built for approximate nearest neighbor (ANN) search, most commonly HNSW. Options include Chroma, FAISS, Qdrant, Weaviate, Pinecone, Milvus, and pgvector for Postgres.
Similarity is usually measured with cosine similarity:
cos(θ) = (A · B) / (‖A‖ × ‖B‖)
A score near 1.0 means the vectors point in nearly the same direction, so the meaning is similar.
Stage 4: Retrieve
At query time, the user’s question is embedded, and the database returns the top-k closest chunks. Typical settings are k = 4-5 for chat and k = 8-20 if you plan to rerank.
Stage 5: Generate with Grounding
Retrieved chunks are inserted into the prompt along with an instruction to answer only from that context. Set temperature between 0 and 0.2 for factual work, and require the model to say “I don’t know” when the context lacks the answer.
Build a Working RAG Pipeline in Python
This minimal example uses Chroma for storage, a local 384-dimension embedding model, and the Anthropic API for generation.
Install dependencies:
pip install anthropic chromadb sentence-transformers
Index your documents:
import chromadb
from chromadb.utils import embedding_functions
# 384-dim local embedding model (no API key needed)
ef = embedding_functions.SentenceTransformerEmbeddingFunction(
model_name="all-MiniLM-L6-v2"
)
client = chromadb.PersistentClient(path="./rag_db")
collection = client.get_or_create_collection(
name="company_docs",
embedding_function=ef,
metadata={"hnsw:space": "cosine"}, # cosine similarity
)
def chunk_text(text, size=400, overlap=60):
"""Word-based chunking with overlap. Swap for a recursive splitter in production."""
words = text.split()
step = size - overlap
return [" ".join(words[i:i + size]) for i in range(0, len(words), step)]
def index_document(doc_id, text, source):
chunks = chunk_text(text)
collection.add(
ids=[f"{doc_id}-{i}" for i in range(len(chunks))],
documents=chunks,
metadatas=[{"source": source, "chunk": i} for i in range(len(chunks))],
)
with open("refund_policy.txt", encoding="utf-8") as f:
index_document("refund-policy", f.read(), source="refund_policy.txt")
Retrieve and generate:
from anthropic import Anthropic
llm = Anthropic() # reads ANTHROPIC_API_KEY from the environment
def answer(question, k=4):
results = collection.query(query_texts=[question], n_results=k)
docs = results["documents"][0]
metas = results["metadatas"][0]
context = "\n\n".join(
f"[{i+1}] (source: {m['source']})\n{d}"
for i, (d, m) in enumerate(zip(docs, metas))
)
response = llm.messages.create(
model="claude-sonnet-5-5",
max_tokens=800,
temperature=0.1, # low temperature for factual answers
system=(
"Answer using ONLY the provided context. "
"Cite sources as [1], [2]. If the context does not contain "
"the answer, say you don't know."
),
messages=[{
"role": "user",
"content": f"<context>\n{context}\n</context>\n\nQuestion: {question}",
}],
)
return response.content[0].text
print(answer("How many days do customers have to request a refund?"))
Annotations:
hnsw:space: cosinesets the distance metric used for retrieval.- The XML-style
<context>tags help the model separate your data from the question. - Numbered chunks make inline citations possible, which builds user trust.
RAG vs. Fine-Tuning vs. Long Context
These approaches solve different problems. Teams often combine them.
| Criterion | RAG | Fine-Tuning | Long Context |
|---|---|---|---|
| Best for | Fresh, private, changing facts | Tone, format, domain behavior | Small, static document sets |
| Update speed | Seconds (re-index) | Hours to days (retrain) | Instant (edit prompt) |
| Citations | Native | Difficult | Possible |
| Per-query cost | Low | Low | High at scale |
| Hallucination control | Strong with grounding | Weak for facts | Moderate |
| Setup effort | Medium | High | Low |
Rule of thumb: use RAG for knowledge, fine-tuning for behavior.
Advanced Techniques That Raise Accuracy
A naive pipeline gets you roughly 70% of the way. These upgrades close the gap.
Hybrid Search
Combine dense vector search (semantic) with BM25 (keyword). Vector search misses exact identifiers like error codes, SKUs, and names. BM25 catches them. Merge the two ranked lists with Reciprocal Rank Fusion (RRF).
Reranking
Retrieve k = 20-50 candidates cheaply, then rescore them with a cross-encoder reranker and keep the top 3-5. Rerankers read the query and chunk together, so they judge relevance more accurately than raw vector distance.
Query Transformation
- Query rewriting: turn “what about the second one?” into a standalone question using chat history.
- Multi-query: generate 3 paraphrases and merge the results.
- HyDE: have the LLM write a hypothetical answer, then embed that to search.
Metadata Filtering
Filter by date, department, product version, or access level before similarity search. This also enforces permissions so users never see documents they should not access.
Contextual Chunking
Prepend a short document-level summary or section title to each chunk before embedding. This resolves ambiguity in chunks like “It expires after 30 days,” where “it” has no referent.
Real-World RAG Use Cases and Workflows
Customer Support Assistant
Data: help-center articles, resolved tickets, product changelogs.
Workflow: embed the customer’s question, retrieve top-5 articles filtered by product version, and generate a reply with links.
Outcome: faster first response, fewer escalations, and answers that match the current release.
Internal Engineering Knowledge Base
Data: runbooks, architecture docs, postmortems, code comments.
Workflow: use hybrid search so exact service names match, rerank, and answer with citations to the source page.
Outcome: new hires find on-call procedures in seconds instead of pinging senior staff.
Legal and Compliance Review
Data: contracts, policies, regulatory text.
Workflow: keep chunks section-aligned, store clause numbers as metadata, and force verbatim citations.
Outcome: reviewers verify every claim against the source passage. Keep a human in the loop.
Prompt Template for Grounded Answers
You are a support analyst. Use ONLY the context below.
<context>
{retrieved_chunks}
</context>
Rules:
1. Cite every claim with its chunk number, like [2].
2. If the context is insufficient, reply: "I don't have enough information."
3. Do not use outside knowledge.
4. Keep the answer under 150 words.
Question: {user_question}
Common RAG Failure Modes and Fixes
| Symptom | Likely Cause | Fix |
|---|---|---|
| Irrelevant answers | Chunks too large or too small | Tune size to 300-800 tokens; add overlap |
| Misses exact terms | Pure vector search | Add BM25 hybrid retrieval |
| Right chunk retrieved, wrong answer | Weak prompt grounding | Add “context only” rule; lower temperature |
| Stale answers | Old index | Schedule incremental re-indexing |
| Confident hallucination | No fallback instruction | Require “I don’t know” behavior |
| Slow responses | Large k, no caching | Reduce k, cache embeddings, rerank fewer |
How to Evaluate Your Pipeline
Do not judge RAG by feel. Build a test set of 50-200 real questions with reference answers, then track:
- Context recall: did retrieval return the chunk that contains the answer?
- Context precision: how much of the retrieved text was relevant?
- Faithfulness: is every claim supported by the retrieved context?
- Answer relevance: does the response address the question?
Frameworks such as RAGAS and TruLens automate these checks. Evaluate retrieval and generation separately so you know which stage is failing.
Frequently Asked Questions
What does RAG stand for in AI?
RAG stands for Retrieval-Augmented Generation. It is a technique that retrieves relevant documents from an external knowledge source and feeds them to a large language model so it can generate grounded, up-to-date answers.
Is RAG better than fine-tuning?
Neither is universally better. RAG is the better choice for factual knowledge that changes, needs citations, or is private. Fine-tuning is the better choice for teaching a model a consistent style, output format, or specialized behavior. Many production systems use both.
Does RAG eliminate hallucinations?
No. RAG reduces hallucinations by grounding answers in retrieved text, but it cannot remove them. Poor retrieval, ambiguous chunks, or weak prompts can still produce wrong answers. Add citations, a “say I don’t know” rule, low temperature, and automated evaluation.
What is the best chunk size for RAG?
Start with 300-800 tokens and 10-15% overlap, then test on your own questions. Short, factual content (FAQs) works well with smaller chunks. Narrative or technical documents often need larger ones to preserve context.




