LESSON 53 Β· RAG

Building Basic RAG

Build a simple Retrieval-Augmented Generation system step by step with Pythonβ€”from documents and chunks to embeddings, retrieval, context, and a grounded answer.

40 min readβ€’Beginnerβ€’Generative AI

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

  • Build the core stages of a small RAG system.
  • Prepare documents and split them into retrieval chunks.
  • Create embeddings for chunks and a user query.
  • Retrieve the most relevant chunks with similarity search.
  • Construct a context prompt from retrieved evidence.
  • Generate an answer that is grounded in the retrieved context.
  • Understand which parts are simplified in this teaching example.
1

What We Are Building

A basic RAG application has two connected flows. First, we prepare knowledge. Then, when a question arrives, we retrieve relevant knowledge and give it to an LLM.

DocumentsSource knowledge
β†’
ChunkSmall retrieval units
β†’
EmbedVectors
β†’
StoreSearchable index
QuestionUser query
β†’
RetrieveRelevant chunks
β†’
ContextEvidence
β†’
LLMAnswer
Goal:Build the smallest useful RAG loop first. Production systems can add vector databases, reranking, filters, evaluation, security, and monitoring later.
2

Step 1: Start With Small Documents

For learning, use a short list of documents instead of beginning with PDFs or a large database. Each document should contain information that can answer a realistic question.

DOCUMENT 1Refund policy

Customers can request a refund within 30 days of purchase.

DOCUMENT 2Shipping policy

Standard shipping normally takes 3 to 5 business days.

DOCUMENT 3Support policy

Business plans include priority support during business hours.

These examples are intentionally small. The same architecture can later work with larger document collections.

3

Step 2: Represent the Knowledge

Store each piece of knowledge as text plus optional metadata. Metadata helps you trace a result back to its source and can later support filtering.

TEXTRefunds are allowed within 30 days.Evidence for generation
SOURCErefund-policyTraceability
TYPEpolicyUseful metadata
ACCESSpublicAuthorization scope
4

Step 3: Chunk the Documents

Real documents are usually too large to treat as one retrieval unit. Split them into chunks that preserve useful meaning.

TOO LARGE

Entire manual

A query about refunds may retrieve a huge document containing installation, shipping, troubleshooting, and warranty information.

β†’
BETTER UNIT

Refund section

A focused chunk can be retrieved and placed into the model context without carrying unrelated content.

</> Python
text = "Refunds are allowed within 30 days. Shipping takes 3 to 5 business days."

# Very simple teaching example: split into sentences.
chunks = [sentence.strip() for sentence in text.split(".") if sentence.strip()]

for chunk in chunks:
    print(chunk)

This is not a production-grade chunker. It is a simple way to see where chunking fits in the pipeline.

5

Step 4: Create Embeddings

An embedding model converts each chunk into a vector. Similar meanings should produce vectors that are useful for similarity search.

Text chunkβ€œRefunds are allowed within 30 days.”
β†’
Embedding modelText β†’ vector
β†’
Vector[0.12, -0.08, …]
</> Python
from sentence_transformers import SentenceTransformer

model = SentenceTransformer("all-MiniLM-L6-v2")

chunks = [
    "Refunds are allowed within 30 days.",
    "Standard shipping takes 3 to 5 business days.",
    "Business plans include priority support."
]

vectors = model.encode(chunks)
print(vectors.shape)

The exact embedding model is a design choice. What matters here is the flow: text β†’ embedding vectors β†’ similarity search.

6

Step 5: Store the Vectors

A basic prototype can keep vectors in memory. A real application will usually use a vector index or vector database when the collection becomes larger or needs persistence.

PROTOTYPE

Python memory

Simple and useful for understanding the algorithm.

LOCAL SEARCH

FAISS

Useful for fast vector similarity search in many local applications.

VECTOR DATABASE

Qdrant / Pinecone

Useful when persistence, filtering, scaling, or managed infrastructure matters.

ALWAYS KEEP

Source mapping

Store the original chunk and metadata alongside its vector.

7

Step 6: Create the User Query

When the user asks a question, embed the question using the same embedding space used for the stored chunks.

QUESTIONβ€œHow long do I have to request a refund?”

The user may use different wording from the source document.

QUERY VECTOREmbedding of the question

The vector lets us compare the query with stored chunk vectors.

8

Step 7: Retrieve Relevant Chunks

Compare the query vector with stored vectors and select the highest-scoring chunks. This is the retrieval step.

</> Python
from sentence_transformers import SentenceTransformer
from numpy import dot
from numpy.linalg import norm

