Skip to content
bits
Retrieval without tears · Lesson 7 of 9

Answers with sources

Tutor:

You've found the right chunks. Now hand them to Claude so that its answer sticks to them, and so you can show the reader where each claim came from.

Claude's API has a block made for this: the search result. Each one carries a source (a URL or any id), a title and the text. Turn on citations, and Claude's reply comes back split into pieces, each pointing at the result it used. No special prompting needed.

import Anthropic from "@anthropic-ai/sdk";
const client = new Anthropic();
type Hit = { source: string; title: string; text: string };
export async function answer(question: string, hits: Hit[]) {
const res = await client.messages.create({
model: "claude-haiku-5-5",
max_tokens: 1024,
messages: [{
role: "user",
content: [
...hits.map((hit) => ({
type: "search_result" as const,
source: hit.source,
title: hit.title,
content: [{ type: "text" as const, text: hit.text }],
citations: { enabled: true },
})),
{ type: "text" as const, text: `Answer using only these search results. If they don't contain the answer, say so.\n\nQuestion: ${question}` },
],
}],
});
const sources = new Set<string>();
for (const block of res.content) {
if (block.type !== "text") continue;
process.stdout.write(block.text);
for (const cite of block.citations ?? []) {
if (cite.type === "search_result_location") sources.add(`${cite.title} (${cite.source})`);
}
}
console.log(`\n\nSources:\n${[...sources].join("\n")}`);
}
TypeScriptUTF-8● lines 14–20: one search result per hit
Tutor:

Example output (yours will be worded differently):

OUTPUT
npx tsx ask.ts "Can I claim on an 18-month-old battery?"
Yes. E-bike batteries have a 2-year warranty from delivery, so an 18-month-old battery is covered if its capacity has dropped below 70%. File the claim within 30 days of noticing the fault.
Sources:
Warranty guide: batteries (https://docs.orbitbikes.example/warranty#batteries)
Warranty guide: claims (https://docs.orbitbikes.example/warranty#claims)
example output, not a live run
Tutor:

Each citation includes cited_text, the exact passage Claude relied on, so you can show it on hover or link to it. Three things worth knowing, from Anthropic's docs. The pointers are guaranteed to point at text you sent. cited_text doesn't count toward output tokens. And citations can't be combined with structured outputs (JSON mode): the request fails with a 400.

Two habits from Anthropic's long-context advice. Put the results first and the question last; that order scored up to 30% better in their tests. And tell Claude what to do when the results don't answer the question. Retrieval sometimes misses, and “I couldn't find that” beats a confident guess.

quick check+10 neurons
Tutor:

You want Claude's answer as JSON via structured outputs, with API citations on your search results. What happens?

    Type your answer or tap an option · Enter checks

    Tutor:

    Your turn, the low-tech version. This sandbox sends Claude plain text, so you'll ask for citations in the prompt itself, the way you would with any model. The passages and a question are in the box. Tell Claude to answer only from them, cite each claim like [1], and say so if they don't cover something. The question asks two things, and the passages only answer one.

    Your turn

    Run your prompt and the replies land here.

      • Limits Claude to the passages, not yet
      • Asks for citations, not yet
      • Says what to do when it's missing, not yet
      • Ran at 1.0, not yet
      run at
      ~0 tokens

      Turn on JavaScript to run prompts.