CTCaliberTek
Course: RAG: End to End · Lesson 5 of 6Log in to track your progress and earn a certificate when you finish the course.
AI & Machine Learning

RAG Step 4: Generating Grounded Answers with Citations

Published October 1, 2026·Updated October 29, 2026·8 min read
Like SaveLog in to like, save, and track progress.

Introduction

Retrieval finds relevant text, but the answer is only as trustworthy as the instructions you give the model. This lesson assembles the prompt from numbered sources, tells the model to stay within them and to admit when the answer is missing, and returns citations the user can check.

Prerequisites

  • The retrieval pipeline lesson
  • The Anthropic Python SDK and an API key

Step-by-Step Guide

Step 1: Label each retrieved chunk

Number the sources and include where each came from. Numbered labels let the model cite with short markers, and your app can turn those markers back into file names and pages.

def build_prompt(question: str, hits: list[dict]) -> str:
    sources = []
    for n, hit in enumerate(hits, start=1):
        label = hit["source"]
        if hit.get("page"):
            label += ", page " + str(hit["page"])
        sources.append("[" + str(n) + "] (" + label + ")\n" + hit["text"])
    context = "\n\n".join(sources)
    return "<sources>\n" + context + "\n</sources>\n\nQuestion: " + question

Step 2: Write grounding instructions

Tell the model to use only the sources, cite them like [1], and say plainly when the sources do not contain the answer. An explicit refusal path is the best defense against made-up answers.

SYSTEM = (
    "You answer questions using only the numbered sources provided. "
    "Cite the sources you use like [1] or [2]. "
    "If the sources do not contain the answer, say: "
    "'I don't have enough information in the documents to answer that.' "
    "Do not use outside knowledge."
)

Step 3: Generate the answer

Combine retrieval and generation in one function. If retrieval returns nothing above the threshold, skip the model call and return the fallback message, which saves cost and avoids a speculative answer.

import anthropic

client = anthropic.Anthropic()

def answer(question: str, store: "VectorStore") -> dict:
    hits = store.search(question, k=4)
    if not hits:
        return {"answer": "I don't have enough information in the documents to answer that.", "sources": []}

    response = client.messages.create(
        model="claude-opus-5-5",
        max_tokens=16000,
        system=SYSTEM,
        messages=[{"role": "user", "content": build_prompt(question, hits)}],
    )
    text = next(b.text for b in response.content if b.type == "text")
    return {"answer": text, "sources": hits}

Step 4: Show citations to the user

Display the answer with the sources behind it so users can verify claims. Check that every citation marker in the answer maps to a source you actually provided, and flag answers with no citations for review.

result = answer("How many vacation days do new employees get?", store)
print(result["answer"])
for n, hit in enumerate(result["sources"], start=1):
    print("[" + str(n) + "]", hit["source"], hit.get("page"))

Step 5: Consider built-in citation features

Some APIs, including Claude's, can attach citations to source documents natively when you pass documents as content blocks, returning exact cited passages. That can replace hand-built numbering, though it has its own constraints, such as not combining with structured output, so check the documentation for your model.

FAQs

What if the model ignores the instruction to use only the sources?
Strengthen the instructions, test with questions that are not in the documents, and measure how often answers are unsupported. Evaluation, covered next, is how you catch this reliably.
Should I include conversation history in RAG chats?
Yes, but rewrite follow-up questions into standalone ones before retrieval, for example 'What about contractors?' becomes a full question, so the search has the context it needs.

Summary

Number the sources, instruct the model to use only them and to admit gaps, return citations, and skip generation when retrieval finds nothing relevant. This is what makes RAG answers checkable.

Related tutorials

Comments

Log in or create a free account to join the discussion.

  • No comments yet. Be the first to comment.