An agent can issue refunds, modify production configuration, and send external messages. Where would you enforce policy, approvals, rate limits, and human confirmation, and how would you keep the model from bypassing those controls through another tool?
Enforce controls in a server-side tool gateway and again in the systems that perform the side effect, never in the prompt or model. Bind approvals and confirmations to the exact action, apply rate limits to the actor and resource, and give every tool—including indirect tools—the same least-privilege, policy-checked route.
How to think about it
Enforce policy at a server-side tool gateway, with a second check in the system that performs the side effect. Put approvals and human confirmation in a trusted workflow bound to the exact action, apply rate limits to the actor and target, and ensure every tool—including indirect tools—must use the same least-privilege path.
The model should propose an action. It should never be the thing that authorizes or executes one.
Why the boundary matters
A prompt saying “never refund more than $100” is guidance, not a security control. The model can misunderstand it, forget it after a long conversation, or be persuaded by a malicious document saying that an emergency exception applies. Tool descriptions have the same weakness. They are instructions to a probabilistic system, not an access-control mechanism.
The reliable boundary is the call from an untrusted agent runtime into a trusted service. That service receives a structured request containing at least:
- who is acting: user, tenant, agent, and workload identity;
- what action is requested;
- the exact arguments and target resource;
- the original task and correlation identifier;
- any approval or confirmation token;
- the requested data and side-effect scope.
The gateway authenticates the caller, validates the arguments, evaluates policy, checks quotas and approvals, and only then invokes the business service. The business service checks again before committing the change. That second check matters because a bug, stolen credential, or future alternate route should not turn the gateway into a single point of failure.
A useful mental model is:
model proposes → gateway decides → human may authorize → service commits → audit records the result
The model can ask for approval. It cannot manufacture approval.
A concrete policy
Imagine a support agent handling a duplicate-charge incident at 3 a.m. It can inspect the order, issue a refund, change a production feature flag, and email the customer.
An illustrative risk policy might look like this:
| Action | Example policy | Why |
|---|---|---|
| Refund | Automatically allow up to $50 for one settled charge, after identity and order checks. Above $50 requires an authorized support lead. | Money leaves the company, and repeated refunds can become fraud. |
| Production configuration | Require a human approval for every production change, a canary where possible, and a recorded rollback plan. | A one-line setting can affect every customer. |
| External message | Allow a draft automatically. Require confirmation before sending one message; require approval for more than 10 recipients or legal, security, or pricing claims. | A sent message is difficult to retract and can create commitments. |
Suppose the agent proposes a $240 refund, changes checkout_timeout_ms from 3000 to 10000, and sends an apology email.
The gateway should not accept a vague request such as “fix the customer’s problem.” It should receive three explicit actions. The refund decision sees an amount of $240 and returns “approval required.” The configuration decision sees a production target and returns “approval required,” regardless of whether the model describes the change as harmless. The email may be rendered as a preview, but sending it requires a person to confirm the exact recipients, subject, body, and attachments.
That distinction is important:
- Approval is an authorized person granting permission under organizational policy.
- Confirmation is a person acknowledging the exact effect about to happen, often after seeing a preview.
For a high-impact action, you may require both. A support lead approves the $240 refund, then the requesting operator confirms the final customer and amount. Neither event should be a general “this agent is trusted” switch.
The approval must be tied to a canonical representation of the action: a stable serialization of the exact arguments. If the approved refund is for order A-1842 and $240, the agent cannot change it to order A-1843 or $2,400 and reuse the approval. The approval should also have a short expiry, a one-time use, the intended tenant, and the relevant policy version. The gateway rechecks all of those immediately before execution.
Rate limits are not just request limits
A limit of 100 tool calls per minute is not enough. An agent could make 100 harmless reads and still issue 20 refunds if the refund endpoint has no separate budget.
Rate limits should be attached to the identity and the effect. For example:
- no more than 5 refund attempts per customer in 24 hours;
- no more than $500 refunded by one support workflow per hour;
- no more than 2 production configuration changes per service in 10 minutes;
- no more than 10 external messages per task without an additional approval.
The counters should normally be enforced server-side and updated atomically with the action. Otherwise two concurrent agent runs can each observe “$400 remaining” and together spend $800. Use idempotency for retryable actions too: if the agent times out after the payment service commits a refund, a retry should return the original result rather than issue a second refund.
Rate limits reduce blast radius. They do not replace authorization. A correctly authorized attacker is still dangerous if the limit is too generous, and a legitimate user can still be harmed by a wrongly targeted action.
Preventing bypass through another tool
The hard part is not protecting the obvious refund tool. It is stopping the model from finding a side door.
I would enforce four rules.
First, expose capabilities rather than raw infrastructure. The agent may receive a narrowly scoped issue_refund capability, not database credentials, a general-purpose HTTP client, shell access, or a secret that can call the payment provider directly. A tool named update_order must not secretly be able to alter payment state. If a tool can execute arbitrary code or arbitrary network requests, its effective capability is much larger than its friendly description suggests.
Second, make all tool-to-tool calls go back through the broker. If the email tool calls a customer-management service, that call carries the original principal, task identifier, target, and cumulative budget. The second call gets a fresh policy decision. An approval for “refund order A-1842 for $240” cannot authorize a later call to a generic “run operation” tool.
Third, enforce based on effect, not tool name. A model might avoid refund and call create_credit, cancel_invoice, or a generic payment endpoint. The receiving service must classify those operations as money-moving actions and apply the same policy. This is why domain services need authorization checks of their own.
Fourth, control credentials and egress. The model runtime should not hold reusable production or payment credentials. A broker can mint short-lived, narrowly scoped credentials after policy passes. Network controls should restrict which services the runtime can reach. Log every side effect, including the tool identity, arguments or safe argument hash, decision, approval, result, and originating task.
A practical test is to ask: “Can the agent cause a refund without producing the refund audit event?” If the answer is yes through a CRM endpoint, webhook, SQL tool, or undocumented integration, the design has a bypass.
The failure mode you should expect
The first symptom of a missing control is often not a dramatic outage. It is an audit mismatch:
refund_completedappears in the payment system, but there is no matching gateway decision or approval record.
Another common symptom is that the agent reports, “The refund was approved,” while the gateway has no approval token at all. Treat the model’s statement as untrusted. The source of truth is the payment service and the audit trail.
This usually means someone added a convenient secondary path: an old admin endpoint, a broad service account, or a “generic API” tool. Fix it by inventorying every side-effecting route, removing direct credentials, requiring all routes to emit the same authorization context, and adding contract tests that attempt the action through every available tool.
The senior-level nuance
Do not put an LLM in charge of the final policy decision merely because the request is ambiguous. A model can classify risk, extract facts, or suggest which human should review it. Deterministic authorization should decide based on facts such as amount, environment, tenant, resource ownership, and recipient count.
Nor should you require a click for every low-risk read or reversible change. That creates approval fatigue. People approve five nearly identical prompts at 3 a.m.; the sixth one gets waved through without inspection. Use risk tiers, previews, automatic rollback where possible, and human review at the last responsible moment.
The trade-off is latency and operational friction. A production change may wait five minutes for an approval, and a customer may wait for a refund. That is preferable to silently granting the model a company-wide debit card. For high-volume, low-risk actions, narrow automatic limits and strong monitoring are usually better than pretending a human can inspect every event.
What they’ll ask next
“Would you put the policy in the system prompt?”
No. The prompt can explain policy to improve behavior, but the gateway and domain service enforce it. A prompt injection must not be able to change authorization.
“How do you handle an agent that needs several tools to complete one task?”
Give the task a scoped budget and identity context. Evaluate each side effect separately, carry provenance across calls, and prevent one tool from passing a broad credential to another. Approval covers exact actions, not the whole conversation.
“What happens if the approval is granted and the state changes before execution?”
Recheck policy and resource state at commit time. Bind the approval to the action arguments, use short expiries, and fail closed if the order, amount, configuration version, or recipient list has changed.
One line to say in the room
“I treat the model as an untrusted planner: every side effect goes through a server-side, least-privilege broker and a second domain-level check, with approvals and confirmations bound to the exact action and no alternate tool path around them.”