No single filter stops a determined attacker. Toggle four defense layers over a fixed set of attack and benign strings, run them through transparent rule-based classifiers, and watch the block rate and the false-positive rate move against each other. The lesson is computed, not asserted: single layers fail where stacks hold.
Defense in depth, made testable
A guardrail is any check that sits between a request and a consequence. There are four common places to put one: filter the input before the model sees it, constrain the model with a system-prompt policy, filter the output after generation, and gate the actual actions the model can take. Each catches a different kind of attack and misses others. The only honest way to see this is to run a small designed corpus through them.
Corpus size
16 strings
Attacks
8
Benign
8
Layers
4
Every attack string below is a tame, illustrative example, the benign strings are deliberately authored to trip naive filters so the false-positive pressure is real, and every model response shown is authored for the demo, never generated by a live model. The classifiers are plain pattern rules you can read, not a hidden scoring model.
Build your stack
Turn layers on and off and drag the thresholds. The whole corpus re-runs instantly. Start with a single layer to watch it fail, then stack all four.
Input filter
Lexical, pre-model
Scans the incoming text for jailbreak trigger phrases and blocks when the matched-pattern score reaches the threshold.
System-prompt policy
Intent, pre-model
Scans the text for references to protected assets (the prompt, keys, instructions) and blocks at its threshold.
Output filter
Post-model
Scans the response after generation for leak markers, catching attacks whose input text looked harmless.
Tool permissioning
Action gate
Blocks any consequential action (send, delete, transfer) regardless of the text, outside the model.
The tradeoff, live
Layers on
0
Block rate
0%
0/8 attacks
False positives
0%
0/8 benign
The ideal is the top-left corner: every attack blocked, no benign request touched. Every single-layer point sits low, none passes 50% block rate. Stacking layers lifts the block rate, and raising thresholds pulls the point back toward the left.
What each layer caught and missed
Every row is scored by the classifiers for the current stack. The four boxes show which layers blocked it (pink) versus let it through. Click any string to see the matched patterns, the authored response, and why the verdict came out the way it did. Look for the exfiltration attack that only tool permissioning stops.
| # | Request string | Kind | Layers | Verdict |
|---|---|---|---|---|
| A1 | attack | missed | ||
| A2 | attack | missed | ||
| A3 | attack | missed | ||
| A4 | attack | missed | ||
| A5 | attack | missed | ||
| A6 | attack | missed | ||
| A7 | attack | missed | ||
| A8 | attack | missed | ||
| B1 | benign | correct | ||
| B2 | benign | correct | ||
| B3 | benign | correct | ||
| B4 | benign | correct | ||
| B5 | benign | correct | ||
| B6 | benign | correct | ||
| B7 | benign | correct | ||
| B8 | benign | correct |