Generative AI & LLM

Module 6 of 16

Module 06: RAG Fundamentals

4 min read646 words
What you'll learn
Explain the RAG architecture and why it solves LLM knowledge limitationsLoad documents from various formats (PDF, web, Markdown, CSV)Implement multiple chunking strategies and select the right oneBuild a complete end-to-end RAG pipeline from scratchEvaluate RAG system quality with quantitative metrics

Duration: 5 hours | Difficulty: Intermediate | Prerequisites: Modules 01–05

Learning Objectives

By the end of this module, you will be able to:

  • Explain the RAG architecture and why it solves LLM knowledge limitations
  • Load documents from various formats (PDF, web, Markdown, CSV)
  • Implement multiple chunking strategies and select the right one
  • Build a complete end-to-end RAG pipeline from scratch
  • Evaluate RAG system quality with quantitative metrics

1. Why RAG?

The Problem with LLMs Alone

Models forget your private wiki, today’s prices, and post-cutoff news. RAG (retrieval-augmented generation) fetches relevant chunks at query time and pastes them into the prompt so the model grounds on your data.

Analogy: Open-book exam vs closed-book. RAG is the open book—still your brain writes the answer, but the pages are real.

┌─────────────────────────────────────────────────────────┐
│                     RAG Pipeline                         │
│                                                          │
│  User Query ──▶ Retrieve ──▶ Augment ──▶ Generate       │
│       │           │            │            │            │
│  "What is X?"   Search      Inject       LLM produces   │
│                vector DB    context      grounded answer │
│                for top-k    into prompt                  │
│                matches                                    │
└─────────────────────────────────────────────────────────┘
Without RAGWith RAG
Model guesses from weightsModel cites retrieved text
Stale by defaultUpdate index when docs change
Risky on proprietary factsStill need guardrails, but better

Key Takeaway

RAG moves knowledge from weights to documents you control.

2. Document Loading

Formats you’ll see in the wild

FormatLoader ideaGotchas
Plain text / MDread_textEncoding, huge files
PDFPage-by-page extractTables break; OCR sometimes
CSVRow → mini-docWhich columns become “content”?
HTML / webParse main bodyBoilerplate, nav noise

Plain-English pipeline: normalize encoding → strip boilerplate → attach metadata (source, page, url, ingested_at).

Try This! Load the same PDF with two libraries; diff the extracted text for one table-heavy page.

3. Chunking Strategies

Chunking is where RAG wins or dies: bad splits = missing context or polluted retrieval.

Fixed-Size Chunking

Sliding window with overlap—dead simple, sometimes cuts mid-thought.

Recursive Character Splitting

Try \n\n, then \n, then sentence boundaries—keeps paragraphs together when possible.

Semantic Chunking

Cluster sentences by embedding similarity—slower, often smoother chunks.

Choosing a Chunking Strategy

StrategyBest forProsCons
Fixed-sizeUniform blobsPredictableMid-sentence tears
RecursiveDocs with headingsStructure-awareNeeds tuning
SemanticMessy mixed contentCoherent chunksCost + complexity

Concept: Overlap between chunks reduces “answer was on the seam” failures.

Fun Fact: Teams often spend more time on chunking than on embedding model choice.

4. End-to-End RAG Pipeline

Key Example: Minimal ingest → retrieve → generate loop: embed with a local model, store in Chroma, answer with OpenAI using injected context.

python
[object Object], chromadb
,[object Object], openai ,[object Object], OpenAI
,[object Object], sentence_transformers ,[object Object], SentenceTransformer


,[object Object], ,[object Object],(,[object Object],) -> ,[object Object],[,[object Object],]:
    chunks, i = [], ,[object Object],
    ,[object Object], i < ,[object Object],(text):
        chunks.append(text[i : i + size])
        i += size - overlap
    ,[object Object], chunks


,[object Object], ,[object Object],:
    ,[object Object], ,[object Object],(,[object Object],):
        ,[object Object],.llm = OpenAI(api_key=,[object Object],)
        ,[object Object],.emb = SentenceTransformer(,[object Object],)
        ,[object Object],.db = chromadb.Client()
        ,[object Object],.col = ,[object Object],.db.create_collection(,[object Object],, metadata={,[object Object],: ,[object Object],})

    ,[object Object], ,[object Object],(,[object Object],):
        texts, metas, ids = [], [], []
        ,[object Object], doc ,[object Object], docs:
            ,[object Object], j, ch ,[object Object], ,[object Object],(chunk_text(doc[,[object Object],])):
                texts.append(ch)
                metas.append({,[object Object],: doc[,[object Object],], ,[object Object],: j})
                ids.append(,[object Object],)
        vectors = ,[object Object],.emb.encode(texts).tolist()
        ,[object Object],.col.add(documents=texts, metadatas=metas, embeddings=vectors, ids=ids)

    ,[object Object], ,[object Object],(,[object Object],) -> ,[object Object],:
        qv = ,[object Object],.emb.encode(question).tolist()
        res = ,[object Object],.col.query(query_embeddings=[qv], n_results=k)
        ctx = ,[object Object],.join(res[,[object Object],][,[object Object],])
        r = ,[object Object],.llm.chat.completions.create(
            model=,[object Object],,
            messages=[
                {,[object Object],: ,[object Object],, ,[object Object],: ,[object Object],},
                {,[object Object],: ,[object Object],, ,[object Object],: ,[object Object],},
            ],
            temperature=,[object Object],,
        )
        ,[object Object], r.choices[,[object Object],].message.content


rag = MiniRAG()
rag.ingest(
    [
        {,[object Object],: ,[object Object],, ,[object Object],: ,[object Object],},
        {,[object Object],: ,[object Object],, ,[object Object],: ,[object Object],},
    ]
)
,[object Object],(rag.ask(,[object Object],))

5. RAG Evaluation

You’re grading two systems: retrieval + generation.

SignalWhat it catches
Hit rate @kDid the right chunk appear in top-k?
FaithfulnessAnswer supported by retrieved text?
Answer relevanceActually addresses user question?
Latency / costProd viability

LLM-as-judge (carefully): prompt a model to score faithfulness 1–5 with the context attached—cheap triage, not courtroom evidence.

Human processAutomation helper
Spot-check 20 queriesLog prompts + sources + answers
Label “good/bad”Train a lightweight classifier later

Try This! For one failure, ask: retrieval miss or generation ignored context? Fix the right layer.

Practice Exercises

ExerciseFocus
Loader bake-offPDF vs MD vs HTML cleanup
Chunk grid searchOverlap + size vs answer quality
Golden set30 questions with “must include” chunk IDs
SafetyPrompt injection via uploaded doc text

Mini-Project: Documentation Q&A Bot

Index your own repo’s README + docs/ → Slack or CLI bot → always show source filenames beside answers.

Key Takeaways

Key Takeaway

  • RAG = retrieve → stuff context → generate.
  • Chunking and metadata matter as much as the LLM choice.
  • Evaluate retrieval and answer separately before you tune prompts blindly.
  • Always plan for stale docs: version, re-ingest, or TTL.
  • Grounding reduces hallucinations—it does not delete the need for safety review.

Resources for Further Learning

← Previous: Embeddings | Next: Advanced RAG →