Skip to content

Guardrails and Sandboxing for AI Agents

CoreConceptJuly 27, 20264 min read

An AI agent becomes risky the moment it can read private data, call tools, write files, send messages, or trigger business workflows. A better prompt helps, but it cannot be the only thing standing between a model mistake and a production side effect.

This guide is for engineers adding tool access to agentic systems. We will secure one refund_order agent by mapping trust boundaries, enforcing tool policies, isolating execution, and designing recovery paths. For related protocol placement, see MCP vs A2A vs ACP.

Agent guardrails: boundary, policy, sandbox, recovery
Agent guardrails: boundary, policy, sandbox, recovery

Map the trust boundaries

A guardrail is useful only when it sits on a real boundary. User text is untrusted. Retrieved documents are untrusted. Model output is untrusted. Tool arguments proposed by the model are untrusted until validated by code. The trusted parts are the runtime, policy engine, identity provider, and the services that own durable state.

For refund_order, the agent may propose { orderId: "123" }, but the payment service still owns whether a refund is allowed. The runtime must check user identity, tenant, order state, amount, idempotency key, and approval requirements before calling the payment provider.

Trust map: user text, retrieved text, model output, runtime policy, durable service
Trust map: user text, retrieved text, model output, runtime policy, durable service

Quick reference

  • Separate data boundaries from action boundaries; reading an order is not the same as refunding it.
  • Treat retrieved text as hostile input when it can influence a privileged tool call.
  • Model output should become a typed request, never direct execution.
  • Authority lives in identity and policy checks outside the model.
  • Durable systems such as payments, orders, and tickets remain the source of truth.

Remember this

Guardrails belong at boundaries where untrusted text could become privileged action.

Enforce tool policies in code

Tool policy should be explicit: which users can call which tools, with which arguments, under which approval mode, and with which rate limits. Put the rule in code or configuration the model cannot rewrite. The model may explain why it wants a refund; it should not decide alone that the refund is authorized.

For side-effecting tools, use idempotency keys and typed outcomes. A timeout after sending a refund request is an unknown commit state, not a clean failure. Recovery means querying by idempotency key before retrying.

Policy checks turn model-proposed arguments into allowed or denied tool calls
Policy checks turn model-proposed arguments into allowed or denied tool calls

Quick reference

  • Validate schema, types, tenant, ownership, and allowed action before execution.
  • Split read tools from write tools so approval rules are simpler.
  • Return typed denials such as invalid_args, not_authorized, and approval_required.
  • Use idempotency keys for every external side effect that can be retried.
  • Log redacted arguments and result codes for later eval and incident review.
Unsafe direct tool call
1async function run(modelArgs: any, payment: any) {2  return payment.refund(modelArgs.orderId);3}
Policy-checked tool call
1async function refundOrder(args: unknown, ctx: UserContext, payment: PaymentApi) {2  const parsed = RefundSchema.safeParse(args);3  if (!parsed.success) return { ok: false, code: "invalid_args" };4  if (!ctx.permissions.includes("refund:create")) {5    return { ok: false, code: "not_authorized" };6  }7  if (parsed.data.amountCents > 5000 && !ctx.approved) {8    return { ok: false, code: "approval_required" };9  }10  return payment.refund(parsed.data.orderId, {11    idempotencyKey: ctx.requestId,12    timeoutMs: 5000,13  });14}

Remember this

The model may request a tool call; policy code decides whether that call is allowed.

Sandbox what the agent can touch

Sandboxing limits blast radius when a policy misses something. For coding agents, isolate filesystem writes, shell commands, network access, environment variables, and credentials. For browser agents, isolate cookies, sessions, downloads, and domains. For data agents, isolate tenants and make production writes explicit.

A useful sandbox is not only a container. It is the combination of a disposable workspace, scoped credentials, network allowlists, resource limits, and an exit path that captures evidence. If the agent needs broader access, make that access temporary and visible.

Sandbox limits filesystem, shell, network, credentials, and resources
Sandbox limits filesystem, shell, network, credentials, and resources

Quick reference

  • Filesystem: work in a branch or disposable checkout; protect secrets and generated credentials.
  • Shell: allow known commands; deny privileged commands and destructive paths by default.
  • Network: allow required domains; block arbitrary outbound calls when data is sensitive.
  • Credentials: issue scoped tokens per run; rotate or revoke after incidents.
  • Resources: cap time, steps, tokens, CPU, memory, and concurrent tool calls.

Remember this

Sandboxing turns one missed instruction into a bounded failed run instead of an unbounded incident.

Design recovery before autonomy

The dangerous failure is not always a dramatic exploit. Often it is ordinary ambiguity: the agent called a payment tool, the provider timed out, and the agent retries with different arguments. Now the system may have two refunds or none, and the trace is too vague to tell.

Recovery should be designed before increasing autonomy. For refund_order, record request IDs, use idempotency keys, query provider status after timeout, keep approval decisions durable, and expose a human review queue for unknown outcomes. The safer agent is not the one that never fails; it is the one whose failures have known terminal states.

Failure detail: timeout after side effect requires status lookup before retry
Failure detail: timeout after side effect requires status lookup before retry

Quick reference

  • Trigger: timeout after a side-effecting call.
  • Symptom: user sees pending or ambiguous result.
  • Root mechanism: network failure hides whether the provider committed the action.
  • Recovery: query by idempotency key before retrying; escalate if state remains unknown.
  • Prevention: typed tool outcomes, approval records, rate limits, and audit logs.

Remember this

Increase autonomy only after every risky tool has a known denial, retry, rollback, or escalation path.

Key takeaway

Guardrails are not one feature. They are layered controls: boundary mapping, policy checks, sandbox limits, traceable decisions, and recovery paths.

Practice (25 min): choose one side-effecting tool in your app. Write its allowed users, denied arguments, approval threshold, idempotency key, timeout behavior, and recovery query. Then simulate a timeout after commit. Pass when the system can detect success without repeating the side effect.

Share:

Related Articles

Prompt injection is what happens when untrusted text tries to steer the model away from the developer's intended instruc

Read

Running one AI coding agent on a task is easy. The moment you want three or thirty of them working at once — without two

Read

AI agents rarely work alone. They read files, query databases, call business APIs, and sometimes delegate work to other

Read

Explore this topic

Keep learning

Follow a structured path or browse all courses to go deeper.