Skip to content

Ground the default system prompt: abstain instead of guess on retrieval miss#3

Open
idanshimon wants to merge 1 commit into
Betanu701:mainfrom
idanshimon:fix/grounded-default-prompt
Open

Ground the default system prompt: abstain instead of guess on retrieval miss#3
idanshimon wants to merge 1 commit into
Betanu701:mainfrom
idanshimon:fix/grounded-default-prompt

Conversation

@idanshimon

Copy link
Copy Markdown

Summary

The default system_prompt

"You are a helpful assistant with access to a knowledge base."

— doesn't instruct the model to stay grounded in the retrieved knowledge. When retrieval misses (a paraphrase gap, or a genuinely absent field), smaller models substitute a well-known training-data fact instead of abstaining.

Concrete example from testing: ask "longest acceptable time from patient arrival to opening the blocked artery?" against a clinical-registry corpus. The corpus never states a maximum, and BM25 never retrieves the door-to-balloon doc (different words than the query). With the bare default, gpt-5-mini confidently answers "Goal: door-to-balloon ≤ 90 minutes... ≤ 120 minutes" — pulled from training data, not the corpus. That's a hallucination by training-data substitution.

Evidence

8 models × trap questions via GitHub Copilot CLI, mechanical grading (absence + paraphrase-miss cells):

Default prompt Hallucinations
Bare ("helpful assistant…") 5 / 72 (~7%) — all mini models, all on the one paraphrase-miss question
Grounded (this PR) 0 / 144

Same model / same corpus / same retrieval miss, gpt-5-mini:

  • Old default: "Goal: door-to-balloon ≤ 90 minutes from patient arrival to opening the blocked artery. For patients transferred… ≤ 120 minutes."
  • New default: "I don't have that information in the provided knowledge. The material lists a 'Door-to-balloon time (min)' field but does not specify the longest acceptable time."

Re-running the 18 mini-model trials that failed under the old default, with this patched default: 0 / 18.

The absence questions (e.g. "max admission potassium?") stayed at 0 either way — there's no registry-potassium-max in any model's training data to substitute. The failure specifically needs a retrieval miss + a famous answer the model already knows, which is exactly what the grounding clause defends against.

Change

Append a grounding + abstention clause to the two default prompts:

  • contextforge/layer.py — constructor default system_prompt
  • contextforge/infinite_context.py — the empty-system_parts fallback

Fully backward compatible — passing your own system_prompt is unchanged. Adds 2 tests (TestGroundedDefaultPrompt: default-is-grounded + custom-still-overrides). Suite 129 → 131, all green.

Why default-on

Anyone who never thinks about the system prompt — the quickstart path, the copy-paste-the-README user — currently gets the permissive behavior and the silent substitution. Making grounding the default means the safe behavior is free, and opting out is explicit.

…al miss)

The default system_prompt ("You are a helpful assistant with access to a
knowledge base.") does not tell the model to stay grounded in the retrieved
knowledge. When retrieval misses (paraphrase gap, or an absent field), smaller
models substitute a well-known training-data fact instead of abstaining -- e.g.
answering "90 minutes" to a door-to-balloon question whose answer was never in
the corpus and was never retrieved.

Evidence (8 models x trap questions, GitHub Copilot CLI, mechanical grading):
- bare default prompt:     5/72 hallucinations (~7%), ALL from mini models,
                           ALL training-data substitution on the one
                           paraphrase-miss question.
- grounded prompt:         0/144 hallucinations.
- re-running the 18 failing mini-model trials with this patched default: 0/18.

Fix: append a grounding + abstention clause to the two default prompts
(layer.py constructor default, infinite_context fallback). Fully backward
compatible -- passing a custom system_prompt is unchanged. Adds 2 tests
(default-is-grounded, custom-still-overrides); suite 129 -> 131, all green.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR updates ContextForge’s default system prompt(s) to explicitly ground answers in retrieved knowledge and instruct the model to abstain (instead of guessing) when the knowledge provided does not contain the answer, addressing hallucinations on retrieval misses.

Changes:

  • Strengthen the default system_prompt in ContextForge to require knowledge-only answers and abstention on missing info.
  • Strengthen the InfiniteContext fallback system prompt when no system parts are present.
  • Add tests ensuring the default prompt is grounded and that a custom system_prompt still overrides the default.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 3 comments.

File Description
tests/test_layer.py Adds tests validating grounded default prompting and confirming custom prompt override behavior.
contextforge/layer.py Updates ContextForge constructor’s default system_prompt to include grounding + abstention instructions.
contextforge/infinite_context.py Updates the empty-system_parts fallback system prompt to include grounding + abstention instructions.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread tests/test_layer.py
Comment on lines +195 to +198
lowered = system.lower()
assert "only" in lowered
# must steer toward abstaining rather than guessing / using outside knowledge
assert "guess" in lowered or "do not have" in lowered
Comment thread contextforge/layer.py
Comment on lines +52 to +57
system_prompt: str = (
"You are a helpful assistant with access to a knowledge base. "
"Answer using ONLY the information in the provided knowledge. "
"If the answer is not contained in the knowledge, say you do not have "
"that information rather than guessing or using outside knowledge."
),
Comment on lines 298 to 305
messages.append({
"role": "system",
"content": "\n\n".join(system_parts) if system_parts else "You are a helpful assistant.",
"content": "\n\n".join(system_parts) if system_parts else (
"You are a helpful assistant. Answer using ONLY the information "
"provided to you. If the answer is not available, say you do not "
"have that information rather than guessing."
),
})
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants