Deep Dive Usecase
Most teams add a human to an agent's loop the way they add a seatbelt warning: late, loud, and everywhere. Every action gets a confirm dialog. Within a week people are clicking yes without reading, and the gate is now a delay that looks like a control.
The fix is to treat the human as a design component with a budget. Attention is the scarce resource, and each approval request spends some of it. Spend it on the few actions that matter and people stay alert. Spend it on everything and you train them to approve on reflex.
So sort actions by two questions. Can it be undone? And how far does the damage reach if it's wrong? Reading a file, drafting a reply, or running a query on a copy is reversible and local: let it run, and log it. Sending an email to a customer, deleting records, spending money, or changing permissions is hard to reverse or reaches beyond the task: stop and ask.
Take a support agent that can look up orders, draft replies, issue refunds and close accounts. Lookups and drafts run freely. Small refunds inside a fixed limit run too, and each is logged so someone can review them in a batch afterwards. A large refund or an account closure pauses for a person.
Concretely, the gate is a small piece of ordinary code that sits between the model and your tools. The model never runs a risky tool itself; it only proposes a call, and your code decides what happens next:
def policy(call):
if call.tool in ("lookup_order", "draft_reply"):
return "allow"
if call.tool == "issue_refund":
return "allow" if call.args["amount"] <= 50 else "ask"
if call.tool == "close_account":
return "ask"
return "deny" # anything unlisted is blocked
for call in agent.proposed_calls():
verdict = policy(call)
if verdict == "allow":
log(call, run(call))
elif verdict == "ask":
save_state(run_id) # checkpoint, then stop
send_to_reviewer(run_id, call) # Slack, email, or a queue page
break
else:
agent.tell("Not permitted: " + call.tool)
Here is one request end to end. The agent proposes issue_refund(order=1042, amount=180). The policy sees 180 is over the limit and returns "ask". The run is saved and stopped, and a person gets a message. Minutes or hours later they click approve, and your code reloads the saved run and executes that exact call. The agent carries on as if nothing happened. The model never sees the policy, so no prompt can talk it past the gate. Keep the check on the tool, not on the agent: when one agent can write a program that spawns many more, only a gate at the tool boundary still sees every call.
A model can pre-screen the "ask" cases too. Pydantic AI's new approval reviewer lets a model clear low-risk tool calls, so people only see the ones it won't. That spends attention well, but keep deny and the high-risk rules in code, since a model's yes can be argued with and a rule can't.
Three details keep this honest. Bind the approval to the exact arguments, so a yes to a $180 refund can't be reused for $1,800. Give every request a timeout that defaults to no, so a forgotten request expires rather than lingering. And let the reviewer edit the request, not just accept or reject it. "Approve at $100" is often the answer you wanted.
The request itself matters as much as the placement. A good one shows what will change, in plain words, with the before and after side by side, not "Agent wants to run a tool. Allow?" The reviewer should be able to say no for a reason. If it can't be understood in ten seconds, it will be approved without being understood.
To check your own gates, look at how they get answered. If nearly every request is approved, and quickly, the gate is not catching anything: either the action is safe enough to run on its own, or the request is too vague to judge. Either way, change the design, not the person.
Human-in-the-loop is not what you fall back on when the system can't be trusted. It is a choice about where judgment is worth its cost, made on purpose, and kept small enough that people still bring judgment to it.
Tools
Human-in-the-loop - Per-tool interrupt_on settings choose which tool calls pause for a person. A when predicate can gate on the call's arguments, and the reviewer can approve, edit, reject or respond.
Human-in-the-loop - OpenAI Agents SDK - needs_approval can be True or an async callable that decides per call from the tool arguments. Paused runs list pending approvals, can be serialized for slow reviewers, and resume after approve or reject.
Try this
Think of the last five confirm dialogs you clicked through in any tool, and for each one ask whether you could have said no for a reason. If you read fewer than half of them, that gate is a delay that looks like a control. If you build agents, take one gate's log of approval requests, count how many were approved and how long each took to answer (the request and answer timestamps give you this), and split them by action type. A type that is approved nearly every time, in seconds, is a candidate to run on its own with logging, or to rewrite so the reviewer can actually judge it.
Worth your time
How we built Claude Code auto mode: a safer way to skip permissions - Read-only tools and in-project edits run ungated, while shell commands, web fetches and out-of-project writes go through a classifier. The authors report users accept 93% of manual prompts and call the 17% miss rate on overeager actions 'the honest number'.
Human-in-the-Loop Gates for AI Agents: Approval Without Review Fatigue - Says the application, not the model, should decide when a gate fires, with rules enforced at the execution boundary. An approval request should state the action in plain language, why the gate fired, and the impact.
Releases worth knowing
Pydantic AI v2.57.0 - Adds an approval reviewer that lets a model approve low-risk tool calls, so only the ones it won't clear reach a person. Worth trying as a first screen, with high-risk rules kept in code.
Claude Platform release notes (API) — The Oct 14 entry adds beta OpenTelemetry export that traces Messages API requests and Managed Agents sessions, which gives you the request and answer timestamps this issue's Try this step above asks for. Oct 9 adds beta dynamic workflows, where an agent writes a program that runs many agents, a reason to gate at the tool, not the agent.
Microsoft Agent Framework dotnet-1.25.0 - Tool-approval requests that never get a response are now rejected automatically, so an unanswered gate fails closed instead of hanging. The notes also add "approval reuse considerations", which is the standing-approval side of the same budget.
Remote Roles
ElevenLabs is hiring a Forward Deployed Engineer
Remote Location: USA

