[LLM 8/10] Guardrails: The Real Safety Fence Isn't a Model, It's a Threshold
The Thai chatbots my team and I deploy for real customers don't only meet polite questions — people ask for recipes for illegal things, people try to trick the bot into insulting others, and there was a day the model volunteered a customer's phone number all by itself. In this chapter we build protection in both directions: a classifier that checks incoming prompts for danger and an outbound PII filter. But the central point of the chapter isn't the model — it's the fact that a guardrail is a threshold decision under asymmetric costs, and the "94% accuracy" figure people love to show off is very nearly meaningless.
Open in Colab08_guardrails.ipynb
1. The Problem
Every previous chapter trained the model to be "better." But no matter how capable a model is, the moment it meets real users, damage can always arrive from two directions:
| Direction | Example damage | This chapter's tool |
|---|---|---|
| Inbound (input) | A user asks how to hurt someone, for an illegal recipe, how to cheat — and the model answers | a dangerous-prompt classifier |
| Outbound (output) | The model emits a national ID number, phone number, or bank account leaked from context or training data | a deterministic PII filter |
Remember that SFT back in chapter 2 had a very quiet side effect: fine-tuning on narrow data erodes the refusal behavior the model used to have. A model you tuned yourself is therefore usually less safe than the original. That is why real systems need another fence that lives outside the model.
So why can't you just buy an off-the-shelf guardrail advertised as "94% accurate"? Because that sentence hasn't answered the three questions that matter most:
- 94% accurate at which threshold — the same number can slide along the entire curve
- Measured on a test set with what percentage unsafe — in production, genuinely dangerous traffic is usually under 1%
- And what percentage of innocent users does it block — the number almost nobody agrees to report
A guardrail that blocks customers asking normal questions is not a safe system. It is a broken product.
2. What We're Going to Do
We'll build two real guardrails on free Colab, then measure them honestly:
| Layer | Position | Technique | Approx. latency |
|---|---|---|---|
| Input guardrail | before the prompt reaches the LLM | Qwen3-0.6B + sequence-classification head + LoRA r=8 | ~15–30 ms |
| Output guardrail | after the LLM answers, before the user sees it | regex + mod-11 checksum (no ML at all) | ~0.1 ms |
A guardrail isn't a model — it's a threshold decision under asymmetric costs. The model only supplies a score ; choosing the cutoff is answering the business question "how many times more expensive is letting one dangerous thing through than blocking one innocent customer?"
An honest report therefore always carries two numbers: the unsafe caught and the benign blocked. A single number on its own is marketing, not engineering.
We'll also see that some of the best guardrails aren't ML at all — a PII filter built from regex + checksum is deterministic, unit-testable, microsecond-fast, and can never be jailbroken.
3. The Equations
3.1 The classifier loss — an old friend
Plain binary cross-entropy ( means unsafe). Nothing new — and that is precisely the point: the ML part of a guardrail is the easiest part of the whole system. The real substance is below.
3.2 The decision rule and expected cost — the actual content of this chapter
The model's job ends at producing a score. Blocking or passing is a comparison against , which we choose by minimizing expected cost:
- = the fraction of unsafe that slips through (false negative rate)
- = the fraction of benign that gets blocked (false positive rate)
- = the price of each kind of mistake
Notice that this equation forces you to answer a question ML cannot answer for you: what is your product's ? This is a pure product decision, and different products answer it differently:
| Product | Price of an FN (unsafe slips through) | Price of an FP (blocking an innocent) | Reasonable |
|---|---|---|---|
| Health-advice chatbot | life-threatening + legal liability | mildly annoyed user | 50:1 or more |
| Enterprise customer assistant | damaging headlines | more support tickets | ~10:1 |
| Internal employee tool | limited (users are identifiable staff) | daily workflow friction | ~2:1 |
If you've never written this ratio down explicitly in a document, it means someone has been choosing for you by accident.
3.3 The most expensive lesson of the chapter: base rates can destroy precision
Suppose our classifier catches unsafe at TPR = 95% and wrongly blocks only FPR = 5% — sounds excellent. Question: of the messages it blocks, what percentage are actually unsafe? Straight Bayes, with the fraction of unsafe in real traffic:
Plug in two scenarios:
- Balanced test set (): precision
- Real traffic (): precision
The exact same classifier — but in production, of every 6 blocked messages, 5 are innocent users. Because when unsafe is rare ( small), the term in the denominator swallows everything. This is why evaluating on a balanced set and then bragging "95% accurate" is self-deception.
3.4 Layered defence
Stack independent guardrails (blocklist → classifier → system prompt → random human review), where unsafe must fool every layer to get through, but benign gets blocked if any single layer trips:
FNR falls geometrically (wonderful), but FPR compounds upward (the bill you pay).
The equation holds only if the layers fail independently, which in reality they almost never do — a single evasion trick (say, inserting a zero-width space mid-word) tends to fool every layer that works on raw text at once. The layers' errors are therefore correlated, and the real is always worse than this formula. Treat it as a best case, not a promise.
3.5 Constrained decoding — the non-ML guardrail everyone overlooks
If your use case only ever answers from a fixed set (a menu, categories, schema-conforming JSON), don't inspect the text afterwards — enforce it at generation time by renormalizing over the allowed token set :
Tokens outside have probability exactly zero, not "very small." The result is a guardrail that is deterministic, adds zero latency, and cannot be bypassed by any prompt in the universe — because it doesn't forbid the model from "wanting to say" something; it makes out-of-set words not exist in the inventory in the first place. Wherever constrained decoding applies, use it first, and save the classifier for the parts that are genuinely free text.
This post is roughly the first 30% of the chapter. The rest — environment setup, data preparation, the main code, measured results and the wrap-up — is in the free LLM Finetuning course. Sign in with Google to read it.
Read the full lesson in the course →