Contents · 7 sections↓
An AI agent that can refund orders, send emails or delete records will eventually try to do one of those things when it shouldn't. Human in the loop AI is the standard answer: a person approves the risky steps before they happen. This glossary page defines human in the loop for AI agents, shows how an approval gate works with a real run, compares it with related terms, and covers the design mistakes that make approvals useless.
What is human in the loop AI?
Human in the loop (HITL) AI is a design in which a person reviews and decides on an AI system's actions before they take effect. For AI agents, it usually means an approval gate: when the agent asks for a risky tool call, such as a refund, the run pauses until a human approves, edits or rejects it.
Key takeaways:
Gate actions, not answers. The useful place for a human is before a tool call that changes something, not after every message.
The agent should learn the decision. A rejection, ideally with a reason, comes back to the model as the tool's result, so it can explain or choose another path.
Decide what happens on silence. Every approval needs a timeout and a default. The safe default is to deny.
Too many approvals are as bad as none. Reviewers who approve dozens of routine requests a day stop reading them.
The term is older than AI agents. In machine learning it also describes people labeling data or rating model outputs during training, as in reinforcement learning from human feedback. This page is about the run-time meaning: a human deciding on an agent's actions while it works.
[02]
How does human in the loop work for AI agents?
The approval gate sits between the model's tool request and the tool itself. Because the model only requests tool calls and your code executes them, the code can stop and wait.
model asks: issue_refund(order_id="A-8", amount=840)
│
▼
┌───────────────────────────┐ no ┌──────────────┐
│ does this tool need │───────▶ │ tool runs │
│ approval? (policy) │ └──────────────┘
└───────────────────────────┘
│ yes
▼
┌───────────────────────────┐ Slack, web UI, queue, webhook
│ send request to a human │─────────────────────────────────▶ reviewer
│ and wait (with timeout) │◀───────────────────────────────── approve / edit / reject
└───────────────────────────┘
│
├─ approved ──▶ tool runs (with edited arguments, if changed)
├─ rejected ──▶ "DENIED: <reason>" becomes the tool result
└─ timeout ──▶ default action, usually deny
│
▼
model reads the result and answers or tries another wayThree choices define a gate. Which calls need approval: a list of tool names or patterns, sometimes with rules on the arguments, such as refunds over $100. Where the request goes: a chat message with buttons, a web dashboard, a queue or a webhook. What happens while waiting: a short wait can block the run, but a wait of hours needs state that survives a restart, which is a different problem; see durable AI agents.
[03]
A human in the loop example
The agent below has a refund tool behind an approval policy. The reviewer function stands in for the human: in production it would post to Slack or a dashboard and wait. Here it approves refunds up to $100 and rejects the rest, with a reason.
import asyncio
from langchain_core.tools import tool
from promptise import build_agent
from promptise.approval import ApprovalDecision, ApprovalPolicy
@tool
def get_order(order_id: str) -> dict:
"""Look up an order by its ID."""
return {"order_id": order_id, "total": 840.00, "status": "delivered"}
@tool
def issue_refund(order_id: str, amount: float) -> str:
"""Refund part or all of an order."""
return f"Refunded ${amount:.2f} on {order_id}"
async def reviewer(request):
# In production this posts to Slack, a web UI or a queue and waits.
# Here the "human" approves refunds up to $100 and rejects the rest.
print(f"APPROVAL NEEDED {request.tool_name} {request.arguments}")
if request.arguments["amount"] <= 100:
print("REVIEWER approved")
return ApprovalDecision(approved=True, reviewer_id="sam")
print("REVIEWER rejected")
return ApprovalDecision(
approved=False, reviewer_id="sam", reason="Refunds over $100 need a manager."
)
async def main():
agent = await build_agent(
model="openai:gpt-5-mini",
instructions="You are a support agent. Answer in one sentence.",
extra_tools=[get_order, issue_refund],
approval=ApprovalPolicy(tools=["issue_refund"], handler=reviewer, timeout=120),
)
try:
for question in [
"Refund $40 on order A-7 for the late delivery.",
"Refund order A-8 in full.",
]:
print(f"\nUSER {question}")
result = await agent.ainvoke({"messages": [{"role": "user", "content": question}]})
print(f"AGENT {result['messages'][-1].content}")
finally:
await agent.shutdown()
asyncio.run(main())Real output from one run with gpt-5-mini:
USER Refund $40 on order A-7 for the late delivery.
APPROVAL NEEDED issue_refund {'order_id': 'A-7', 'amount': 40.0}
REVIEWER approved
AGENT Refund of $40 for order A-7 has been completed.
USER Refund order A-8 in full.
APPROVAL NEEDED issue_refund {'order_id': 'A-8', 'amount': 840.0}
REVIEWER rejected
AGENT I attempted to refund order A-8 for the full amount ($840.00) but it was denied because refunds over $100 require manager approval—would you like me to escalate this to a manager for approval?The model never saw the policy. For each refund it asked for the tool as usual, and the gate paused the call and handed the reviewer the tool name and arguments. The $40 refund ran. The $840 refund didn't, and the reviewer's reason came back to the model as the tool result, so the agent could tell the user why and offer to escalate. In Promptise Foundry a rejected call returns DENIED: plus the reason, the default timeout is 300 seconds, and a timeout denies the call. The approval docs list webhook, queue and callback handlers for real reviewers.
[04]
Human in the loop vs related terms
Term | What the human does | When | Fits |
|---|---|---|---|
---|---|---|--- | |||
Human in the loop | Approves, edits or rejects each gated action | Before the action runs | Refunds, payments, deletes, external emails |
Human on the loop | Watches the agent and can stop or correct it | During or after the run | High-volume, low-risk actions with good monitoring |
Human out of the loop | Nothing at run time | Never | Read-only tools and fully reversible actions |
Risk-based (auto) approval | Reviews only calls that rules flag as risky | Before risky actions | Agents with many routine calls |
RLHF and data labeling | Rates or labels outputs to train a model | During training | Model development, not agent operation |
Most production agents mix these: read-only tools run freely, routine writes are auto-approved by rules, and only rare, costly or irreversible actions reach a person. Approval fatigue shows how to set up those rules.
Regulation points the same way. Article 14 of the EU AI Act requires that high-risk AI systems can be effectively overseen by people while in use, and asks that overseers stay aware of the tendency to over-rely on a system's output.
[05]
Common misconceptions about human in the loop
"Approving everything makes the agent safe." If a reviewer sees fifty requests a day and all of them are fine, they start clicking approve without reading. The one bad request gets the same click. Gate fewer, riskier calls, and show the reviewer what matters.
"The reviewer can judge the call from the tool name." "issue_refund" says little. Show the arguments, the user's request and recent context, and redact secrets and personal data the reviewer doesn't need.
"A rejection ends the run." It shouldn't. The model should get the decision and the reason as a tool result, so it can tell the user, ask for missing information, or take a safer path. A bare error leads the model to retry or to make up an explanation.
"Approval in the agent is enough." An agent-side gate protects one agent. If several agents or apps call the same tools, put the gate on the tool server too, so every caller goes through it. See AI agent human in the loop for both options.
[06]
Frequently asked questions
What does human in the loop mean for AI agents?
It means a person approves, edits or rejects an agent's risky actions before they run. The agent asks for a tool call, the call pauses, a reviewer decides, and the agent continues with the result. Low-risk actions such as lookups usually run without approval.
Which AI agent actions should need human approval?
Gate actions that are expensive, irreversible or visible to others: payments and refunds, deleting data, sending emails or messages outside the company, changing permissions, and deploying code. Let read-only tools run freely, and use rules to auto-approve routine low-value writes so reviewers see only the calls that matter.
What happens if nobody approves in time?
The gate applies its timeout action. The safe default is to deny the call, so the agent tells the user it couldn't complete the action. Allowing on timeout only makes sense for low-risk tools. For approvals that may take hours, the run's state must be saved so the agent can resume later.
What is the difference between human in the loop and human on the loop?
With human in the loop, a person decides before each gated action runs. With human on the loop, the agent acts on its own while a person monitors it and can intervene or undo. The first gives more control at the cost of speed; the second scales better but relies on good monitoring and reversible actions.
How do you avoid approval fatigue?
Send fewer, better requests. Auto-approve routine calls with rules, such as refunds under a set amount, and keep humans for large, unusual or irreversible actions. Show reviewers the arguments and context they need, and log every decision with who made it and why.
[07]
Next steps
Build it: AI agent human in the loop.
Fewer, better approvals: Approval fatigue.
Send approvals to Slack or a webhook: AI agent notifications.
Record every decision: AI agent audit log.
All guides on the topic: the human in the loop hub.
Reference: Approval in the Promptise docs.