iwantcoding.com
🔥 Daily 👥 Rooms 🏆 Top Log in Sign up

RAG Intro

Retrieval-Augmented Generation (RAG) gives an LLM access to your documents at query time. Index docs into vectors, retrieve the most relevant chunks for a query, paste them into the prompt with instructions.

Minimal RAG pipeline

EXAMPLE
from langchain_anthropic import ChatAnthropic
from langchain_community.document_loaders import WebBaseLoader, DirectoryLoader, TextLoader
from langchain_text_splitters import RecursiveCharacterTextSplitter
from langchain_community.vectorstores import Chroma
from langchain_openai import OpenAIEmbeddings
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.runnables import RunnablePassthrough
from langchain_core.output_parsers import StrOutputParser

# 1) Load documents
loader = DirectoryLoader('./docs', glob='**/*.md', loader_cls=TextLoader)
docs = loader.load()
print(f'Loaded {len(docs)} documents')

# 2) Chunk — overlap stops semantic boundaries from cutting concepts in half
splitter = RecursiveCharacterTextSplitter(
    chunk_size=1000,
    chunk_overlap=200,
    length_function=len,
    separators=['\n\n', '\n', '. ', ' ', ''],
)
chunks = splitter.split_documents(docs)
print(f'Created {len(chunks)} chunks')

# 3) Embed + store in a vector DB
embeddings = OpenAIEmbeddings(model='text-embedding-3-small')
vs = Chroma.from_documents(chunks, embeddings, persist_directory='./chroma')
vs.persist()

# 4) Build a retriever
retriever = vs.as_retriever(
    search_type='similarity',
    search_kwargs={'k': 4},
)

# 5) Prompt — include retrieved context + the question
prompt = ChatPromptTemplate.from_template('''Answer the question using only the context below.
If the context doesn't contain the answer, say "I don't know based on the provided documents."

Context:
{context}

Question: {question}

Answer:''')

# 6) LCEL chain — retrieve, format, prompt, model, parse
llm = ChatAnthropic(model='claude-opus-4-7', temperature=0)

def format_docs(docs):
    return '\n\n---\n\n'.join(d.page_content for d in docs)

chain = (
    {'context': retriever | format_docs, 'question': RunnablePassthrough()}
    | prompt
    | llm
    | StrOutputParser()
)

# 7) Ask
answer = chain.invoke('What is the company refund policy?')
print(answer)

# 8) Stream tokens
for chunk in chain.stream('Summarise our SLA in 3 bullets.'):
    print(chunk, end='', flush=True)

# 9) Cite sources — return docs alongside the answer
from langchain.chains import RetrievalQAWithSourcesChain
qa = RetrievalQAWithSourcesChain.from_llm(llm, retriever=retriever)
result = qa.invoke({'question': 'What is our PTO policy?'})
print(result['answer'])
print('Sources:', result['sources'])

# 10) Improve quality (rough order of impact)
#   - Better chunking: semantic boundaries (headings) over fixed-size
#   - Larger embedding model (text-embedding-3-large)
#   - Hybrid retrieval: vector + keyword (BM25)
#   - Reranking: cross-encoder over top-50 results
#   - Query rewriting: have the LLM expand the question first
#   - Larger k + smarter prompt formatting
#   - Eval set + measure (recall@k, faithfulness, answer quality)

# 11) Production checklist
#   • Persistent vector DB (Pinecone, Qdrant, pgvector) — not in-process
#   • Background re-indexing as docs change
#   • Source attribution in the UI — users distrust un-cited answers
#   • Guardrails: refuse out-of-scope questions; don't generate beyond the context

Why it matters

RAG quality is mostly a chunking + retrieval problem, not a model problem. Get the right context into the prompt and even small models answer well; get it wrong and the best model invents nonsense.

Tip: Tweak the snippet with Try it Yourself », then sit the quiz at the bottom of the page.

Example

Example
# RAG = Retrieval-Augmented Generation.
# Embed your docs → store in a vector DB → at query time, retrieve top-k → feed to the LLM as context.
Try it Yourself »

Test yourself

Q1. RAG stands for…
Q2. Vector stores enable…
Q3. Embeddings convert text into…

Discussion

Loading…