Preview environment - you also see content that isn't published yet.
Aha!

Why a model sounds more certain when it's wrong than when it's right

Invented libraries, invented statutes, invented citations - delivered without the slightest hesitation. That's not a glitch; it follows directly from what the model was trained to do.

July 30, 2026hallucination · trust · training

A model names a function that doesn't exist. It cites a statute nobody ever wrote. And it does so not hesitantly but in the same firm tone it uses for everything else. The error is rarely the annoying part - the annoying part is that it doesn't sound any different from the truth.

Fluency is the objective, truth is a side effect

A language model is trained to predict the next token. Its entire skill is the answer to one question: what usually comes next in a text of this kind? Truth appears nowhere in that task. It comes along for the ride, because training data mostly contains accurate statements.

That fixes the failure mode. When a fact is missing, no gap appears - a gap would be improbable. What appears is whatever is plausible in that position: a function name built exactly like the real function names of that library. A statute number in the correct format. A source with a fitting author, a fitting year, a fitting publisher. The invention isn't a slip; it's the conscientious output of the very machinery that is otherwise right.

The tone is a style, not a measurement

On top of that, sounding certain is itself a learned pattern. When a model writes "this is definitely the case", it hasn't listened inward - it picked the phrasing that fits a text of that kind. Explanations in manuals, answers to technical questions, documentation: the templates in the training data are almost uniformly confident. Hedging barely occurs in them.

The subsequent fine-tuning for helpfulness tends to amplify this rather than damp it: what got rewarded was the answer that helps, and a clearly stated answer looks more helpful than one carrying three caveats. So anyone inferring reliability from tone is reading a stylistic feature as a measurement.

Baustein

Guardrails

The agent's house rules: what it must never do, how errors are caught - and why boundaries aren't distrust, they're architecture.

An agent that can act needs boundaries: validate inputs, filter outputs, stop dangerous actions, handle errors cleanly. Guardrails turn an experiment into a system you can trust with real work.

A production agent can move money, delete data, send emails to customers. What it can't do is just as important as what it can.

Guardrails operate on multiple layers: prompt constraints, tool whitelists, output filters, rate limits, sandboxed execution. And the most important guardrail of all: threat modeling before the system goes live.

The defence ladder

The most common attack on an agent is also the cheapest: you talk to it. Prompt injection means smuggling instructions into a place where the model expects data - a customer message, a document, a web page some tool just fetched. The model doesn't distinguish between "this is my assignment" and "this is the text I'm supposed to talk about". To it, both are the same thing: characters in a context.

Defending against it isn't a decision, it's a ladder. Every rung adds exactly one layer, and every layer has the attack it breaks on:

LevelDefenceWhat breaks it
0nonejust ask
1hardened system promptrole play, reframing
2output filter, hard-wiredencoding, acrostics, letter by letter
3output filter, model-basedparaphrase, spreading it across a story
4input filter, hard-wiredsynonyms, another language
5input filter, model-basedharmless-looking tasks with a hidden payload
6everything at once + domain restrictionlittle left but narrative

The ladder has an axis that's easy to miss on first reading: input versus output and hard-wired versus model-based. A hard-wired filter looks for characters - it's fast, free and blind to meaning, which is why Base64 slips past. A model-based guard understands meaning - it's slow, costs a second model call, and can itself be talked around, because it too is just a model with a prompt.

And: input filters act too early to know what will happen, output filters too late to stop what already has. That's why the top of the ladder isn't a better layer, it's all of them at once.

What 279,000 real attacks show

You don't have to speculate about how well these layers work. In Gandalf the Red, Lakera analysed the attacks on a public prompt-injection game - 279,000 real attempts by real people against systems defended in graded steps. Three of their findings are the difference between game knowledge and professional knowledge:

  • Layering works, single layers don't. None of the layers holds on its own; each has its attack. Only the combination drives the success rate down noticeably - not because any layer gets better, but because an attack suddenly has to outwit all of them at the same time.
  • Domain restriction is the single strongest measure. A bot that may only talk about one topic is far harder to hijack than a jack-of-all-trades. Not because it's better guarded - but because there's barely a way to address it outside its topic in the first place.
  • Defence in the system prompt costs usability, even when it blocks nothing. Every "never do this" sentence makes the model more cautious, more terse, more suspicious of harmless questions. Security and usability are one joint objective, not two separate ones. Which is why a game where only security counts is no template for production guardrails.

The OWASP Top 10 for LLM applications has listed prompt injection as LLM01 since its first edition - the top spot, and unchanged there in the 2025 edition. Its recommendations read like the ladder above, with one explicit caveat: no single measure solves the class. OWASP talks about mitigation, not about a fix.

An example from our own house

The most effective measure appears on no filter list, because it isn't one: user text does not belong in the system prompt. That is exactly what this site's comment agent does in src/lib/comments/agent.ts - the system prompt is a fixed string, and the comment follows as a user message:

/**
 * System prompt of the comment agent. The comment itself goes into the model
 * as a user message, NEVER in here - that dampens prompt injection:
 * instructions inside the comment stay data.
 */
export function buildCommentAgentPrompt(candidates: SearchDoc[]): string {

The difference is structural, not cosmetic. If the comment text were interpolated into the system prompt, an "ignore all previous instructions" would sit on the same level as the operator's own instructions - it would be that level. This way it stays one rung below. That's no guarantee: models follow instructions from user messages too. But it's the difference between an attack that has to argue across the role hierarchy and one that is simply written down alongside.

The same module adds the other half: the prompt tells the model outright that the comment is untrusted user data, and the answer is pinned to a narrow schema. A hijacked agent can at best write the wrong thing into a field that only accepts three values anyway - damage control through form, not through vigilance.

The uncomfortable conclusion

Prompt injection is not solved. There is no library, no model and no vendor that closes the class; there are only systems where it lands less often and does less harm. Both are achievable, and both are engineering work: lower the hit rate through layering and domain restriction, limit the damage through narrow tool permissions, confirmations before irreversible actions, and logs that show what actually happened.

Anyone waiting instead for the one correct system prompt is building their system on a promise nobody made.

→ Zum Baustein

There is a signal - just not in the text

The model isn't entirely blind to its own uncertainty. For every token it also produces a probability distribution, and when it's guessing, that distribution is flatter: five candidates sit close together instead of one leading clearly. But those numbers (the logprobs) don't reach the output - the text is the drawn sample, not a measure of how wobbly the draw was. And the language a human would use to express doubt ("I think", "possibly") is itself just more text, predicted like everything else.

Put differently: the channel on which the model would have doubts and the channel on which we read them are two different channels.

What follows when you build

You can't prompt the tendency away - "don't make anything up" is a wish, not a lock. What helps is construction:

Verifiability instead of assertion. The answer shouldn't claim, it should show its work: file path and line, source with the passage, the raw tool result alongside. Evidence can be checked; a tone can't.

Looking up beats remembering. A tool that reads the actual library interface moves the question out of memory and into the present. That's the point of tool use and of RAG - not to know more, but to guess less.

Executing is the most honest judge. Invented code fails to compile, invented endpoints fail on call. An agent with a feedback channel from reality corrects itself; one without it stays with its first, well-phrased version.

Force uncertainty where it matters. An "unknown" offered as an explicitly permitted value in a structured schema gets used far more often than a "tell me if you don't know" in prose. What is offered gets chosen.

The most useful habit turns out to be the simplest one: infer nothing from the tone. It's a property of the text, not of its truth.

Related
More pieces