Vector RAG with LLMs

RAG stands for Retrieval-Augmented Generation. It combines a vector database with a large language model (LLM) like GPT or Claude, letting the AI answer questions using your own documents instead of relying only on what it learned during training. This topic shows you how to build a RAG system.

Why RAG Exists

LLMs have two key limitations on their own:

  • They know nothing about events or documents created after their training cutoff
  • They know nothing about your private data — your company documents, your codebase, your customer records

RAG solves both problems by retrieving relevant information from a vector database and inserting it into the prompt before the LLM generates its answer.

The RAG Pipeline

RAG ARCHITECTURE
─────────────────────────────────────────────────────────────

STEP 1: User asks a question
  "What is our company's refund policy?"
       │
       ▼
STEP 2: Embed the question
  Question → Embedding Model → Query Vector
       │
       ▼
STEP 3: Search the vector database
  Query Vector → Vector DB → Top 3 relevant document chunks
       │
       ▼
STEP 4: Build an augmented prompt
  "Using the following context, answer the question:
   
   Context: [retrieved chunk 1] [retrieved chunk 2] [retrieved chunk 3]
   
   Question: What is our company's refund policy?"
       │
       ▼
STEP 5: Send to LLM
  Augmented Prompt → LLM (GPT, Claude, etc.) → Final Answer
       │
       ▼
STEP 6: Return answer to user
  "Our refund policy allows returns within 30 days..."

Without RAG vs. With RAG

ScenarioWithout RAGWith RAG
Ask about private company policyLLM has no knowledge, makes something upLLM reads the actual policy document and answers accurately
Ask about recent eventsLLM's knowledge stops at training cutoffLLM uses retrieved current information
Ask about a specific PDF you uploadedLLM never saw that documentLLM retrieves relevant sections and answers from them

Building a Simple RAG System

Step 1: Index Your Documents

def index_documents(documents):
    for doc in documents:
        chunks = chunk_text(doc["content"])
        for i, chunk in enumerate(chunks):
            vector = get_embedding(chunk)
            index.upsert(vectors=[{
                "id": f"{doc['id']}-chunk-{i}",
                "values": vector,
                "metadata": {"text": chunk, "source": doc["title"]}
            }])

documents = [
    {"id": "policy-1", "title": "Refund Policy", "content": "Customers may return items within 30 days..."},
    {"id": "policy-2", "title": "Shipping Policy", "content": "Standard shipping takes 5-7 business days..."},
]

index_documents(documents)

Step 2: Retrieve Relevant Context

def retrieve_context(question, top_k=3):
    query_vector = get_embedding(question)
    results = index.query(
        vector=query_vector,
        top_k=top_k,
        include_metadata=True
    )

    context_chunks = [match["metadata"]["text"] for match in results["matches"]]
    return "\n\n".join(context_chunks)

Step 3: Build the Augmented Prompt and Call the LLM

from anthropic import Anthropic

client = Anthropic(api_key="YOUR_ANTHROPIC_KEY")

def ask_rag_question(question):
    context = retrieve_context(question)

    prompt = f"""Answer the question using only the context below.
If the context does not contain the answer, say you don't know.

Context:
{context}

Question: {question}"""

    response = client.messages.create(
        model="claude-sonnet-4-6",
        max_tokens=500,
        messages=[{"role": "user", "content": prompt}]
    )

    return response.content[0].text

answer = ask_rag_question("How long do I have to return an item?")
print(answer)

Expected Output

Based on the refund policy, you have 30 days from the date of
purchase to return an item.

The LLM grounds its answer in the retrieved document rather than guessing. This dramatically reduces hallucination — a major problem when LLMs answer questions outside their training knowledge.

Best Practices for Production RAG

Tell the LLM to Admit Uncertainty

Always instruct the model to say "I don't know" when the retrieved context lacks the answer. This single instruction prevents the model from fabricating plausible-sounding but false answers.

Cite Sources

Include the source document name with each retrieved chunk and ask the LLM to cite which source it used. This builds user trust and makes answers verifiable.

context_with_sources = "\n\n".join(
    f"[Source: {match['metadata']['source']}]\n{match['metadata']['text']}"
    for match in results["matches"]
)

Tune top_k Carefully

Retrieving too few chunks risks missing the answer. Retrieving too many wastes tokens and can confuse the model with irrelevant information.

top_k ValueEffect
1–2Fast, risks missing relevant context
3–5Balanced, works well for most use cases
10+Comprehensive but costly, can dilute relevance

Re-rank Before Sending to the LLM

Fetch more candidates than needed (such as 10), apply a re-ranking model, then send only the top 3–5 highest-quality chunks to the LLM. This improves answer accuracy without raising retrieval costs significantly.

Common RAG Failure Modes

ProblemCauseFix
LLM ignores the contextWeak prompt instructionsExplicitly instruct: "answer only using the context"
Wrong chunks retrievedPoor chunking strategyAdjust chunk size, add overlap
Outdated answersVector database not refreshedSet up scheduled re-indexing
Slow responsesToo many retrieved chunksReduce top_k, add re-ranking

RAG is the most common production use case for vector databases today. It powers AI chatbots that answer questions about company documentation, customer support systems, and internal knowledge assistants used across thousands of businesses.

Leave a Comment

Your email address will not be published. Required fields are marked *