Building a Basic End-to-End RAG System
Learning Objectives: By the end of this lesson, you will be able to:
- Integrate retriever and generator components into a functional RAG pipeline
- Automate document ingestion, chunking, embedding, indexing, retrieval, and generation
- Build and test a Q&A system over your own documents
1. Welcome and Introduction (5 minutes)
Icebreaker Activity
Have you ever wished you could instantly search and ask questions about your own notes or research papers? What would you use such a system for?
Lesson Overview
- Duration: Approximately 2.5 hours including all exercises (or 60–90 minutes when selected activities are assigned)
- Prerequisites: Familiarity with RAG components, Python, and basic NLP
- Materials Needed: Python environment, Hugging Face Transformers, FAISS or ChromaDB, sample documents (e.g., PDFs, text files)
2. Learning Objectives and Session Outline (5 minutes)
What We’ll Cover Today:
- Orchestration: Combining retriever and generator
- Data ingestion: Loading, chunking, embedding, and indexing
- Building the RAG pipeline: Retrieval and generation
- Hands-on: Building and testing a Q&A system
- Common pitfalls and best practices
Success Criteria:
By the end of this session, you should be able to build a simple RAG system and answer questions using your own documents.
3. Foundation Knowledge (15-20 minutes)
Core Concepts
What is an End-to-End RAG System?
A RAG system combines a retriever (for finding relevant information) and a generator (for producing answers) into a single pipeline. The system can answer questions using up-to-date or private data, not just what was in the LLM’s training set.
Real-World Analogy: Imagine a librarian (retriever) who finds the right books and a subject expert (generator) who reads them and answers your question.
Why This Matters:
- Enables dynamic, updatable knowledge for LLMs
- Supports Q&A, search, and summarization over custom data
- Powers modern AI assistants and enterprise search tools
Orchestration Tools
- Libraries like LangChain, LlamaIndex, Haystack simplify RAG pipeline construction
- You can also build from scratch for deeper understanding
Key Terminology
- Orchestration: Coordinating multiple components in a pipeline
- Data Ingestion: Loading and preparing documents for retrieval
- Pipeline: Sequence of steps from query to answer
4. Detailed Explanation and Examples (25-30 minutes)
Data Ingestion and Preparation
- Load documents (text, PDF, etc.)
- Chunk into passages (e.g., 256-512 tokens)
- Generate embeddings for each chunk
- Index embeddings in a vector database
Building the RAG Pipeline
- User submits a query
- Embed the query and retrieve top-k relevant chunks
- Construct a prompt with retrieved context and the query
- Pass prompt to LLM generator for answer
Practical Applications
- Q&A over research papers, company wikis, or personal notes
- Customer support bots with up-to-date knowledge
- Legal, medical, or academic document search
Common Misconceptions
- Misconception: RAG is only for large companies Reality: Anyone can build a basic RAG system with open-source tools
- Misconception: You need a huge dataset Reality: RAG works well even with small, focused corpora
5. Live Coding Demonstration (20-25 minutes)
Example 1: Data Ingestion and Indexing
from sentence_transformers import SentenceTransformer
import faiss
import numpy as np
# Load and chunk documents (simple example)
docs = [open('doc1.txt').read(), open('doc2.txt').read()]
chunks = [
{"source_id": f"doc-{doc_i}:chunk-{chunk_i}", "text": para}
for doc_i, doc in enumerate(docs)
for chunk_i, para in enumerate(doc.split('\n\n')) if para.strip()
]
# Embed chunks
model = SentenceTransformer('all-MiniLM-L6-v2')
embeddings = model.encode([chunk["text"] for chunk in chunks], normalize_embeddings=True)
# Index with FAISS
index = faiss.IndexFlatIP(embeddings.shape[1])
index.add(np.array(embeddings))
Example 2: Retrieval and Generation
from transformers import pipeline
query = "What is the main finding in doc1?"
q_emb = model.encode([query], normalize_embeddings=True)
D, I = index.search(np.array(q_emb), k=3)
retrieved = [chunks[i] for i in I[0]]
# Build prompt
context = "\n".join(f"[{item['source_id']}] {item['text']}" for item in retrieved)
prompt = f"Answer the question based on the following context:\n{context}\n\nQuestion: {query}\nAnswer:"
# Generate answer
llm = pipeline('text2text-generation', model='google/flan-t5-base')
answer = llm(prompt, max_new_tokens=100)[0]['generated_text']
print(answer)
Example 3: Framework Integration Boundary
# Pseudocode: keep application logic behind a small, testable interface.
# If using LangChain, pin the exact LangChain and integration-package versions
# in your lockfile and implement this interface with those versions' v1 docs.
retrieved_records = retriever.search(query, k=3)
prompt = prompt_builder.format(query=query, sources=retrieved_records)
result = generator.invoke(prompt)
assert citations_exist(result, retrieved_records)
Debugging Demonstration
Show a common error: missing or poorly chunked data
# Problem: Some documents are not chunked, so relevant info is missed
# Solution: Ensure all documents are properly chunked and indexed
6. Hands-On Practice (30-40 minutes)
Exercise 1: Basic Practice (10 minutes)
Objective: Ingest and index your own documents
Instructions:
- Load 2-3 text files from your computer
- Split into paragraphs or fixed-size chunks
- Generate embeddings and index with FAISS
Starter Code:
from sentence_transformers import SentenceTransformer
import faiss
import numpy as np
# Your document loading and chunking code here
Solution:
docs = [open('doc1.txt').read(), open('doc2.txt').read()]
chunks = [para for doc in docs for para in doc.split('\n\n') if para.strip()]
model = SentenceTransformer('all-MiniLM-L6-v2')
embeddings = model.encode(chunks, normalize_embeddings=True)
index = faiss.IndexFlatIP(embeddings.shape[1])
index.add(np.array(embeddings))
Exercise 2: Intermediate Challenge (15 minutes)
Objective: Build a retrieval and generation pipeline
Requirements:
- Accept a user query
- Retrieve top-3 relevant chunks
- Construct a prompt and generate an answer
Hints:
- Use Hugging Face pipeline for generation
- Print both retrieved context and answer
Exercise 3: Creative Application (15 minutes)
Objective: Extend your RAG system
Challenge:
- Add support for PDF or web page ingestion (e.g., use PyPDF2 or BeautifulSoup)
- Try a different LLM (e.g., Llama, Falcon)
- Add source citation in generated answers
Extension Ideas:
- Build a simple web interface for your RAG system
- Experiment with chunk sizes and retrieval strategies
7. Common Pitfalls and Troubleshooting (10 minutes)
Frequent Mistakes
- Problem: Poor retrieval due to bad chunking or missing data Cause: Inconsistent or overly large/small chunks Solution: Standardize chunk sizes and check all docs are indexed
- Problem: LLM ignores context Cause: Weak prompt engineering Solution: Use explicit, clear prompt templates
Debugging Tips
- Print retrieved context for each query
- Test with known questions and answers
- Monitor LLM output for hallucinations
8. Best Practices and Professional Tips (5 minutes)
Industry Standards
- Automate data ingestion and indexing for scalability
- Log queries, retrieved docs, and answers for analysis
- Regularly evaluate retrieval and generation quality
Next Steps in Your Learning Journey
- Explore advanced orchestration with LangChain or LlamaIndex
- Add re-ranking, summarization, or multi-hop retrieval
- Read about production RAG deployments
Production Notes
Use an instruction-following model designed for question answering rather than GPT-2 for a realistic generator, and carry stable source metadata (source_id, title, location, and chunk ID) from ingestion through retrieval and citation validation. Framework APIs change quickly: the LangChain snippet in this lesson is conceptual unless dependencies are pinned. For LangChain v1 projects, follow the installed version’s current retrieval-chain APIs and integration-package imports rather than copying legacy langchain.vectorstores examples.
9. Summary and Review (5 minutes)
Key Takeaways
- End-to-end RAG systems combine retrieval and generation for dynamic Q&A
- Data ingestion, chunking, and indexing are critical for good performance
- Prompt engineering and pipeline orchestration enable practical applications
Quick Review Questions
- What are the main steps in building a RAG system?
- Why is chunking and indexing important?
- How can you improve retrieval and generation quality?
For Instructors
Teaching Tips
- Walk through the pipeline step by step
- Encourage students to use their own documents for practice
- Address confusion about orchestration and data ingestion
Assessment Ideas
- Quiz: Identify pipeline steps in code
- Project: Build a RAG system for a specific domain
- Peer review: Evaluate retrieval and answer quality
Adaptation Notes
- For beginners: Focus on text files and basic pipeline
- For advanced: Add PDF/web ingestion, re-ranking, or web interface
- Online: Provide Colab notebooks; In-person: Live coding demos