A practice prompt we wrote. No company or candidate report names it, so it carries no company tag.
How to answer
Two questions hide in this one: how a citation is produced, and how you know it is honest. A model asked to “add citations” produces plausible references, some to passages it never saw. Make the citation a data structure your code checks, not text the model writes.
- Give every passage an ID the system owns. Put retrieved passages in the context under short IDs, keep the mapping in code, and render titles and links from your source store, never from model output, after checking the user may open each source.
- Constrain the output. Ask for structured output: each statement with the passage IDs that support it and a short verbatim quote. Say that this turns citation checking into plain validation.
- Verify in four layers. The ID was in this request’s retrieved set. The quote appears in that passage, after normalizing whitespace and punctuation, and is long enough to mean something; an empty or tiny quote proves nothing. Every number in the statement, including the digits of a date, appears in the passage, a cheap check that catches a wrong figure before any model runs. The passage entails the statement, checked by an entailment model or an LLM judge you have calibrated against human labels.
- Decide what happens on failure, and what it costs. Drop the statement, regenerate with the failure named, or say the documents don’t answer it. Fail closed for anything a customer will act on. Checking before display rules out streaming, so name that tradeoff.
- Measure it offline. Citation precision (cited passages that support their statement) and citation recall (statements that carry a supporting citation) on a labeled set, the framing used in Gao et al., Enabling Large Language Models to Generate Text with Citations. Sample production answers for review.
Then raise the hardest case before you’re asked: a real citation attached to a statement it doesn’t support. The ID and quote checks both pass; only the entailment check catches it, which is why that layer isn’t optional.
How do you know your AI system works? takes the evaluation half of this answer. Retrieval is taught in Production AI systems, citations inside a customer’s cloud in Enterprise system design for FDEs, and labeled sets and calibrated judges in Evaluation: proving it works; all three modules are in Pro.
Follow-ups
What the interviewer may ask next, once your first answer is on the table.
- How is a citation tied to a retrieved passage?
- How do you check automatically that the passage supports the claim?
- A citation is real but the claim is wrong. How do you catch that?
Where answers go wrong
- Asks the model to add citations and trusts them.
- Checks that the quote exists but never whether the passage supports the statement.
- Lets the model write titles or links, or shows sources the user may not open.
Answer this in two minutes
Write the answer you would say out loud. The clock starts with your first word.
Compare with the model answer
Model answer
“I split this into producing citations and verifying them, and the model owns neither a source’s identity nor how it’s rendered.
Producing. Retrieval returns passages with stable IDs from the index, such as policy_812#p4. I put them in the prompt under short aliases, [S1] through [S8], and keep the alias map in code; short aliases are easy for the model to copy and trivial to validate. The model returns JSON: a list of statements, each with sources, a list of aliases, and quote, a short span copied from one of them. The UI renders title, link and date from our source store by ID, after checking the user may open that source, so a citation never shows a document the user isn’t allowed to see. The model never writes a URL.
If the provider’s API returns citations as spans of documents you pass in, such as Anthropic’s citations feature, I’d use it. It guarantees the cited span exists in a document I supplied, so it replaces the alias and quote checks, but not the number and entailment checks. On that API it can’t be combined with structured outputs, so it replaces the JSON format too.
Verifying. A validator runs on every response before anyone sees it:
import re
import unicodedata
MIN_QUOTE_WORDS = 5
NUM = re.compile(r"\d+(?:[.,]\d+)*")
def normalize(s: str) -> str:
s = unicodedata.normalize("NFKC", s).lower()
s = re.sub(r"[^\w\s]", " ", s)
return " ".join(s.split())
def numbers_supported(claim: str, passage: str) -> bool:
wanted = set(NUM.findall(claim))
return wanted <= set(NUM.findall(passage))
def verify(
answer: Answer,
retrieved: dict[str, Passage],
judge: Judge,
) -> list[Statement]:
kept = []
for s in answer.statements:
passages = [retrieved.get(a) for a in s.sources]
if not passages or None in passages:
log_failure("unknown_source", s)
continue
q = normalize(s.quote)
# the passage the quote came from
source = next(
(p for p in passages if q in normalize(p.text)),
None,
)
too_short = len(q.split()) < MIN_QUOTE_WORDS
if too_short or source is None:
log_failure("quote_not_found", s)
continue
if not numbers_supported(s.text, source.text):
log_failure("number_mismatch", s)
continue
verdict = judge.label(
premise=source.text, claim=s.text
)
if verdict != "supported":
log_failure("not_entailed", s)
continue
kept.append(s)
return kept
Four statements show what each check catches:
| Statement | Fails | Why |
|---|---|---|
“Covers burst pipes [S9]” | unknown_source | S9 was never retrieved |
| Quotes “covers all water damage” | quote_not_found | Those words aren’t in the passage |
“The limit is $5,000” | number_mismatch | The passage says $50,000 |
| “Covers flooding”, real quote | not_entailed | A burst pipe is not a flood |
The first check catches invented sources. The second catches a real source with an invented quote; an empty or tiny quote fails, or the check proves nothing. The third is cheap and runs before any model: every number in the statement, including the digits of a date, must appear in the passage, because a wrong figure is the error a legal or finance customer can least afford, and an entailment model can let $5,000 pass against $50,000. The fourth is for the hard case, where the citation and quote are both real but the statement goes beyond them: the passage says the policy covers water damage from a burst pipe, and the statement says it covers flooding. Only an entailment check sees that. I’d use an NLI model or a small LLM judge with a narrow prompt: given this passage, is the statement supported, contradicted, or not addressed? The judge reads the passage the quote came from, so every check is about the same passage, and a single passage fits a typical NLI model, which reads about 512 tokens and would truncate joined text. A statement only two passages support together fails this check, so the prompt asks for statements small enough that one passage supports each. Either way I calibrate the judge against a few hundred human-labeled pairs from this customer’s documents and report agreement, because the judge is a model too and can be wrong.
Checking before display rules out streaming, so I send every statement and passage pair to the judge in one batch and show the passages while it runs. For a low-stakes internal tool, I’d stream the answer and mark each statement as unverified until its check passes.
On failure. Unsupported statements are dropped. If that removes the core of the answer, I regenerate once, naming the statement that failed; if it fails again, the assistant says the documents don’t answer the question and shows the closest passages. For a legal or financial customer, any failure means no generated answer, only the passages.
Knowing it keeps working. Offline, a labeled question set with gold passages, scored on citation precision and recall, with the judge spot-checked by a person. Online, the failure rate for each check goes on a dashboard: a jump in quote_not_found after a model or prompt change is a regression I want to see the same day. A weekly sample of shown answers goes to a reviewer, since the judge misses things as well.
When their legal team asks what the check mark next to a sentence means, I say: ‘It means our checker found that the cited passage supports that sentence. We tested the checker on a few hundred sentences from your own documents that people had labeled, and its agreement with them is on this dashboard. Every week a person reviews a sample of the answers it passed.’“