# Grounded Responses

Course: [Context Engineering](https://www.devtutorials.ai/learn/context-engineering) · Section: Design Patterns
Source: https://www.devtutorials.ai/learn/context-engineering/design-patterns/grounded-responses

> How to make the model answer only from its sources: source of truth, refusal rules, evidence boundaries, and citations with quotes, ids, sections and pages that code can check.

Think about taking a test with an open book. A good student does three things:

- They answer from the book, not from memory.
- They write down the page number, so the teacher can check.
- When the book does not cover a question, they write "not in the book" instead of making something up.

That is exactly what we want from an AI assistant that answers from documents. It is called a **grounded response**.

**The main idea:** a grounded answer comes only from the sources in the context, shows where it came from, and says "I don't know" when the sources are not enough.

## A Real-World Example

Imagine you ask a shop assistant, "Is this jacket waterproof?"

- A careless assistant says "Yes, sure," without looking.
- A careful assistant reads the label and says, "The label says water-resistant, not waterproof. Here, look."
- When there is no label, the careful assistant says, "I'm not sure. Let me ask my manager."

The careful assistant is slower, but you can trust them. And you can check their answer yourself.

**In simple words:** grounding means "answer from the label, show the label, or say you don't know."

## What You Will Learn

In this lesson, you will learn:

1. What a source of truth is, and why the model must answer from it.
2. How to write refusal rules for when the sources are not enough.
3. How to set evidence boundaries.
4. How to ask for citations: quotes, source ids, section headers, and page numbers.
5. How to check a citation with code.
6. When this pattern fits, and when it does not.

The Context sandbox has a new part for this chapter: **Grounding rules**. Switch it on in the Builder to make the model answer only from the documents, quote them, or refuse. The Builder also has a **Check the reply** box, so you can test every reply against a rule.

## Grounded Answer Pattern

A **pattern** is a tested way to solve a problem that keeps coming up. The **grounded answer pattern** solves this problem: "The model must answer from our documents, not from its own memory."

The pattern has four parts.

### Source of Truth

The **source of truth** is the set of documents the answer must come from. Everything else, including what the model learned in training, does not count.

For Brightgrid support, the source of truth is the official help articles and the customer's own record. A forum post is not a source of truth. Neither is the model's general idea of how "most products" work.

You tell the model this in the system message: "Answer only from the documents in the context."

### Factual Grounding

**Factual grounding** means every fact in the answer can be found in the source of truth.

You can check this with code. The next box splits an answer into claims and looks for any number that is not in the context.

```js runnable title="Find the claim that is not grounded"
const context = "Free plan: export up to 5,000 rows. Pro costs $20 per developer per month.";

function unsupportedClaims(answer) {
  const claims = answer.split(". ").map((c) => c.replace(/\.$/, ""));
  return claims.filter((c) => {
    const facts = c.match(/\$?\d[\d,]*/g) ?? [];
    return facts.some((f) => !context.includes(f));
  });
}

const reply = "The Free plan exports up to 5,000 rows. Pro costs $25 per developer per month";
console.log("Claims:", reply.split(". ").length);
console.log("Not supported:", JSON.stringify(unsupportedClaims(reply)));
```

Expected output:

```text
Claims: 2
Not supported: ["Pro costs $25 per developer per month"]
```

The first claim is grounded. The second one says $25, but the context says $20. The model mixed in something from its memory. This simple check only looks at numbers. Real checks also compare names, dates, and quotes.

### Refusal Rules

A **refusal rule** tells the model what to do when the sources do not contain the answer. Without one, the model fills the gap with a guess.

A good refusal rule:

- Says exactly what to reply, such as "I don't know from these documents."
- Gives a next step, such as "offer to connect a human agent."
- Is short and clear, so the model follows it.

The next box shows the idea in code. It answers only when a document contains every key word of the question.

```js runnable title="Answer from a document, or refuse"
const docs = {
  "doc-1": "Free plan: export up to 5,000 rows.",
  "doc-2": "Pro plan: export up to 1,000,000 rows.",
};

function answer(question) {
  for (const [id, text] of Object.entries(docs)) {
    const words = question.toLowerCase().split(" ").filter((w) => w.length > 3);
    if (words.every((w) => text.toLowerCase().includes(w))) {
      return { answer: text, source: id };
    }
  }
  return { answer: "I don't know from these documents.", source: null };
}

console.log(JSON.stringify(answer("free plan export")));
console.log(JSON.stringify(answer("export excel files")));
```

Expected output:

```text
{"answer":"Free plan: export up to 5,000 rows.","source":"doc-1"}
{"answer":"I don't know from these documents.","source":null}
```

The documents say nothing about Excel files, so the code refuses. A model with a refusal rule should do the same.

Try it with the real model. Both contexts have the same help article. Only B has the grounding rules. The question is one the article can't answer.

Without the rule, the model may say "yes" and even describe the steps. With the rule, it should say it doesn't know. Small models don't always follow the rule. That is why the next pattern adds proof you can check.

### Evidence Boundaries

An **evidence boundary** is the line between what the sources say and what they don't. A grounded answer stays inside it.

Two common ways to cross the boundary:

- **Over-reach.** The source says "Free plan: 5,000 rows." The answer says "Free plan: 5,000 rows, and the limit resets every month." The second part was invented.
- **Wrong blending.** The source covers the Free plan. The answer applies it to the Pro plan too.

Tell the model to stay inside the boundary: "Only state what the documents say. Do not add details, reasons, or numbers that are not written there."

**Easy Way to Remember:** a grounded answer **reads the label, shows the label, or says "no label"**.

## Citation Pattern

A **citation** shows where a fact came from. The **citation pattern** asks the model to cite its sources, so people and code can check the answer.

### Supporting Quotes

A **supporting quote** is the exact sentence from the source that backs up the answer.

Quotes are powerful because they are easy to check. If the quote is not in the source, word for word, something is wrong.

Ask for the quote **with** the answer. Some teams even ask for the quote **first**, then the answer. This makes the model find its evidence before it writes, which keeps it closer to the source.

### Source IDs

A **source id** is a short, unique name for each document, such as `doc-1`. You add ids when you build the context. Lesson 4 showed how.

The model then writes "Source: doc-1". Code can look up `doc-1` in a second and check the quote.

### Section Headers

For long documents, the id is not enough. A help article can have ten sections. A **section header** in the citation says which part of the document was used, such as "Export limits".

Keep section headers in the text when you split documents. If you remove them, the model can't cite them.

### Page Numbers

For PDFs, manuals, and contracts, people think in **page numbers**. A citation like "User Guide, page 4" lets a reader open the right page at once.

The page number must come from your context, not from the model. Add it to each chunk's label when you extract the text.

```js runnable title="A citation with id, section, and page"
const docs = [
  { id: "doc-1", section: "Export limits", page: 4, text: "The Free plan exports up to 5,000 rows. Larger exports need the Pro plan." },
  { id: "doc-2", section: "Pricing", page: 9, text: "Pro costs $20 per developer per month." },
];

const reply = {
  answer: "Your export failed because the Free plan allows 5,000 rows.",
  quote: "The Free plan exports up to 5,000 rows.",
  source: "doc-1",
};

const doc = docs.find((d) => d.id === reply.source);
console.log("Source found:", Boolean(doc));
console.log("Quote is in that source:", doc ? doc.text.includes(reply.quote) : false);
console.log("Cite as:", doc.id + ", " + doc.section + ", page " + doc.page);
```

Expected output:

```text
Source found: true
Quote is in that source: true
Cite as: doc-1, Export limits, page 4
```

The model only gave the id and the quote. The section and page came from your own labels, so they can't be invented.

### Citation Validation

**Citation validation** means checking every citation with code before you show the answer.

A citation can fail in two ways:

- The source id does not exist. The model made it up.
- The id exists, but the quote is not in that document.

```js runnable title="Check each citation"
const docs = { "doc-1": "The Free plan exports up to 5,000 rows.", "doc-2": "Pro costs $20 per developer per month." };

function validate(citation) {
  if (!docs[citation.source]) return "fail: source " + citation.source + " does not exist";
  if (!docs[citation.source].includes(citation.quote)) return "fail: quote is not in " + citation.source;
  return "pass";
}

console.log(validate({ source: "doc-1", quote: "exports up to 5,000 rows" }));
console.log(validate({ source: "doc-2", quote: "exports up to 5,000 rows" }));
console.log(validate({ source: "doc-7", quote: "Pro is free" }));
```

Expected output:

```text
pass
fail: quote is not in doc-2
fail: source doc-7 does not exist
```

When a check fails, don't show the answer as it is. Ask the model again, show it with a warning, or send it to a person.

Try a full grounded answer with the real model. The card checks that the reply contains the word "5,000".

Read the reply. Does it quote the article? Does it name the source? Now change the question to one the article can't answer and run it again.

## Pattern Usage

### Suitable Scenarios

The grounded answer pattern fits when the answer **must** come from known documents:

- Support assistants answering from help articles.
- HR assistants answering from the employee handbook.
- Assistants that answer questions about contracts, policies, or manuals.
- Research tools that must show their sources.

### Unsuitable Scenarios

It does not fit when there is no source of truth to stay inside:

- Creative writing, like stories or poems.
- Brainstorming new ideas or names.
- General chat, or explaining a common idea in simple words.

In these tasks, a strict "only from the documents" rule just makes the model refuse things it could do well.

```js runnable title="Does the task need grounding?"
const tasks = [
  { task: "Answer from our help center", factsFromDocs: true, creative: false },
  { task: "Summarize a contract clause", factsFromDocs: true, creative: false },
  { task: "Write a birthday poem", factsFromDocs: false, creative: true },
  { task: "Brainstorm product names", factsFromDocs: false, creative: true },
];

for (const t of tasks) {
  const fit = t.factsFromDocs && !t.creative ? "use the grounded pattern" : "grounding not needed";
  console.log(t.task + " -> " + fit);
}
```

Expected output:

```text
Answer from our help center -> use the grounded pattern
Summarize a contract clause -> use the grounded pattern
Write a birthday poem -> grounding not needed
Brainstorm product names -> grounding not needed
```

### Pattern Limitations

The pattern is strong, but it has limits:

- **It is only as good as the sources.** If the help article is wrong or old, the grounded answer is wrong too.
- **It needs the right documents in the context.** If retrieval missed the right article, the model will refuse, or worse, use a weaker one. Lesson 6 covers this.
- **Models don't always obey.** Small models especially may still add facts. That is why you validate with code.
- **Refusing too often is also a failure.** If the rule is too strict, the model says "I don't know" even when the answer is there in other words.

A citation that looks right is not proof. Always check that the source exists and the quote is really in it.

## Common mistakes

| Mistake | Wrong idea | Correct idea | What goes wrong |
|---|---|---|---|
| No refusal rule | "The model will say when it doesn't know." | Write the exact refusal sentence into the rules. | The model invents an answer that sounds right. |
| Trusting citations | "It cited doc-3, so the answer is checked." | Check that doc-3 exists and the quote is in it. | Made-up sources look real to readers. |
| Asking for page numbers with none given | "The model can work out the page." | Put page numbers in each chunk's label. | Pages are invented and point to the wrong place. |
| Rules too strict | "Refuse unless the exact words match." | Allow answers that the documents clearly support. | The assistant refuses good questions. |
| Grounding creative tasks | "Every task should use the grounded pattern." | Use it when answers must come from documents. | The model refuses to brainstorm or write freely. |
| Ignoring old sources | "If it's in the context, it's true." | Grounding copies the source, errors included. | A wrong or old article becomes a wrong answer. |

## Exercises

Each exercise ends with a value on the last line. Press **Check** to compare it with the expected answer.

## What to Remember

- A **grounded answer** comes only from the **source of truth** in the context.
- **Refusal rules** say exactly what to reply when the sources are not enough.
- Stay inside the **evidence boundary**: no extra details, reasons, or numbers.
- **Citations** use supporting quotes, source ids, section headers, and page numbers. Ids, sections, and pages come from your labels.
- **Validate** every citation with code: the source must exist, and the quote must be in it.
- Use the pattern when answers must come from documents. Don't use it for creative work.

Next: **Retrieval Patterns**, what to do when the retrieved documents are weak, missing, or disagree with each other.
