Skip to content

Medical AI · Retrieval-augmented generation (RAG) · Lesson 5 of 7

Citations and answer grounding

Reviewed by Dr. Ram Kumar G, MBBS, MD (Internal Medicine), MPH (USA), on · Last updated

  • Intermediate
  • Technical focus
  • About 30 minutes
  • Includes exercise

Before this lesson: Evaluating retrieval, Hallucination and grounding

In short

In a RAG system, each statement in an answer should cite the passage it came from, so a reader can check it. But models attach citations to claims their sources do not support, cite the wrong passage, or reverse a rule while keeping its words. Check every claim against its cited source automatically where possible, flag or remove unsupported claims, and remember that simple word-overlap checks miss negation: 'start metformin' and 'do not start metformin' look almost the same to them.

You will learn to

  • Explain why citations need to be verified, not just displayed
  • Check claim-level support against cited passages in Python
  • Find a better source for a mis-cited claim, or flag it as unsupported
  • Recognise the limits of lexical checks, especially for negation

What can go wrong with citations

Each problem looks reassuring on screen. A citation creates the impression of evidence, so unverified citations can be more misleading than none.

ProblemExample
Correct claim, wrong citation'Stop metformin below eGFR 30' cites the hypoglycaemia section
Unsupported claim with a citation'Metformin can be continued at any eGFR above 15' cites the kidney section, which says no such thing
Reversed meaning'Start metformin if eGFR is below 45' cites a passage saying not to
Citation to a retrieved but unused passageA long list of sources, most of which the answer did not use

Checking claims against their sources

The check below takes an answer split into claims, each with the section it cites, and measures how much of the claim's content, and every number in it, appears in the cited section. Run the protocol block from the chunking lesson first.

python
import re

sections = {f"s{i + 1}": p.strip() for i, p in enumerate(re.split(r"\n(?=## )", PROTOCOL)[1:])}

answer = [   # (claim, section it cites)
    ("Check eGFR at least once a year.", "s2"),
    ("Do not start metformin if eGFR is below 45.", "s2"),
    ("Stop metformin if eGFR falls below 30.", "s4"),          # right fact, wrong citation
    ("Metformin can be continued at any eGFR above 15.", "s2"),  # unsupported claim
    ("Start metformin if eGFR is below 45.", "s2"),             # reverses the rule
]

STOP = {"the", "a", "an", "is", "if", "at", "of", "to", "and", "or", "be", "can", "any", "do", "not"}

def content_words(text):
    return {w for w in re.findall(r"[a-z0-9]+", text.lower()) if w not in STOP}

def support(claim, source_text):
    """Share of the claim's content words, and all of its numbers, found in the cited source."""
    words, src = content_words(claim), content_words(source_text)
    numbers_ok = all(n in src for n in re.findall(r"\d+", claim))
    return len(words & src) / len(words), numbers_ok

for claim, cited in answer:
    overlap, numbers_ok = support(claim, sections[cited])
    verdict = "supported" if overlap >= 0.8 and numbers_ok else "CHECK"
    print(f"{verdict:<9} [{cited}] overlap {overlap:.2f} numbers {'ok' if numbers_ok else 'missing'}  {claim}")

# supported [s2] overlap 1.00 numbers ok  Check eGFR at least once a year.
# supported [s2] overlap 0.80 numbers ok  Do not start metformin if eGFR is below 45.
# CHECK     [s4] overlap 0.17 numbers missing  Stop metformin if eGFR falls below 30.
# CHECK     [s2] overlap 0.40 numbers missing  Metformin can be continued at any eGFR above 15.
# supported [s2] overlap 0.80 numbers ok  Start metformin if eGFR is below 45.
Claim-level support check against cited sections of the synthetic protocol.
  • The wrong citation and the unsupported claim are both caught.
  • The last line is the most dangerous failure: 'Start metformin if eGFR is below 45' reverses the rule, yet passes, because 'not' is ignored as a stop word and every other word matches. Word overlap measures topic, not meaning.

