Grounding Answers in Provided Documents
What Does "Grounded" Mean? An answer is grounded when every factual statement in it is supported by the documents provided to the model. A grounded answer does not add facts from the model's general knowledge, does not guess missing details, and does not change what the documents say.
Source: "Refunds are available within 7 days of purchase."
Grounded: "You can request a refund within 7 days of purchase." Not grounded: "You can request a refund within 7 days, and most refunds are approved." (the second part is not in the source) Lesson 103 described how to structure a RAG answer prompt. This lesson focuses on the grounding rules inside that prompt, and on how to check grounding in code. Lessons 105 and 106 then cover missing information and unsupported claims in more depth.
Why Grounding Is Hard for Models Language models are trained to produce helpful, fluent, complete-sounding answers. When documents answer only part of a question, a model's natural tendency is to fill the gap with plausible information from its training data. For a general question, that can be helpful. For your platform's policies, prices, and schedules, it produces confident errors, because the model's general knowledge describes typical platforms, not yours.
Source of a Statement Example Acceptable in a Grounded Answer? Directly stated in a document "Refunds within 7 days" Yes A direct inference from documents "A purchase 10 days ago is outside the 7-day window" Yes, if the inference is simple and clear Typical practice elsewhere "Refunds usually take 3–5 days" No Plausible guess "Premium members get faster refunds" No Model's opinion "This is a fair policy" No, unless asked and labeled as opinion Levels of Grounding Different applications need different strictness. Choose the level deliberately and state it in the prompt.
Level 1: Strict Extractive Answers may only restate the documents, preferably with quotes.
Answer using only sentences from the sources, quoted or closely restated. Do not draw conclusions that are not written in the sources. Use for: legal, financial, medical, or compliance information, where any interpretation is risky.
Level 2: Grounded Synthesis Answers may combine and apply information from the documents, including simple inferences, but must not add outside facts.
Answer using only the information in the sources. You may combine information from several sources and apply the rules to the user's situation, but do not add facts that are not in the sources. Use for: most help assistants, such as applying a refund rule to "I bought it 10 days ago".
Level 3: Grounded With Labeled General Knowledge Answers are based on documents, but may add general knowledge if it is clearly separated and labeled.
Answer from the sources first, with citations. If general knowledge helps, add it in a separate final sentence starting with "In general," and do not present it as our policy. Use for: learning assistants, where general explanations of concepts are useful alongside platform-specific facts.
Grounding Techniques in the Prompt Technique 1: Explicit Source Boundary State clearly which content is the source of truth, and that it is the only source for certain kinds of facts:
For any fact about GOCOURSE policies, prices, dates, or schedules, use only the sources. Naming the kinds of facts that must be grounded is clearer than a general "use only the sources", because it tells the model exactly where outside knowledge is forbidden.
Technique 2: Sentence-Level Citations End every sentence that states a fact with the id of the source it comes from, in square brackets. A sentence without a source must not contain a factual claim. Citations do two jobs. They tell users where information comes from, and they force the model to connect each statement to a specific source, which reduces unsupported additions.
Technique 3: Quote, Then Answer Ask the model to first extract the relevant quotes and then answer from them (Lesson 70):
First, inside , copy the exact sentences from the sources that are relevant to the question. Then, inside , answer using only those quotes, with citations. The quotes can be verified in code, and the answer is built from verified evidence. This technique is especially effective for longer sources.
Technique 4: Define Allowed Inference Be explicit about what kind of reasoning is allowed:
You may apply rules in the sources to the details the user provides, for example comparing dates or amounts. You must not assume details the user did not provide. This allows useful answers such as "Your purchase 10 days ago is outside the 7-day refund window", while preventing assumptions such as "You have probably completed more than 20% of the course".
Technique 5: Grounded Refusal Tell the model exactly what to say when the sources do not support an answer:
If the sources do not contain the information needed, say "I couldn't find this in our help documents" and do not provide a partial guess. Lesson 105 develops this into a full strategy for missing information.
Checking Grounding in Code Prompt rules reduce ungrounded statements but do not eliminate them. A simple automated check can catch three common problems:
Sentences without citations Citations to sources that were not provided Numbers that do not appear in the cited source import re
SOURCES = { "refund-policy#0": "Refunds are available within 7 days of purchase. A refund is only possible " "if less than 20% of the course has been completed.", "refund-policy#1": "Approved refunds reach the original payment method within 5 to 7 business days.", }
CITATION = re.compile(r"[([\w.#-]+)]") NUMBER = re.compile(r"\d+(?:.\d+)?")
def check_grounding(answer: str, sources: dict[str, str]) -> list[tuple[str, str]]: """Return (status, sentence) for each sentence in the answer.""" report = [] sentences = [s.strip() for s in re.split(r"(?<=[.!?])\s+", answer.strip()) if s.strip()] for sentence in sentences: cited = CITATION.findall(sentence) claim = CITATION.sub("", sentence) if not cited: report.append(("NO CITATION", sentence)) continue unknown = [c for c in cited if c not in sources] if unknown: report.append((f"UNKNOWN SOURCE {unknown}", sentence)) continue cited_text = " ".join(sources[c] for c in cited) missing_numbers = [n for n in NUMBER.findall(claim) if n not in NUMBER.findall(cited_text)] status = f"NUMBER NOT IN SOURCE {missing_numbers}" if missing_numbers else "OK" report.append((status, sentence)) return report
answer = ("Refunds are available within 7 days of purchase [refund-policy#0]. " "You must have completed less than 30% of the course [refund-policy#0]. " "Refunds arrive within 5 to 7 business days [refund-policy#1]. " "Most students receive their money in 3 days. " "Partial refunds are possible [policy#9].")
for status, sentence in check_grounding(answer, SOURCES): print(f"{status:<28} {sentence}") Output:
OK Refunds are available within 7 days of purchase [refund-policy#0]. NUMBER NOT IN SOURCE ['30'] You must have completed less than 30% of the course [refund-policy#0]. OK Refunds arrive within 5 to 7 business days [refund-policy#1]. NO CITATION Most students receive their money in 3 days. UNKNOWN SOURCE ['policy#9'] Partial refunds are possible [policy#9]. Code Explanation Splitting into sentences The answer is split after ., !, or ? followed by whitespace, so each sentence is checked separately.
Citations CITATION.findall() extracts source IDs in square brackets. CITATION.sub("", sentence) removes them before checking numbers, so the 0 in refund-policy#0 is not treated as a number in the claim.
Three checks per sentence
No citation: "Most students receive their money in 3 days" has none, and it is not in the sources. It is an invented claim. Unknown source: policy#9 was never provided. The citation itself is invented. Numbers: every number in the claim must appear in the cited source text. "30%" is a changed number; the source says 20%. What the Check Cannot Do This check is fast and catches many problems, but it is a heuristic:
It cannot detect unsupported claims without numbers, such as "Refunds are approved automatically" with a valid citation. It can raise false alarms when numbers are written differently, such as "seven" versus "7". It flags numbers that come from the user's own message, such as "5 days ago", because they are not in the cited source. The next section shows how to handle this. For claims without numbers, use a model-based support check (Lesson 106). Use the code check first, because it is free and instant.
Grounding and the User's Own Details Users often provide details about their own situation: "I bought the course 5 days ago and finished 10%." These details are not in the documents, but the answer may need them.
The Rule The answer may use the user's stated details to apply the documents' rules, but must not invent additional details:
Apply the rules in the sources to the details the user has stated. If a rule depends on a detail the user has not stated, explain the rule instead of assuming the detail. Example Question: "I bought the Django course 5 days ago. Can I get a refund?"
Grounded answer:
You bought the course 5 days ago, which is within the 7-day refund window [refund-policy#0]. A refund also requires that less than 20% of the course is completed [refund-policy#0], so you're eligible if you've completed less than 20%. The answer applies the 7-day rule to the stated detail, and explains the 20% condition because course progress was not stated, instead of assuming it.
Allowing the User's Numbers in the Check Run through check_grounding(), the first sentence of this answer would be flagged, because "5" appears in the user's question, not in the cited source. To allow numbers the user provided, add the question to the text that numbers are compared against:
cited_text = " ".join(sources[c] for c in cited) + " " + question This requires passing the question to the function. Numbers from the user are then accepted, while numbers that appear in neither the sources nor the question are still flagged.
Common Mistakes Mistake 1: "Use the Context" Without Boundaries What it is: Instructions like "Use the context to answer" without forbidding outside facts.
Why it happens: It seems obvious that the context should be used.
What it causes: The model uses the context and adds general knowledge freely.
How to fix it: State that specific kinds of facts must come only from the sources.
Mistake 2: Citations as Decoration What it is: Asking for a source list at the end of the answer, not per statement.
Why it happens: It looks cleaner.
What it causes: Unsupported sentences hide among supported ones, because nothing connects each claim to a source.
How to fix it: Require citations per sentence, and check them.
Mistake 3: Unclear Inference Rules What it is: Not specifying whether the model may apply rules to the user's situation.
Why it happens: The difference between inference and guessing is not considered.
What it causes: Either unhelpful answers that refuse simple applications, or answers that assume missing details.
How to fix it: State what inference is allowed, and that missing details must not be assumed.
Mistake 4: Trusting Grounding Without Checks What it is: Assuming grounding rules are always followed.
Why it happens: Answers look well-cited.
What it causes: Changed numbers and invented citations reach users.
How to fix it: Run automated checks and model-based verification for important answers.
Best Practices Choose a grounding level deliberately. Strict extractive, grounded synthesis, or labeled general knowledge.
Name the facts that must come from sources. Policies, prices, dates, schedules, and rules.
Require sentence-level citations. Connect every claim to a source.
Use quote-then-answer for important or long sources. Verify the quotes in code.
Define allowed inference. Apply rules to stated details; never assume missing ones.
Check grounding automatically. Citations, unknown sources, and numbers first; model-based checks for the rest.
Try It Yourself Exercise 1: Compare Grounding Levels Write three versions of an answer prompt, one for each grounding level. Ask the same five questions about your refund or certificate policy with each version. Compare which answers add outside information, and decide which level suits your help assistant.
Exercise 2: Run the Grounding Check Collect ten answers from your RAG prompt and run check_grounding() on each. Count sentences without citations, unknown sources, and number mismatches. Then manually check five "OK" sentences for unsupported claims without numbers.
Exercise 3: User Details Ask five questions that include user details, such as purchase dates and progress percentages, with some details missing. Extend check_grounding() to accept the question text as described above, and check that each answer applies the rules only to stated details and explains conditions for missing ones.