Context and Grounding

By the Syncfusion DevTutorials team · Last updated

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.

Grounding: choose the facts that matter, put them in the prompt with clear rules, and the model answers from them with a source number.

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.

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:

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.

Give every document piece a score for how well it matches the question, keep the best few, and put only those into the prompt.

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.

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:

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".

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.

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:

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.

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:

{"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.

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:

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

MistakeWrong ideaCorrect ideaWhat 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.