Instructions in a prompt are requests. A model usually honours them and sometimes does not, and "usually" is not a property you can build on. A guardrail is the same rule expressed as code that runs whether the model cooperated or not.
Both directions
Input guardrails run before the call: strip or reject material you do not want reaching the model, cap the size of retrieved context, refuse requests that fail an authorisation check.
Output guardrails run after: validate the shape, check claims against sources, verify a generated identifier actually exists.
// output guardrail: every cited id must be real
function checkCitations(answer, sources) {
const known = new Set(sources.map((s) => s.id))
const cited = [...answer.matchAll(/\[source:([^\]]+)\]/g)].map((m) => m[1])
const invented = cited.filter((id) => !known.has(id))
return { ok: invented.length === 0, invented }
}That check is a handful of lines and it catches fabricated citations every time, which no amount of prompt instruction achieves.
Why deterministic matters
The point of a guardrail is that it does not share the model's failure modes. Asking a second model call "is this output safe?" is useful, but it is another probabilistic judgement, and it can be wrong in the same direction as the first. A guardrail should be a check whose behaviour you can predict without running it. Schema validation, set membership, regex, an authorisation lookup, a numeric range.
Where a model-based check is genuinely needed, treat it as one signal rather than the gate.
Practical notes
- Fail loudly in development, gracefully in production. You want to see every violation while building, and a sensible fallback for users.
- Log what tripped. A guardrail firing is a signal about your prompt or retrieval, not just an error to swallow. Repeated invented citations means the retrieval is not supplying what the question needs.
- Keep them cheap. A guardrail that costs more than the model call will get removed the first time latency matters.
Related terms
Structured outputs
Structured outputs constrain a model to return data matching a schema you define, rather than prose you have to parse. It removes an entire class of failure: the model answered correctly and your code could not read it.
Read definition →PatternValidatedRetry and repair
Retry and repair feeds a failed validation back to the model as the next input, so it fixes its own output instead of you regenerating blind. It converts a hard failure into one more turn, with the error as the instruction.
Read definition →AntipatternProvenPrompt injection
Prompt injection is untrusted content in the context being followed as instruction. It is not a prompting bug to be patched but a structural consequence of putting data and instructions in the same channel.
Read definition →PatternProvenTool use
Tool use lets a model do more than produce text: you expose named actions with typed inputs, and the model calls them to read data, run code, or reach the outside world. It is the bridge from talking to doing.
Read definition →