Every agent demo looks the same: a clean happy path, a handful of tools, a task that resolves in three steps. Then someone asks the question that ends the meeting — what stops it from doing that to the wrong record? Usually nothing does, because guardrails were never designed, only assumed.

Guardrails are not a prompt instruction telling the model to be careful. A determined-enough context window will talk a model out of caution. Guardrails are constraints enforced outside the model, in code the model cannot argue with.

Separate what the agent can see from what it can do

The most common failure we find in agent audits is a single service account with write access to everything the agent might plausibly need, granted once and forgotten. Scope tool permissions the way you would scope a junior engineer's first week: read access broadly, write access narrowly, and nothing destructive without a second gate.

  • Give read tools wide scope — the agent needs context to reason well
  • Give write tools the narrowest scope that completes the task
  • Put irreversible actions behind an explicit confirmation step, human or programmatic
  • Treat every tool credential as something you will need to revoke in a hurry

Approval gates only work if they interrupt the right things

A gate on every action trains users to click approve without reading. A gate on nothing means the first mistake is already in production. The useful middle is risk-based: read and low-stakes writes proceed automatically, anything touching money, external communication, or deletion pauses for a human.

The goal is not zero mistakes. It is that every mistake is small, visible, and reversible. Design for the failure, not just the success.

Log the reasoning, not just the action

When an agent does something wrong, 'it called the refund tool' tells you what happened but not why. Capture the intermediate reasoning and the tool inputs alongside the output. Without that trail, every incident becomes an argument about what the model 'must have' been thinking.

log.write({
  step: 'tool_call',
  tool: 'issue_refund',
  input: { order_id, amount },
  reasoning_snapshot: agent.lastThought,
  requires_approval: amount > threshold,
});

Test the agent against its own failure modes

Standard evaluation sets check whether the agent gets the right answer. A separate set should check whether it does the wrong thing safely — ambiguous instructions, conflicting data, a tool that returns an error mid-task. Run those before every deployment, not just once at launch.

None of this slows an agent down in the case that matters. It slows down the one case in a thousand that would otherwise have become an incident report.