How To · LLM TechniqueIntermediate

How to Add Citations to RAG Answers

Add RAG citations that cite sources at the span level: grounded answers with links, refusal when retrieval is empty, UI highlights, and faithfulness checks.

Chunk offsetsClaim splitterCitation verifierRefusal policySource UI14 min · 6 steps · Updated 2026-08-25
The short answer

To add citations to RAG answers, retrieve chunks with stable IDs and offsets, require every factual claim to point at one of those IDs, refuse when nothing supports the claim, render grounded answers with links and span highlights, and verify faithfulness so the model cannot invent sources.

What you’ll build

  • Every factual sentence points at a chunk id, URL, and span — or the turn is a refusal
  • Span-level highlights in the source pane, not a footnote to a 40-page PDF
  • A verifier that drops or flags claims the retrieved text does not support
  • No fabricated URLs, page numbers, or quote marks around paraphrases the source does not contain
  • Faithfulness scores on a weekly sample, with thumbs-down routed to missing-source tickets

Before you start

  • 01A retriever that returns chunk id, source URL, and character (or token) offsets — not only raw text
  • 02A generator that can emit structured citations, or a post-hoc linker you will accept as v1
  • 03A golden set of questions with gold passages, including some that must be refused
  • 04A UI that can open a source and highlight a span, even if v1 is just a link
  • 05Agreement that an answer without a source is a defect, not a style choice

Key takeaways

  • 01

    RAG citations are a contract: no unsupported sentence ships. The UI is how users audit that contract.

  • 02

    Pass allowed source IDs into a structured output schema; never let the model mint a new one.

  • 03

    Span-level offsets beat document-level footnotes; quote only when the text matches.

  • 04

    Post-hoc citation (answer first, link later) is a v1 — pair it with a verifier or you will decorate hallucinations.

  • 05

    Empty retrieval and failed verification are refusals, not hedging.

01

Cite sources in LLM answers as a contract, not a footnote

A RAG citation exists so a reader can audit a sentence. That means a stable source ID, a URL they can open, and a span they can see. Filename footnotes, 'according to internal docs,' and markdown links the model dreamed up do not meet that bar. If you cannot highlight the supporting text, you do not have RAG citations — you have decoration.

The contract is simple to state and easy to violate: every factual claim either points at retrieved evidence or the system refuses. Opinions, chitchat, and 'I don't know' are exempt. Policy numbers, dates, prices, and names are not. Write that in the product spec before you argue about superscript vs hover cards.

02

Span-level citations vs document-level links

Document-level citations ('source: Travel Policy') send the reader hunting. Chunk-level is better. Span-level is what users trust: start and end offsets into the chunk, highlighted on click. Store those offsets at ingest. If you only stored embeddings, you will be regex-matching the quote at request time and failing on harmless whitespace.

Multi-hop answers need more than one span. Split the completion into claims (sentence-level is a decent default) and map each claim to one or more spans. 'Parental leave is 16 weeks and starts after 6 months' is two claims, possibly two documents. A single footnote under the paragraph hides a miss on the second number.

  • Legal citation object: chunk_id, url, title_path, page, start, end
  • Inline marker or structured claims[] — pick one and parse it in code
  • Verbatim quote: character match after whitespace fold, or do not use quote marks
  • Unknown id in the completion: drop the claim, do not look the id up in the corpus
03

Constrained generation vs post-hoc linking

Constrained generation is the production default: the schema lists allowed source IDs, the model must attach them, and the parser rejects extras. Inline markers like [S12] work if S12 was in the pack. Do not ask the model to emit raw URLs; it will autocomplete a plausible path.

Post-hoc linking (generate freely, then retrieve quotes that support each sentence) is faster to prototype and worse at honesty. It can staple a real link onto an invented fact if the overlap is coincidental. If you start there, add a verifier that requires the cited span to entail the claim, and still refuse when retrieval was empty before generation.

04

Refusal without sources, and the UI

Empty retrieval, permission-filtered-empty, and 'we retrieved three chunks and none entail the claim' are the same user-visible outcome: we do not have a source, here is who to ask. Do not distinguish 'no document' from 'a document you cannot see' — that is an existence leak. Do not hedge ('it might be 16 weeks'). Hedging is still an unsourced number.

The UI should make the contract visible. Click a sentence, see the span. Show a source pane. If verification failed and you still show a draft to an operator, mark it unverified in a way a customer never sees. Grounded answers with links are a product surface, not an afterthought in markdown.

05

Faithfulness checks that catch fake citations

Three failure modes: unsupported claim (no span), contradictory span (the source says the opposite), and fabricated citation (id or URL not in the pack). The first two need a textual entailment check — overlap heuristics catch copies, a small NLI or judge catches paraphrases. The third is a set-membership check in code and should be zero.

Put those checks in CI on a golden set that includes unanswerable questions and look-alike policies. Weekly, sample live traces: citation coverage (claims with a valid id), verifier fail rate, and user clicks on sources. If users never click, your highlights may be wrong — or the answers may be too vague to audit.

06

What we implement

