Open this lesson in your favourite AI. It'll walk you through the why, explain the demo, and quiz you on the try-it list.
A RAG answer without citations is unverifiable. Use Anthropic's Citations API (built-in span citations) OR prompt-engineer [#N] markers and render them as clickable links. Either way, every claim gets a source — the user trusts the answer because they can check; you debug failures because you can trace.
Anthropic Citations API: pass documents as content blocks with citations enabled; the response comes with citation spans referencing source passages. For other providers, use a strict system prompt requiring [#N] markers per claim, parse them post-hoc, render as clickable badges. Either way, also include a 'Sources' section listing each cited chunk with section_path + URL.
Use these three in order. Each builds on the one before.
Why do citations matter for RAG? Two reasons beyond 'looks nicer'.
Walk me through Anthropic Citations API. What's returned in the response?
Design a citation UX for long answers (15+ cites). How do you avoid clutter?
from anthropic import Anthropic
client = Anthropic()
def answer(question, chunks):
response = client.messages.create(
model="claude-sonnet-4-6",
max_tokens=600,
system="Answer using ONLY the provided documents. Cite every factual claim. If absent, say you don't know.",
messages=[{
"role": "user",
"content": [
*[{
"type": "document",
"source": {"type": "text", "media_type": "text/plain", "data": c["text"]},
"title": c["metadata"].get("source_url", c["id"]),
"citations": {"enabled": True},
} for c in chunks],
{"type": "text", "text": question},
],
}],
)
out = {"text": "", "citations": []}
for block in response.content:
if block.type == "text":
out["text"] += block.text
if hasattr(block, "citations"):
for c in block.citations:
out["citations"].append({
"title": c.document_title,
"cited_text": c.cited_text,
})
return out
# Render in the UI: inline cite badges (hover for preview) + bottom 'Sources' listpython3 main.py