How to Get an LLM to Admit When It Doesn't Know Something

By Carlos Montiel | Enterprise AI Specialist
Leer en español →
Published: 2026-07-28 | By: Carlos Montiel | Reading time: ~5 minutes

Language models are trained to complete text plausibly, not to measure their own certainty. Teaching them to say "I don't know" means going against that default bias -- and there are concrete techniques that pull it off consistently.

Why the "default" model never says "I don't know"

Language models' training bias favors answers that sound complete and confident over honest ones that admit a gap. This isn't an isolated bug -- it's a direct consequence of how these systems are trained and evaluated: an incomplete or hesitant answer gets penalized more in many benchmarks than an incorrect but fluent one. The result is that, with no explicit instruction, the model almost always prefers inventing something plausible over admitting an information gap.

The solution isn't a single prompt trick -- it's a combination of explicit instruction, structural constraint, and downstream verification.

Explicit instruction: give permission, don't just ask for it

The most effective instruction isn't "be honest" (too vague to change the model's behavior) -- it's giving explicit, unambiguous permission to respond with uncertainty, and specifying exactly which phrase to use.

system_prompt = """ If the question requires information that isn't in the provided context, or if you don't have enough certainty to answer confidently, respond exactly: "I don't have enough information to answer this confidently." This is a valid and preferred response over an invented one. You will not be penalized for admitting an information gap -- you will be penalized for confidently asserting something incorrect. """

The phrase "you will not be penalized for..." looks unnecessary but is effective: it directly counteracts the training bias toward answers that sound complete.

Structural constraint: ask for a confidence level as a field, not prose

Asking the model to "say if you're not sure" inside a prose response is fragile -- easy to skip under pressure to complete the task. Forcing an explicit confidence field into a structured output turns uncertainty into a data point, not an opinion the model can omit.

output_config = { "format": { "type": "json_schema", "schema": { "type": "object", "properties": { "answer": {"type": "string"}, "confidence": { "type": "string", "enum": ["high", "medium", "low", "insufficient_information"], }, "source_used": { "type": ["string", "null"], "description": "Exact context fragment supporting the answer, " "or null if there's no direct support", }, }, "required": ["answer", "confidence", "source_used"], "additionalProperties": False, }, } }

With this schema, your application can treat `confidence: "insufficient_information"` as an explicit code branch -- show a different message, escalate to a human, or ask for more context -- instead of relying on the model spontaneously mentioning it in the text.

Grounding: the root cause is usually missing context, not missing instruction

Before assuming you need more prompting, check whether the model simply doesn't have the information in its context. A model well-instructed to say "I don't know" will still invent things if its context doesn't include the correct fact -- because from its perspective, there's no difference between "this isn't in my context" and "this doesn't exist." If your system is RAG, check retrieval recall first (see the RAG context engineering article) before assuming the problem is model calibration.

The cross-verification pattern (chain of verification)

For high-risk cases, a second call auditing the first response against the context detects unsupported claims more reliably than asking the same model, in the same call, to self-evaluate while generating.

def verify_response(original_response, context): verification_prompt = f""" Verify each claim in this response against the context. For each claim, mark: - SUPPORTED: the context confirms it directly - NOT SUPPORTED: the context doesn't say this - CONTRADICTED: the context says something different Context: {context} Response: {original_response} """ return client.messages.create( model="claude-haiku-4-5", # cheap model for the verification step max_tokens=1024, messages=[{"role": "user", "content": verification_prompt}], )

Using a cheaper model for this verification step is reasonable -- the task of "comparing a claim against the source text" is simpler than the original generation, and doesn't need the bigger model.

Handle the refusal `stop_reason` explicitly

Some models, faced with certain risk categories, outright refuse to answer instead of trying to respond with low confidence. This is a different case from "I don't know" but your code must handle it -- never assume `response.content` always has useful content without checking `stop_reason` first.

if response.stop_reason == "refusal": # The model refused -- treat it as a result, not a code error handle_refusal(response.stop_details) else: process_normal_response(response.content)

Measure calibration, not just accuracy

A well-calibrated model isn't one that never makes mistakes -- it's one that, when it's wrong, expressed low confidence; and when it's right, expressed high confidence. To measure this on an evaluation set:

def evaluate_calibration(evaluated_cases): # For each declared confidence level, what % of the # responses were correct? by_level = {} for case in evaluated_cases: level = case["declared_confidence"] by_level.setdefault(level, {"correct": 0, "total": 0}) by_level[level]["total"] += 1 if case["was_correct"]: by_level[level]["correct"] += 1 return { level: data["correct"] / data["total"] for level, data in by_level.items() } # A well-calibrated model should show: high > medium > low # in accuracy rate. If "high" and "low" have similar rates, # the declared confidence means nothing -- it's noise.

If your evaluation shows the model declares "high confidence" at the same accuracy rate as "low confidence," the problem isn't that the model doesn't know when to doubt -- it's that your prompt isn't giving it a real reason to differentiate, and you need to revisit the calibration instruction from scratch.

Carlos Montiel
Enterprise AI Solutions Architect
Specialist in LLMs, Agents, and Orchestration
guatemalia.com/en/#contact · info@guatemalia.com

Need to implement AI at your company?

Carlos Montiel is an enterprise AI solutions architect. He implements LLMs, Agents, RAG, and orchestrators for companies across Guatemala and Latin America. Reach out for a consultation.

Contact Carlos Montiel

info@guatemalia.com