0–5 min
Real Hook
5–25 min
Core Concept
25–45 min
Live Coding
45–55 min
You Code It
55–60 min
Cheat Sheet
🔥 Real-World Hook (0–5 min)

ChatGPT's knowledge cuts off at a date. It doesn't know your company's internal docs, last week's product update, or your student handbook.

RAG (Retrieval-Augmented Generation) fixes this: embed your documents, store in a vector DB, retrieve the relevant chunks at query time, inject them into the prompt. The LLM answers with YOUR data. Every enterprise AI chatbot works this way.

📈 How RAG Works (5–25 min)

💡 4-Step RAG Pipeline

1. Chunk — split documents into 500-token chunks with overlap.
2. Embed — convert each chunk to a 768-dim vector (semantic meaning as numbers).
3. Store — put vectors in FAISS (fast approximate nearest-neighbour index).
4. Retrieve & Generate — embed query, find top-k similar chunks, send to LLM with question.

💻 Live Demo — Course Notes Q&A (25–45 min)

>from langchain_community.vectorstores import FAISS
from langchain_community.embeddings import HuggingFaceEmbeddings
from langchain.text_splitter import RecursiveCharacterTextSplitter
from langchain_groq import ChatGroq
from langchain_core.prompts import ChatPromptTemplate
import os

# Sample knowledge base (your company docs, textbooks, etc.)
docs = [
    "Linear regression finds the best-fit line through data points
    by minimising the sum of squared errors (least squares method).",
    "Random Forest is an ensemble of decision trees. Each tree trains
    on a random subset of data and features. Final prediction = majority vote.",
    "Overfitting occurs when a model memorises training data but
    generalises poorly to unseen data. Fix with regularisation or more data.",
    "Gradient descent updates model weights by stepping in the
    direction of steepest loss decrease. Learning rate controls step size.",
    "Neural networks stack layers of neurons. Each layer learns
    increasingly abstract features. Deep = many layers.",
]

# 1. Chunk documents
splitter = RecursiveCharacterTextSplitter(chunk_size=300, chunk_overlap=50)
chunks   = splitter.create_documents(docs)

# 2. Embed and store in FAISS
embeddings = HuggingFaceEmbeddings(model_name="all-MiniLM-L6-v2")
vectorstore = FAISS.from_documents(chunks, embeddings)

# 3. Build retriever + LLM
retriever = vectorstore.as_retriever(search_kwargs={"k": 2})
llm = ChatGroq(model="llama3-8b-8192", api_key=os.getenv("GROQ_API_KEY"), temperature=0)

# 4. RAG chain
prompt = ChatPromptTemplate.from_template("
Answer using ONLY the context below. If unsure, say "I don't know."

Context: {context}
Question: {question}
")

def rag_answer(question):
    context_docs = retriever.invoke(question)
    context = "\n\n".join(d.page_content for d in context_docs)
    chain = prompt | llm
    return chain.invoke({"context": context, "question": question}).content

print(rag_answer("How does random forest prevent overfitting?"))

📋 You Code It (45–55 min)

✍ Task

Add 3 more documents about PyTorch, transformers, and LangChain. Ask a question that requires combining two docs. Change k=2 to k=3 — does the answer improve? Print which chunks were retrieved.

⚡ Day 11 Cheat Sheet

HuggingFaceEmbeddings(model_name=...)
Free local embeddings
FAISS.from_documents(chunks, emb)
Build vector index
vectorstore.as_retriever(k=3)
Top-3 similar chunks
vectorstore.save_local("db")
Persist index to disk
FAISS.load_local("db", emb)
Reload saved index
RecursiveCharacterTextSplitter
Smart chunking with overlap
retriever.invoke(question)
Get relevant docs
chunk_overlap=50
Overlap prevents context loss