The existing RAG overview on this site stops at 'require citations.' This page is the rest: offsets, claim mapping, verifier, refusal, UI. ReinforcedX implements that layer in your stack on top of the retriever you already have, or as part of a four-week RAG build.

You own the index metadata, evals, and runbooks. Work stays in your VPC. Model-agnostic, no token markup, 30 days on-call after handover. We will not ship a footnote list that the model is free to invent.

Step-by-step build

  1. 1

    Make retrieval citation-ready

    Return chunk_id, document_id, URL, title path, page, and start/end offsets on every hit; if the index cannot store offsets, fix the index before you touch the prompt.

  2. 2

    Number the evidence pack

    Give the generator a short list of sources with those IDs and a truncated quote; the only legal citations are IDs from that list.

  3. 3

    Require per-claim citations in the schema

    Have the model emit claims[] with text plus source_ids[], or inline markers that you parse; drop any claim whose IDs are missing or unknown.

  4. 4

    Verify before render

    Run a faithfulness check (NLI, overlap, or a small judge) from claim to cited span; strip or refuse claims that fail, and never display a URL the retriever did not return.

  5. 5

    Render links and highlights

    In the UI, attach each sentence to a clickable source and highlight the span; show quote marks only on verbatim matches.

  6. 6

    Refuse and measure

    On empty retrieval or total verification failure, return a refusal plus 'ask the owner'; track citation coverage, fabricated-id rate, and faithfulness weekly.

Common pitfalls

The mistakes that show up in real deployments — each one costs a week if you learn it the hard way.

Footnotes to the whole document

Citing 'HR policy.pdf' after a three-sentence answer is theater. The user cannot see the clause. Store offsets and open the span.

Letting the model invent citation IDs

If the prompt says 'cite [1][2]' and the model has no constrained list, it will cite [3] from a document you never retrieved. Pass the allowed IDs in the schema and reject anything else in code.

Quote marks around a paraphrase

Users treat quotation marks as a verbatim guarantee. If the span is a paraphrase, do not quote it. If you quote, the characters must match the source after whitespace fold.

Citing after a synthesis with no mapping

Multi-source answers need per-claim citations. A pile of links at the bottom does not tell you which number came from which table.

No refusal path

When retrieval is empty or the verifier fails, the model should say it cannot find a source — not 'answer anyway with low confidence.' Low confidence still ships a sentence.

Frequently asked questions

How do RAG citations work?

The retriever returns chunks with stable IDs and offsets. The generator may only cite those IDs, one or more per factual claim. The app verifies that the cited span supports the claim, then the UI opens the source at that span. If no ID supports the claim, the system refuses instead of answering.

How do I cite sources in an LLM so answers are grounded with links?

Put the allowed source list in a structured schema, parse citations in code, reject unknown IDs, and render each claim as a link plus a highlight. Do not ask the model to type URLs. Pair this with a faithfulness check or you will attach real links to unsupported sentences.

What is a span-level citation?

A pointer to start and end offsets inside a retrieved chunk, not merely to a document. The UI highlights those characters. Span-level citations are how a reader checks a number without reading a 40-page policy. They require offsets at ingest and a parser that will not accept a bare filename.

Should the agent answer if retrieval is empty?

No. Empty retrieval, including permission-filtered empty, is a refusal. Offer a document owner or a ticket path. An unsourced 'best guess' is how RAG agents get banned by legal. Hedging language does not make the guess sourced. Show the same empty state whether the miss is a missing doc or a doc the user cannot see.

Can you add citation UI without replacing our retriever?

Yes, if the retriever can expose chunk IDs and offsets (or you add offsets on ingest). ReinforcedX implements the schema, verifier, refusal path, and source pane in your cloud. You own datasets, evals, and runbooks. Four weeks, 30 days on-call, no token markup.

Want this built for your team?

From architecture review to a deployed, evaluated system your engineers own — we ship it with you.

FAQ

Working with us

How soon can this build work start?

Typically within a week or two of a scope being agreed. The first delivery is deliberately a small batch so you can check the output against your expectations before volume ramps.

What do you need from our team?

One process owner who knows the workflow, one engineer with access to the systems involved, and a weekly 45-minute review. No standing committee, and no requirement for an ML specialist on your side.

Who owns the output and the data?

You do. Datasets, labels, weights, evaluation suites and runbooks are yours and are handed over at the end. Your data trains your models only, with zero-retention provider settings by default.

Can you scale volume up quickly if we need it?

Yes, and the quality bar holds because the rubric and gold set are already agreed by that point. Ramping is a staffing question, not a re-scoping one, so it usually takes days rather than a new engagement.

We already have a vendor for this. Why switch?

Often you should not. The cases where teams move to us are when they cannot get a quality number out of their current vendor, or when the work is delivered as an opaque batch with no trace of how disagreements were resolved.

What happens after the engagement ends?

We stay on-call for 30 days at no extra cost, then move to an optional support retainer. Most teams also keep a quarterly evaluation review with us to catch drift early.

Copyright © 2026
ReinforcedX, Inc.
All rights reserved