model = SentenceTransformer("all-MiniLM-L6-v2")
chunks = [
    "Refunds are allowed within 30 days.",
    "Standard shipping takes 3 to 5 business days.",
    "Business plans include priority support."
]

chunk_vectors = model.encode(chunks)
query = "How long do I have to request a refund?"
query_vector = model.encode([query])[0]

def cosine(a, b):
    return dot(a, b) / (norm(a) * norm(b))

scores = [cosine(query_vector, vector) for vector in chunk_vectors]
ranked = sorted(zip(chunks, scores), key=lambda item: item[1], reverse=True)

for text, score in ranked:
    print(round(score, 3), text)

The highest-scoring result should be the refund chunk for this simple example. In a production system, use an appropriate vector index or vector database rather than scanning every vector manually.

9

Step 8: Choose Top-K

Usually, you do not send every retrieved chunk to the LLM. Top-K controls how many candidates are selected for the next stage.

K = 1

Very focused

Low context size, but the system can miss useful supporting evidence.

↔
K = 5

More evidence

Higher recall can help, but more context can increase cost and noise.

There is no universally correct K. Choose it using retrieval evaluation and the type of questions your application receives.

10

Step 9: Build the Context

The retrieved chunks become evidence for the LLM. A simple context builder can label each source and join the selected text.

</> Python
retrieved = [
    {"source": "refund-policy", "text": "Refunds are allowed within 30 days."},
    {"source": "shipping-policy", "text": "Standard shipping takes 3 to 5 business days."}
]

context = "\n\n".join(
    f"Source: {item['source']}\n{item['text']}"
    for item in retrieved
)

print(context)

Good context is relevant, concise, and traceable. Avoid dumping every available document into the prompt.

11

Step 10: Augment the Prompt

RAG adds the retrieved evidence to the user question before sending the request to the generation model.

System instructionsHow the assistant should behave
+
Retrieved contextEvidence from the knowledge base
+
User questionWhat the user wants to know
β†’
LLM inputGrounded generation request
Important:Retrieval supplies evidence; it does not automatically guarantee a correct answer.
12

Step 11: Generate a Grounded Answer

The LLM receives the question plus selected context. A strong prompt should tell the model to use the supplied evidence and avoid inventing facts that are not supported.

</> Python
question = "How long do I have to request a refund?"
context = "Refunds are allowed within 30 days."

prompt = f"""Answer the question using only the context below.
If the context does not contain the answer, say that you do not have enough information.

Context:
{context}

Question:
{question}
"""

print(prompt)

This example prepares the prompt but does not call a paid or hosted LLM. An application would send this prompt to its selected generation model.

13

Step 12: Return the Answer and Source

A useful RAG application should preserve source information so the application can show where the evidence came from.

ANSWERYou can request a refund within 30 days.Generated response
SOURCErefund-policyEvidence location
CHUNKRefund sectionRetrieved evidence
TRACEQuery β†’ resultUseful for debugging

Source labels or citations should correspond to the actual retrieved evidence. A citation by itself does not make an unsupported answer correct.

14

Putting the Basic RAG Loop Together

Here is the complete teaching model in one place.

DocumentsKnowledge
β†’
ChunksRetrieval units
β†’
EmbeddingsVectors
β†’
IndexSearchable store
QuestionUser query
β†’
Query embeddingVector
β†’
Similarity searchCandidates
β†’
Top-KEvidence
ContextSelected chunks
+
QuestionUser intent
β†’
LLMGeneration
β†’
AnswerResponse + source
15

A Compact Python Prototype

This prototype demonstrates the retrieval portion of a basic RAG system. It intentionally keeps the index in memory so the logic is easy to understand.

</> Python
from sentence_transformers import SentenceTransformer
from numpy import dot
from numpy.linalg import norm

model = SentenceTransformer("all-MiniLM-L6-v2")

knowledge = [
    {"source": "refund-policy", "text": "Refunds are allowed within 30 days."},
    {"source": "shipping-policy", "text": "Standard shipping takes 3 to 5 business days."},
    {"source": "support-policy", "text": "Business plans include priority support."},
]

vectors = model.encode([item["text"] for item in knowledge])
query = "How long can I request a refund?"
query_vector = model.encode([query])[0]

def cosine(a, b):
    return dot(a, b) / (norm(a) * norm(b))

ranked = sorted(
    zip(knowledge, vectors),
    key=lambda item: cosine(query_vector, item[1]),
    reverse=True,
)

top_k = ranked[:2]
context = "\n\n".join(item[0]["text"] for item in top_k)

print("Retrieved context:")
print(context)

