The Manual RAG Pipeline: 'Embed It and RAG It' — and Where It Works

~11 min read

The book's framing of the naive default approach to context retrieval: embed the data, store it in a vector DB, do RAG. It works beautifully for static sources — the next subtopic covers exactly where it doesn't.

This course opens this topic with a deliberately relatable scenario: imagine you have data that's spread across several sources (Gmail, Drive, etc.). How would you build a unified query engine over it? Devs would typically treat context retrieval like a weekend project ...and their approach would be: 'Embed the data, store in a vector DB and do RAG.'

This is the manual RAG pipeline this subtopic covers — and it's worth being precise about what it actually involves, since the rest of this topic contrasts against it directly. The recipe is exactly the standard RAG workflow covered elsewhere in this curriculum's RAG & Vectors material: take your source documents, chunk them, embed each chunk (turning text into vectors, per the embeddings-basics prerequisite topic), store those vectors in a vector database, and at query time, embed the incoming query and retrieve the most similar stored chunks to assemble as context for the LLM.

This course is direct about this approach's real strength: this works beautifully for static sources. If your data is a fixed collection of documents — a product manual, a policy handbook, a knowledge base that doesn't change moment to moment — this pipeline is genuinely the right tool. It's well-understood, has mature tooling (the vector databases covered in vector-search-basics), and doesn't need to solve any problem beyond 'find the most similar chunks to this query,' which is exactly what embeddings and vector search are built for.

But this course pivots immediately and firmly: but the problem is that no real-world workflow looks like this. This is the crucial setup for the rest of the topic — the manual RAG pipeline isn't WRONG, it's simply solving a narrower problem (single or few STATIC sources) than most real organizational data actually presents. Real enterprise information lives scattered across many different, constantly-changing systems — email, chat, project trackers, calendars — each with its own access rules and its own rate of change, a situation the manual pipeline's single 'embed once, query forever' assumption doesn't hold up against. The next subtopic makes this concrete with this course's own motivating example.

💻 Code example

# The manual RAG pipeline, exactly as the book frames it: embed once,
# store in a vector DB, retrieve at query time -- genuinely fine for
# a STATIC, single-source knowledge base.

import math

def toy_embed(text: str) -> list[float]:
    """Toy embedding stand-in (real code would call an embedding model,
    per the embeddings-basics topic)."""
    words = text.lower().split()
    return [words.count(w) for w in ["refund", "policy", "shipping", "account"]]

def cosine_similarity(a, b):
    dot = sum(x * y for x, y in zip(a, b))
    na, nb = math.sqrt(sum(x**2 for x in a)), math.sqrt(sum(y**2 for y in b))
    return dot / (na * nb) if na and nb else 0.0

class ManualRagPipeline:
    """The 'embed it and RAG it' default -- a single static source,
    embedded once, queried many times."""
    def __init__(self, static_documents: list[str]):
        # "Embed the data" -- done ONCE, upfront, since the source is static
        self.vector_store = [(doc, toy_embed(doc)) for doc in static_documents]

    def query(self, user_query: str, top_k: int = 1) -> list[str]:
        """"Do RAG": embed the query, retrieve the most similar chunks."""
        query_vec = toy_embed(user_query)
        ranked = sorted(self.vector_store,
                        key=lambda item: cosine_similarity(item[1], query_vec), reverse=True)
        return [doc for doc, _ in ranked[:top_k]]

static_policy_docs = [
    "Our refund policy allows returns within 30 days of purchase.",
    "Shipping typically takes 3-5 business days within the country.",
]

pipeline = ManualRagPipeline(static_policy_docs)
results = pipeline.query("What is the refund policy?")
print("Retrieved:", results)
# This works great -- but only because the source (a static policy
# handbook) doesn't change moment to moment and lives in ONE place

💬 Deep Dive with AI

Key points

  • The book's framing of the default developer approach: embed the data, store it in a vector DB, and do RAG — the standard manual pipeline
  • This recipe matches the standard RAG workflow: chunk documents, embed each chunk, store vectors, retrieve top-k similar chunks at query time
  • The book is direct that this works beautifully for STATIC sources — a fixed document collection with mature, well-understood tooling
  • The book's pivot is equally direct: no real-world workflow looks like this — most real data lives scattered across many different, constantly-changing systems
  • The manual pipeline isn't wrong, it's solving a narrower problem (few static sources) than what most real enterprise data actually presents