Repairing citations

python
def best_source(claim):
    return max(sections, key=lambda sid: support(claim, sections[sid])[0])

for claim, cited in answer:
    overlap, numbers_ok = support(claim, sections[cited])
    if not (overlap >= 0.8 and numbers_ok):
        better = best_source(claim)
        o2, n2 = support(claim, sections[better])
        fix = f"cite {better} instead" if o2 >= 0.8 and n2 else "no section supports it: remove or flag"
        print(f"{claim}\n  -> {fix}")

# Stop metformin if eGFR falls below 30.
#   -> cite s2 instead
# Metformin can be continued at any eGFR above 15.
#   -> no section supports it: remove or flag
Continuing from the previous block: look for a section that does support each flagged claim.
  • A correct claim with the wrong citation can be repaired automatically; an unsupported claim should be removed or clearly flagged, never kept with a borrowed citation.

Stronger checks and safer designs

ApproachStrengthLimitation
Word and number overlap (above)Fast, transparent, catches invented numbers and wrong sectionsMisses negation, paraphrase and reversed meaning
Entailment (natural language inference) modelsJudge whether the source logically supports the claimCan be wrong on clinical language; need testing
A second LLM as verifierHandles paraphrase and contextCan share the generator's errors; must be evaluated
Extractive answers (quote the source)Every word is in the source; citations are exactLess fluent; must quote enough context
Quote then answerModel first copies the supporting sentences, then answers from themStill needs checking that the answer follows the quotes

Negation is a known weak spot

  • Clinical text is full of negation: 'no', 'not', 'denies', 'without', 'stop'. Any automated support check used in a clinical system should be tested specifically on negated and reversed claims.

Exercise

Build and stress-test a claim-level citation checker on the synthetic protocol.

Data: Synthetic protocol and answer claims in the notebook (Created for this course; not clinical guidance)

  1. Reproduce the support check and the citation repair.
  2. Write ten more claims, including three negated or reversed ones, and record which the checker gets wrong.
  3. Add a negation rule: flag any claim whose negation words ('not', 'no', 'stop', 'never') differ from the best-matching source sentence.
  4. Optional: try an open natural language inference model on the same claims and compare.

Open the notebook in ColabView on GitHubWorked solution coming soon

Key takeaways

  • Citations only help if the cited passage actually supports the claim.
  • Check every claim; repair wrong citations and remove unsupported claims.
  • Word-overlap checks miss negation and reversed meaning; test for them specifically.
  • Extractive or quote-then-answer designs make grounding easier to verify.

Terms used: Grounding, Hallucination, RAG (retrieval-augmented generation)

Related: Hallucination and grounding, Build a guideline Q&A assistant, Evaluating retrieval

FAQ

Do citations make RAG answers trustworthy?

Only if they are checked. Models can attach citations to statements the source does not support, so citations make verification possible rather than guaranteeing accuracy.

How can citations be checked automatically?

By comparing each claim with its cited passage, using word and number overlap, entailment models or a second model as verifier, and flagging claims that are not supported.

Why do simple checks miss reversed rules?

Because 'start' and 'do not start' share almost all their words. Overlap measures whether the topic matches, not whether the meaning is the same.

What is an extractive answer?

An answer made of sentences quoted directly from the source documents, so every statement can be traced exactly. It is less fluent than generated text but easier to verify.

Should unsupported claims be shown with a warning?

In clinical settings it is usually safer to remove them, or show them clearly separated from supported content, than to display them with a citation that does not support them.

Educational content only. This material teaches methods; it is not clinical advice. Code, models and examples here are not validated medical devices and must not be used to make decisions about real patients without proper clinical validation, governance and any required regulatory approval. Examples use synthetic or openly licensed de-identified data only. Never paste identifiable patient information into public AI tools. Mentions of models, tools or vendors are for identification, not endorsement.