To turn this into a complete application, connect the selected context to an LLM API and return the generated answer with the retrieved source metadata.

16

What This Prototype Simplifies

01In-memory vectors

A real system may use FAISS, Qdrant, Pinecone, or another index.

02Simple chunking

Production chunking should respect document structure and retrieval goals.

03One embedding model

Real applications must choose and evaluate an embedding model for their data.

04No reranking

A second-stage reranker can improve ordering when basic retrieval is not enough.

05No access control

Production systems must prevent users from retrieving documents they are not allowed to see.

06No evaluation loop

You need representative questions and metrics to know whether changes improve the system.

17

Basic RAG vs Production RAG

Basic learning prototype
  • Small text collection
  • In-memory vectors
  • Simple chunking
  • Basic similarity search
  • Simple prompt
Production system
  • Document ingestion pipeline
  • Persistent vector database
  • Metadata filtering
  • Hybrid search and/or reranking
  • Security, evaluation, monitoring, and cost controls

The prototype teaches the architecture. Production engineering adds reliability, scale, security, observability, and continuous evaluation.

18

When Basic RAG Works Well

FAQ

Support assistant

Retrieve answers from a controlled FAQ or help center.

DOCS

Knowledge assistant

Search internal documentation and explain relevant sections.

POLICIES

Policy Q&A

Retrieve the correct policy section before generating an answer.

PRODUCT

Product information

Ground responses in current product documentation.

19

Common Mistakes

01Sending all documents

Retrieval exists to reduce the knowledge sent to the model.

02Ignoring chunk quality

Bad boundaries can make otherwise good documents difficult to retrieve.

03Using the wrong K

Too few chunks can miss evidence; too many can add noise.

04Trusting every answer

RAG reduces unsupported generation but does not eliminate model errors.

05Losing source metadata

Without source mapping, debugging and user trust become harder.

06Skipping evaluation

Measure retrieval and answer quality instead of judging the system by a few examples.

20

How to Improve Your Basic RAG

STEP 1

Improve chunking

Use structure-aware boundaries and appropriate chunk sizes.

STEP 2

Improve embeddings

Evaluate models against your actual query and document distribution.

STEP 3

Add a vector index

Use an efficient searchable index as the collection grows.

STEP 4

Add filters

Restrict retrieval by tenant, product, language, date, or access scope.

STEP 5

Add reranking

Reorder candidates when first-stage retrieval is not precise enough.

STEP 6

Evaluate

Test retrieval recall, ranking, groundedness, answer quality, latency, and cost.

21

Practice: Build Your First RAG Flow

Imagine a company has 20 help-center articles. A user asks: β€œHow long does standard shipping take?”

TASK

Trace the seven key steps

Write down: 1) source article, 2) chunk, 3) chunk embedding, 4) query embedding, 5) Top-K retrieval, 6) context prompt, and 7) grounded answer. Also decide what source metadata you would return.

Quick Quiz

1. What is the first major task when building RAG?

Prepare the knowledge: collect, clean, structure, and chunk the source content.

2. Why are embeddings created?

They provide vector representations that can be compared for semantic similarity.

3. What does Top-K control?

It controls how many high-ranking retrieval candidates are selected for the next stage.

4. What does augmentation mean?

It means adding selected retrieved evidence to the model input along with the user question.

5. Does retrieval guarantee a correct answer?

No. The evidence can be wrong, incomplete, poorly ranked, or misunderstood by the model.

6. Why keep source metadata?

It supports traceability, filtering, citations, debugging, and access control.

30-Second Recap

1. Prepare

Collect and clean knowledge.

2. Chunk

Create useful retrieval units.

3. Embed

Convert chunks into vectors.

4. Retrieve

Find relevant candidates for a query.

5. Select

Use Top-K, filters, or reranking.

6. Context

Build concise evidence for the LLM.

7. Generate

Answer using the retrieved context.

8. Evaluate

Measure retrieval and answer quality.

Key Takeaways

  • A basic RAG system connects a knowledge source to an LLM through retrieval.
  • The core flow is documents β†’ chunks β†’ embeddings β†’ index β†’ query β†’ retrieval β†’ context β†’ generation.
  • Top-K controls how much retrieved evidence moves forward.
  • Good context should be relevant, concise, and traceable to a source.
  • A simple in-memory Python prototype is useful for learning, but production systems need stronger infrastructure.
  • Vector databases, metadata filters, hybrid search, and reranking can improve larger RAG systems.
  • RAG can reduce unsupported answers, but it does not guarantee correctness.
  • Evaluation, security, monitoring, freshness, latency, and cost become important as the application moves toward production.