# Context and Grounding

Course: [Prompt Engineering](https://www.devtutorials.ai/learn/prompt-engineering) · Section: Foundations
Source: https://www.devtutorials.ai/learn/prompt-engineering/foundations/context-and-grounding

> Give the model the facts it needs: grounded prompts, picking the right context, letting the model say I don't know, asking for sources and checking them, and fitting context in the window.

Ask a model about your company's refund policy, and it will give you an answer. The answer will sound sure. But the model has never seen your policy. It just wrote a refund policy that **sounds** likely to be true.

The fix is simple. Give the model the facts, and tell it to answer only from those facts. This is called **grounding**, and it is one of the most useful skills in this course.

**The main idea:** a model only knows the text it was trained on and what is in your prompt. So put the facts it needs into the prompt, and ask it to stick to them.

## A Real-World Example

Think about two kinds of exams:

- In a **closed-book exam**, you answer from memory. If you forgot something, you might guess and write a wrong answer.
- In an **open-book exam**, the book is on your desk. You find the right page and answer from it. You can even say which page you used.

A plain prompt is like a closed-book exam for the model. A grounded prompt is like an open-book exam. You give the model the right pages, and it answers from them.

## What You Will Learn

In this lesson, you will learn:

1. Why a model needs context for facts it never learned.
2. What grounding means.
3. How to choose the right context from many documents.
4. How to let the model say "I don't know".
5. How to ask for sources and check them with code.
6. How to fit the context into a limited amount of space.

The examples use **Brightgrid**, a made-up product. The model has never seen it, so any fact it gives about Brightgrid must come from your prompt. This makes it easy to see grounding at work.

## Why the Model Needs Context

A model learned from public text, up to a date called its **knowledge cutoff**. It has never seen:

- Your company's documents, prices, or policies.
- Your code, your tickets, or your emails.
- Anything that happened after its training ended.

When you ask about these things, the model does not stop. It writes the answer that sounds most likely. That answer is often wrong. This is called a **hallucination**, which you learned about in the Large Language Models course.

Try it. Prompt A asks the question with no facts. Prompt B gives the facts first.

Prompt A gets a made-up number or an unclear answer. Prompt B gets "5,000", because the fact is right there in the prompt.

## What Is Grounding?

**Grounding** means giving the model trusted text and asking it to answer only from that text. This text is called the **context**.

A grounded prompt has three parts:

1. **The context**, placed inside clear markers.
2. **A rule**: answer only from the context.
3. **A way out**: if the answer is not in the context, the model should say so.

The next box builds a grounded prompt from a list of facts.

```js runnable title="Build a grounded prompt"
function groundedPrompt(facts, question) {
  const numbered = facts.map((f, i) => "[" + (i + 1) + "] " + f).join("\n");
  return [
    "Answer the question using only the facts between the <facts> tags.",
    "If the facts do not contain the answer, reply exactly: I don't know.",
    "<facts>",
    numbered,
    "</facts>",
    "Question: " + question,
  ].join("\n");
}

const facts = ["The free plan exports up to 5,000 rows.", "The Pro plan costs $20 per month."];
console.log(groundedPrompt(facts, "What does the Pro plan cost?"));
```

Expected output:

```text
Answer the question using only the facts between the <facts> tags.
If the facts do not contain the answer, reply exactly: I don't know.
<facts>
[1] The free plan exports up to 5,000 rows.
[2] The Pro plan costs $20 per month.
</facts>
Question: What does the Pro plan cost?
```

Each fact has a number. Later in this lesson, you will use these numbers as sources.

## Pick the Right Context

Real apps have many documents, far too many to fit in one prompt. So before you ask the model, you **choose the few pieces that matter** for the question.

Big apps use embeddings for this, which you learned about in the Large Language Models course. But you can show the idea with a simple word match: count how many words from the question appear in each piece.

```js runnable title="Pick the pieces that match the question"
const docs = [
  "The free plan exports up to 5,000 rows.",
  "The Pro plan exports up to 1,000,000 rows.",
  "Brightgrid supports dark mode and custom themes.",
  "Support replies within one working day.",
];

function score(question, doc) {
  const words = question.toLowerCase().match(/[a-z0-9]+/g);
  const text = doc.toLowerCase();
  return words.filter((w) => w.length > 3 && text.includes(w)).length;
}

const question = "How many rows can the free plan export?";
const ranked = docs
  .map((d) => ({ doc: d, score: score(question, d) }))
  .sort((a, b) => b.score - a.score);

ranked.forEach((r) => console.log(r.score, r.doc));
```

Expected output:

```text
4 The free plan exports up to 5,000 rows.
3 The Pro plan exports up to 1,000,000 rows.
0 Brightgrid supports dark mode and custom themes.
0 Support replies within one working day.
```

The best piece is at the top. You would send only the top one or two pieces, not all four. When there is less extra text, the model is less likely to mix up facts.

## Let the Model Say "I Don't Know"

Models are trained to be helpful, so they try hard to give an answer. If the answer is not in the context, they may fill the gap with a guess.

So give the model a clear way out: "If the facts do not contain the answer, reply exactly: I don't know." Then **test it** with a question the facts cannot answer.

The card below runs one grounded template on four questions. The facts cannot answer the last question, so the check expects "I don't know".

<PromptTry tab="test" template="Answer using only the facts between the facts tags. If the facts do not contain the answer, reply exactly: I don't know.\n<facts>\n[1] The free plan exports up to 5,000 rows.\n[2] The Pro plan exports up to 1,000,000 rows.\n[3] The Pro plan costs $20 per month.\n</facts>\nQuestion: {{input}}\nReply in one short sentence." cases="How many rows can the free plan export? | contains: 5,000\nWhat does the Pro plan cost? | contains: 20\nHow many rows can the Pro plan export? | contains: 1,000,000\nDoes Brightgrid run on mobile phones? | contains: know" />

For the last case, the check looks for the words "don't know" instead of one exact sentence. A model may write "I don't know." or "I don't know the answer." Both are fine. The next box runs a check like this on a few sample replies.

```js runnable title="Did the model admit it does not know?"
function admitsUnknown(reply) {
  const r = reply.toLowerCase().replace(/’/g, "'");
  return r.includes("don't know") || r.includes("not in the facts");
}

const samples = [
  "I don't know.",
  "I don’t know the answer.",
  "Yes, Brightgrid works on all phones.",
  "That is not in the facts.",
];

samples.forEach((s) => console.log(admitsUnknown(s), "|", s));
```

Expected output:

```text
true | I don't know.
true | I don’t know the answer.
false | Yes, Brightgrid works on all phones.
true | That is not in the facts.
```

The third reply is a guess. Nothing in the facts says Brightgrid works on phones. This is the kind of mistake the test is built to catch.

If the last case fails, the model guessed. Make the rule stronger, or move it to the end of the prompt, and run the test again.

**Easy Way to Remember:** every grounded prompt needs **facts, a rule, and a way out**. And every test of a grounded prompt needs at least one question the facts cannot answer.

## Ask for Sources

When the facts have numbers, you can ask the model to show which fact it used, like "[1]". This helps in two ways:

- A person can check the answer against the source.
- Code can check that the source numbers are real.

A model can cite a number that does not exist, like "[7]" when there are only 3 facts. The next box catches this.

```js runnable title="Check the sources in a reply"
function checkSources(reply, factCount) {
  const cited = (reply.match(/\[(\d+)\]/g) || []).map((c) => Number(c.slice(1, -1)));
  const bad = cited.filter((n) => n < 1 || n > factCount);
  return { cited, bad, ok: cited.length > 0 && bad.length === 0 };
}

console.log(JSON.stringify(checkSources("The free plan exports 5,000 rows [1].", 3)));
console.log(JSON.stringify(checkSources("It costs $20 [3] and has an API [7].", 3)));
console.log(JSON.stringify(checkSources("It costs $20 per month.", 3)));
```

Expected output:

```text
{"cited":[1],"bad":[],"ok":true}
{"cited":[3,7],"bad":[7],"ok":false}
{"cited":[],"bad":[],"ok":false}
```

- The first reply cites a real fact, so it passes.
- The second reply cites fact 7, which does not exist. This means something was made up.
- The third reply cites nothing, so you cannot check it.

A real source number does not prove the answer is right. It only proves the model pointed to a fact that exists. A person, or another check, still needs to make sure the fact says what the reply claims.

## Fit the Context in the Space

Every model has a **context window**. This is a limit on how many tokens it can read at once. The model in your browser has room for about 4,000 tokens. The instructions, the context, the question, and the reply all share this space.

So you set a **budget** for the context, and add the best pieces until the budget is full.

```js runnable title="Add the best pieces until the budget is full"
const ranked = [
  "The free plan exports up to 5,000 rows.",
  "The Pro plan exports up to 1,000,000 rows.",
  "Large exports run in the background and send an email when ready.",
  "Brightgrid supports dark mode and custom themes.",
];

const budget = 30;
let used = 0;
const chosen = [];

for (const piece of ranked) {
  const tokens = Math.ceil(piece.length / 4);
  if (used + tokens > budget) break;
  chosen.push(piece);
  used += tokens;
}

console.log("Chosen:", chosen.length, "pieces,", used, "tokens");
```

Expected output:

```text
Chosen: 2 pieces, 21 tokens
```

The third piece would go over the budget of 30 tokens, so the code stops there. The best pieces come first, so the most useful facts always get in.

Grounding reduces hallucinations, but it does not stop them completely. A model can still mix in things from its training, or misread a fact. For important answers, check the reply against the source.

## Common mistakes

| Mistake | Wrong idea | Correct idea | What goes wrong |
|---|---|---|---|
| Asking about private facts without giving them | "The model will know our product." | It only knows the public text it was trained on. Give it the facts. | The model makes up prices, limits, and features. |
| No way out | "It will tell me if it does not know." | Tell it exactly what to reply when the answer is missing. | It guesses instead of saying it doesn't know. |
| Pasting everything | "More context is always better." | Choose the few pieces that match the question. | Facts get mixed up, and the prompt may not fit. |
| Only testing questions the facts can answer | "All my tests pass, so it is grounded." | Add questions the facts cannot answer. | You never see that the model makes things up when facts are missing. |
| Trusting source numbers | "It cited [2], so it must be correct." | Check that the number exists and that the fact says the same thing. | Made-up or wrong sources look trustworthy. |
| Facts without markers | "It's clear which part is the facts." | Put the facts inside tags, separate from the question. | The model mixes up the facts and the question. |

## Exercises

In each exercise, the last line should give back a value. Press **Check** to compare your value with the expected answer.

## What to Remember

- A model knows only the **text it was trained on** and **your prompt**.
- **Grounding** means giving the model trusted facts and asking it to answer only from them.
- A grounded prompt needs **facts inside markers**, a **rule**, and a **way out** ("I don't know").
- **Choose** the few pieces that match the question. Don't paste everything.
- **Number** the facts, ask for sources, and **check** the numbers with code.
- Keep the context within a **budget**, because everything shares the context window.
- Grounding **reduces** hallucinations, but it does not stop them completely.

This is the end of Chapter 1. Next: **Zero-Shot**, the first main prompting technique, where you get good results without giving any examples.
