Guardrails Are Two Checkpoints, Not One
Someone types a request into your AI feature, and the model just answers — no friction, no check, nothing standing between "the user asked" and "the model did." That's what running with zero guardrails looks like. The fix isn't one filter bolted on somewhere. It's two.
The problem: one filter covers half the risk
Most teams that add "a guardrail" add exactly one check, usually right before the model responds, and call it done. But a single checkpoint only catches failures on one side of the exchange. It misses the failures that happen on the other side entirely.
Guardrails split naturally into two categories, based on when they run relative to the model call:
- Input guardrails run before the request reaches the model. Their job is to stop bad requests from ever being processed.
- Output guardrails run after the model generates a response. Their job is to stop bad responses from ever reaching the user.
These aren't redundant. An input check can't see what the model is about to say — it only knows what the user asked. An output check can't undo the fact that the model already processed a malicious prompt — it can only catch what leaks out the other end.
Why it works: each side has a distinct job
Input guardrails are your first line of defense. Common patterns include:
- Prompt injection / jailbreak detection — catching attempts to override the system prompt ("ignore previous instructions...").
- Topic guardrails — rejecting requests outside the feature's intended scope before they burn a model call.
- PII detection — blocking requests that contain sensitive data the user shouldn't be pasting in (a phone number, an SSN, an internal ID).
- Toxicity filters — refusing clearly abusive or harmful requests up front.
Output guardrails catch what got through, or what the model produced on its own even from a clean input:
- Content safety checks — scanning the generated response for unsafe or policy-violating content before it's shown.
- Leak detection — catching secrets, credentials, or internal data the model shouldn't have surfaced.
- Format / groundedness checks — verifying the response doesn't hallucinate outside allowed bounds.
The reason both matter: an input guardrail can be clean and the model can still produce an unsafe answer. And an output guardrail alone means you've already spent a model call — and potentially exposed the model to a malicious prompt — before catching anything.
How to build it
You don't need a heavyweight framework to start. Each guardrail is just a small function, a list of patterns, or a rubric evaluated by a lightweight classifier (or a cheaper LLM call). The shape is simple:
request → [input check] → model → [output check] → response
- Write the input check first. Start with the highest-value catches: an off-topic filter, a PII detector, a jailbreak pattern check.
- Write the output check next. At minimum, run a content-safety classifier over the generated response before it's returned. Add a schema-specific leak-detection pass if your feature can expose structured data.
- Wire both into the request path, not as an afterthought — the output check should block the response from reaching the user, not just log a warning.
- Log every block, on both sides. Every blocked input previews the next attack pattern; every blocked output shows where your model's failure modes actually are.
A concrete demo pattern: a request the topic guardrail should reject returns something like "Blocked. Topic not allowed"; a request that trips the toxicity filter returns "Unsafe content detected"; a request carrying sensitive data returns "Sensitive information detected". All three live on the input side and never touch the model. The output side runs its own, separate content check on whatever the model actually produces.
Pitfalls
- Treating the output check as optional because the input check "should" catch everything. It won't — input filters are pattern-based and adversaries iterate on patterns.
- Using the same classifier or prompt for both checks. "Is this request acceptable to process" and "is this response acceptable to show" are different questions.
- Not logging blocks. Without logs, you can't see attackers converging on a bypass, or legitimate users getting false-positived.
- Setting the checks once and forgetting them. New guard types show up as your feature gets used in the wild — treat guardrails as a living list.
Power tricks
- Log every block, both sides, from day one. The patterns attackers try against your input check are literally the spec for your next guardrail rule.
- Test with a short, correct request against a long, adversarial one to make sure your input filter isn't accidentally length-biased or over-broad.
- Treat the two checkpoints as independently owned — different failure modes, different test suites, different alerting thresholds.
Resources
Shipping an AI feature? Yeda AI designs, audits, and ships production LLM systems.