TRANSMISSION//OPEN ENTRY_027
Guardrails: A call for taxonomy
Everyone's talking about guardrails in agentic systems, but what does that actually mean? A look at a few different categories of guardrails I've come across and why each are important.
One of the topics that comes up frequently when we talk about agentic systems is trust. How can we ensure that agents are doing what we’ve asked them to do? How do we ensure they’re not accessing system resources that they don’t have access to? How do we prevent agents from telling us confidently that they’re “done” the task at hand when a simple follow up tells us that they’re sitting on a throne of lies?
Guardrails.
But what does that actually mean in practice? Why is the inclusion of the word typically sufficient to quell most angst? Why is there not much talk about the different kinds of guardrails and what’s needed for them to be implemented? That, ladies and gentlemen, is what I’m going to look at in this post: the taxonomy of guardrails, or at least how I see them.
The pieces here aren’t novel, but there’s surprisingly little discussion that brings these different approaches together. I figured I’d take a stab at establishing some vocabulary that I can use at my next agentic systems design session.
Let’s dig right in.
Semantic guardrails
High level concept: Is this the right thing to do?
Semantic guardrails are likely what most handwavy answers around agentic safety are referring to. In a nutshell, they’re using an LLM to evaluate whether or not something meets a set of expectations:
- Did the agent do what it was asked?
- Is the result correct?
- Did it introduce changes that weren’t requested?
They can take a number of shapes, each with different implications for how much control you actually have over the system. A few examples:
Skills
It can be prose baked into a single skill. The guardrail is Step 3: the agent is asked to grade its own homework before reporting back.
Skill: bump-dependency
Step 1: Update the version in package.json.
Step 2: Run `npm install` and `npm test`.
Step 3: Before reporting back, re-read the original request and your diff.
- Did you change anything that wasn't asked for?
- Did the tests actually run, or are you assuming they passed?
If either answer is bad, fix it before continuing.
Step 4: Report back.
Subagents
It can be prose spawning a subagent. The guardrail is Step 4: a second LLM, with a fresh context and without the first agent’s reasoning to influence its judgement, acts as the judge.
Step 3: Implement the change.
Step 4: Spawn a reviewer subagent. Give it ONLY the original request and the
diff (not your reasoning) with this prompt:
"Does this diff do what the request asks, and nothing else?
Run the tests yourself. Reply PASS, or FAIL: <reason>."
Step 5: On FAIL, address the reason and return to Step 4.
After 2 failures, stop and ask the user.
Graphs
Or it can be a graph of agents, where the judge is its own node and the edges decide what happens next. The semantic guardrail is still the judge node, but there’s an important difference here. Assuming the graph is enforced by software rather than navigated by an LLM, nothing reaches the output without passing through that node.
The judge is a semantic guardrail, but the graph itself is providing an operational guarantee that the judge is actually invoked.
Two different kinds of guardrails working together. We’ll come back to that shortly.
judge:
input: original request + plan + result (not the executor's reasoning)
rule: "Does the result satisfy every item in the plan?
Reply PASS, or FAIL: <reason>."
on PASS: emit result
on FAIL: route back to executor with the reason (max 2 loops),
then escalate to a human
So why aren’t they enough on their own?
Semantic guardrails are fantastic for providing checks and balances that require human-like judgement. They can catch issues that would be incredibly difficult to account for with deterministic code, particularly when dealing with the enormous variety of failure modes in LLM-generated output.
However, we’re dealing with probabilistic systems here. An LLM judge can be wrong. It can approve something it shouldn’t have or reject something that’s perfectly valid. Adding another LLM doesn’t magically make that problem disappear.
Because the probabilistic system is in control (at least in the skill and subagent examples), we’re leaving the decision up to the LLM whether these controls are actuated and whether or not they are reported accurately, which can both leave the operator in a trying position at times.
When was the last time a model told you “Sorry, you’re right. I should have…”?
So yeah, semantic guardrails are incredibly useful, but on their own, they may not be sufficient for your system. Sometimes we need stronger guarantees around how the system operates, regardless of what the model decides to do.
Which brings us to…
Operational guardrails
High level concept: Is the system following the required process?
Operational guardrails are software-enforced contracts governing how your agentic system operates. Rather than solely relying on an LLM to do all the right things, we use good old software to control the topological behaviour, deterministically constraining how the system behaves.
If we want to ensure that code is actually reviewed before it’s signed off on, or that a prod database is back online before we flip a switch to reactivate public traffic, we do not want to rely on a probabilistic system, but rather deterministic software controls.
There are a number of ways to accomplish this as well. Here’s a simple example:
Code-based workflow
There’s nothing inherently magical about operational guardrails. They’re typically good old software.
result = executor.run(request)
for attempt in range(2):
review = judge.run(request, result)
if review.passed:
return result
if attempt == 0:
result = executor.retry(review.reason)
raise HumanEscalationRequired()
Kinda boring, right? It should be, it’s simply adding software-owned constraints around the magic of the LLM. In these cases, the LLM doesn’t get to decide whether the review happens, or how many opportunities it gets to correct itself. That’s now enforced in software.
Why aren’t operational guardrails enough on their own?
The astute reader will have seen that, while the above example has old boring
software, it’s still using a handwavy judge. That judge will likely be a
function that calls an LLM and hands them the reins temporarily.
Software-owned guardrails can guarantee that a process is followed, but they can’t necessarily determine whether the decisions made within that process are correct. We can add deterministic checks like unit tests, static analysis and the like. Sometimes that’s all we need. But when the correctness determination requires human-like judgement, we’re back to relying on an LLM or human to make that call.
Capability guardrails
High level concept: What is the system actually allowed to do?
So far, we’ve seen how we can add controls around how a request is satisfied. We’ve seen how we can start to trust the behaviour of the system you’re working with.
But what about the system resources that a given agent has access to? What if an agent decides to leak your SSH keys? Or open a backdoor to your system? Or exfil data to some potentially malicious actor?
Sure, we can add rules to a semantic guardrail, but we’ve already taken a look at how those can fail. When system resource protections fail, many people can be in for a Bad Day. This is where capability guardrails come into play.
Of the three, I’ve found this one to be particularly fiddly to implement properly, especially when portability is a concern.
Capability guardrails are generally implemented through some combination of sandboxing and policy enforcement. The goal is to constrain what the agent can actually do instead of relying on it to decide what it should do.
Sandboxing
Sandboxing provides system-level containment of the agentic system. Some use docker containers, some use microVMs, others (like myself) implement kernel-level sandboxing using established system utilities (see my kernel-level agent sandboxing post for more details).
Policies
Sandboxing establishes the isolation boundary. Policies determine what we’re allowed to do within (or across) that boundary. Depending on the implementation, those policies might be enforced by the sandbox itself, a network proxy, a tool broker, or some other mechanism outside the LLM’s control.
| Capabiliy | Restriction |
|---|---|
| Outbound network access | Only allow connections to approved destinations |
| IPC | Only allow communication with approved processes or sockets |
| Filesystem writes | Enforce storage quotas |
In practice, we need both clearly defined permissions and mechanisms that actually enforce them. A policy that an agent can simply ignore isn’t providing much of a guardrail.
Throwing it all together
Three categories of guardrails, each addressing a different problem:
- Semantic: Is the agent doing the right thing?
- Operational: Is the system following the process we’ve defined?
- Capability: What is the agent actually allowed to do?
You can have all the semantic guardrails in the world, but if your system can simply bypass them, how much trust can you actually build? Similarly, a perfectly enforced workflow isn’t going to help much if an agent has unrestricted system access.
While I’d argue that all three types of guardrails deserve consideration when building a trusted agentic system, that doesn’t mean every system needs all three. It all depends on the risk tolerance around the system you’re building (see more in Risk and Judgement: A Pattern for Agentic System Design).
So there you have it: my thoughts on the different categories of agentic system guardrails. Other than a personal braindump, my hope is that others may be able to take a nugget or two from it for the next time someone handwaves “guardrails” at them. Or tell me how I’m completely wrong in the comments below. Either or.
Comments