intermediate22 min

Reranking and Citations

Improve retrieval order and make every claim traceable back to its source.

What you'll learn

  • Explain what a reranking step does differently from initial retrieval
  • Deduplicate retrieved chunks by source
  • Attach a citation label to generated content

Prerequisites

Explanation

Initial retrieval (embedding similarity, or hybrid search) is built for speed: it scans potentially millions of chunks and quickly narrows them down to a shortlist — say, the top 20. But "fast enough to search everything" and "precise enough to trust blindly" are different goals. Reranking takes that shortlist and re-scores it with a slower, more accurate model (often one specifically trained to judge query/passage relevance pairs), producing a better-ordered final top 3-5 to actually hand to the language model.

This two-stage design (broad-and-fast, then narrow-and-precise) is a common, practical pattern: retrieval alone is optimized for scale, reranking is optimized for accuracy on an already-small candidate set, where its higher cost is affordable.

Before or during reranking, you typically want to deduplicate: if three chunks all came from the same source document (or are near-duplicates in content), keeping all three wastes context window budget on redundant information instead of diverse, useful coverage. A simple approach is keeping only the highest-scoring chunk per source document, or per near-duplicate content group.

Once you've settled on a final set of chunks, citations mean attaching a reference back to each chunk's source — its document title, section heading, or link — so the generated answer can point to exactly where each claim came from. This isn't just a nicety: it's what lets a user verify a claim themselves, and it's a strong deterrent against the system quietly blending in unsupported claims, since every sentence attributed to a source needs an actual source behind it. A well-built RAG system labels retrieved chunks (e.g. "[1]", "[2]") in the prompt itself and instructs the model to include those same labels next to any claim drawn from that chunk, which is also how you'll implement the "not enough evidence" honesty check in the next lesson — if nothing was retrieved above a relevance threshold, there's nothing to cite, and the system should say so instead of generating an answer anyway.

Example

Deduplicating retrieved chunks by source, then attaching citation labels.

const retrieved = [
  { text: "Refunds within 30 days.", source: "policy.md", score: 0.91 },
  { text: "Refund window is 30 days from purchase.", source: "policy.md", score: 0.88 },
  { text: "Shipping takes 3-5 days.", source: "shipping.md", score: 0.75 },
];

function dedupeBySource(chunks) {
  const bestPerSource = new Map();
  for (const chunk of chunks) {
    const existing = bestPerSource.get(chunk.source);
    if (!existing || chunk.score > existing.score) {
      bestPerSource.set(chunk.source, chunk);
    }
  }
  return [...bestPerSource.values()].sort((a, b) => b.score - a.score);
}

function withCitations(chunks) {
  return chunks.map((chunk, i) => ({ ...chunk, citation: `[${i + 1}] ${chunk.source}` }));
}

console.log(withCitations(dedupeBySource(retrieved)));

Try it yourself

Add a third source document to the retrieved list and see it survive deduplication.

Code editor. Press Escape then Tab to leave the editor if keyboard focus becomes trapped. Press Control+Shift+M inside the editor to toggle Tab-key focus trapping.

Loading editor…

Guided exercise

Guided exercise

Complete `dedupeBySource(chunks)` (each chunk is `{ text, source, score }`) so it keeps only the highest-scoring chunk per unique source, returned in any order.

Checks: Produces one result per unique source · plus 1 hidden check

Code editor. Press Escape then Tab to leave the editor if keyboard focus becomes trapped. Press Control+Shift+M inside the editor to toggle Tab-key focus trapping.

Loading editor…

Stuck? Get a hint.

Independent exercise

Independent exercise

Write `attachCitations(chunks)` (each chunk is `{ text, source }`) that returns a new array of objects each with the original `text`, `source`, and an added `citation` field formatted exactly as `[N] source` where N is the 1-based position in the array.

Checks: First chunk gets citation [1] · plus 1 hidden check

Code editor. Press Escape then Tab to leave the editor if keyboard focus becomes trapped. Press Control+Shift+M inside the editor to toggle Tab-key focus trapping.

Loading editor…

Stuck? Get a hint.

Common mistakes

  • Skipping deduplication and feeding the model three near-identical chunks from the same source, wasting context budget.
  • Generating an answer with confident claims but no way to trace any of them back to a specific source.
  • Assuming reranking and initial retrieval are the same step — reranking is a deliberate second, more precise pass.

Knowledge check

Knowledge check

1. What is the main purpose of a reranking step?
2. Why deduplicate retrieved chunks by source before generation?
3. What does attaching a citation to a generated claim let a user do?

Takeaway

Rerank for precision, dedupe for context efficiency, and cite every claim so it can be checked against its source.

Summary

Reranking re-scores an initial retrieval shortlist with a more precise method, deduplication keeps redundant same-source chunks from crowding out the context window, and citations attach a traceable source label to generated claims.

References

Your notes

Notes save automatically.

Finished this lesson?

Mark it complete to track your progress and schedule a future review.