Guardrails an agent cannot argue with

An instruction lives in the prompt. A guardrail lives somewhere the model cannot reach.

The idea

Write "never issue a refund over five hundred without approval" into a system prompt and you have expressed a preference. Everything the agent reads afterwards, including the ticket it is working on, is also text, and text can argue.

A guardrail is the same sentence enforced somewhere else: in the credential the agent was issued, in the list of calls it is allowed to make, in a gate that requires a human signature before the call lands. Those cannot be talked around, because they do not read.

The part people find uncomfortable is that a good guardrail does not stop the model being fooled. The model is fooled either way. What changes is whether being fooled has consequences.

An agent trace on the left and the layers a refund call has to pass through on the right, in prompt-only and enforced modes

How it works

The check that matters is not inside the reasoning. It sits between the decision and the effect, and it takes no argument from the model about whether it should apply.

# the guardrail, outside the model, on the call path
def issue_refund(actor, amount, order_id, approval=None):
    if "refund:write" not in actor.scopes:            # credential scope
        raise Forbidden("this agent holds read scopes only")
    if "issue_refund" not in actor.allowed_tools:     # tool allowlist
        raise Forbidden("issue_refund is not callable by this agent")
    if amount > 500 and approval is None:             # non-overridable gate
        raise NeedsApproval(amount=amount, order=order_id)
    return payments.refund(order_id, amount)

# what does NOT work
SYSTEM_PROMPT = """You are a support agent.
Never issue a refund over 500 without approval."""      # a preference, not a rule

Notice that the gate raises NeedsApproval rather than returning false. The agent is told what to do next, so a blocked action becomes an escalation with all the context attached, instead of a dead end it will try to route around.

Cost

LayerStopsCosts
Prompt instructionHonest mistakes, some of the timeNothing, and it is worth having anyway
Credential scopeEvery write, including ones you did not think ofReal work the agent then cannot do
Tool allowlistThe specific calls you namedA list to maintain as tools are added
Approval gateHigh-value actions, individuallyA human in the path, and the risk they stop reading

Layers are not redundant. Scope is broad and blunt, the allowlist is specific and needs upkeep, the gate is precise and expensive. Turn each one off in the panel above and watch which of the others still catches the call.

Watch out for

Worked example

A support queue gets four hundred tickets a day and an agent drafts first replies. One ticket contains a sentence written to be read by the agent rather than by a person: it cites a policy exception that does not exist and asks for the approval step to be skipped.

Run it above in prompt mode. The agent reads the ticket, finds a rule in its instructions, finds a more specific sounding exception in the ticket, and resolves the conflict the way text usually resolves conflicts: the most recent and most specific wins. The refund goes out.

Now switch to enforced and run exactly the same trace. The reasoning does not improve. The agent still decides to refund, and the call is refused before it reaches the payment system, because the credential it holds has no refund scope at all. Turn that layer off and the allowlist catches it. Turn that off too and the approval gate catches it, and turns the block into an escalation with the amount, the order, and the reason attached. Turn off all three and you are back to hoping.

Check yourself

Your agent must never touch production data. Which of these is a guardrail?