Approval gate (default)

Human-in-the-Loop diagramTask, Agent, Risk check, Reviewer, Action API, Done. Step captions follow the diagram.TaskAgentRisk checkReviewerAction APIDoneHuman-in-the-Loop diagramTask, Agent, Risk check, Reviewer, Action API, Done. Step captions follow the diagram.TaskAgentRisk checkReviewerAction APIDone

All steps

High risk, approved

High risk, rejected

Low risk, no pause

Parts of this pattern
Task Input / output
The task that starts the run, such as 'Refund customer #4821 for the duplicate $480 charge'.
Agent Agent
Decides the next action and proposes a tool call; replans when the reviewer rejects it.
Risk check Router
Classifier that rates the proposed call high or low risk: irreversible, moves money, or changes production data.
Reviewer Human
A person who sees the action, its rationale and effect, then approves, edits or rejects. The run waits.
Action API Tool
The real action API, e.g. issuing the refund. Runs only after a low-risk pass or human approval.
Done Input / output
Confirmation that the action completed, such as 'refund sent'.
Task → Agent
The task goes to the agent, which decides what action to take.Refund customer #4821 for the duplicate $480 charge.
Agent → Risk check
The agent's proposed tool call goes to the risk check before anything executes.{"tool": "issue_refund", "arguments": {"customer": 4821, "amount": 480}}
Risk check → Reviewerhigh risk
High-risk calls are paused and sent to the reviewer as an approval request.Agent wants to take an action that needs your approval. Action: issue_refund. Arguments: customer 4821, $480. Reversible: no. Reply: approve, edit or reject.
Risk check → Action APIlow risk
Low-risk calls skip review and go straight to the tool.{"tool": "get_order_status", "arguments": {"order": "A-1093"}}
Reviewer → Action APIapprove
The reviewer's approval, with any edited arguments, lets the run resume and execute.{"kind": "approve"} (or {"kind": "edit", "edited_action": {"amount": 240}})
Reviewer → Agentreject
A rejection with a reason goes back so the agent replans instead of acting.{"kind": "reject", "reason": "Deleting account data needs legal sign-off first."}
Action API → Done
The tool's result flows out as the final outcome.{"status": "ok", "message": "Refund of $480 sent to customer 4821."}

Agent asks

The agent itself pauses to ask a person for information it is missing, then carries on with the answer. No risky action is involved.

Human-in-the-Loop: Agent asks diagramTask, Agent, Account owner, Quotes API, Done. Step captions follow the diagram.TaskAgentAccount ownerQuotes APIDoneHuman-in-the-Loop: Agent asks diagramTask, Agent, Account owner, Quotes API, Done. Step captions follow the diagram.TaskAgentAccountownerQuotes APIDone

All steps

Missing a choice, asks first

Nothing missing, no pause

Parts of this pattern
Task Input / output
The task that starts the run, such as 'Send Acme their renewal quote'.
Agent Agent
Works on the task with its tools, and pauses to ask a person when it is missing something it must not guess.
Account owner Human
The person who knows the answer. Gets one specific question and replies when they can; the run waits.
Quotes API Tool
The tool the agent needs the answer for: it creates and sends a quote.
Done Input / output
Confirmation that the task is complete, such as 'quote sent'.
Task → Agent
The task goes to the agent.Send Acme their renewal quote.
Agent → Account ownerquestion
The agent pauses and asks for the one thing it is missing, with the choices and why it needs it.{"tool": "ask_human", "question": "Which plan should Acme's renewal quote use?", "choices": ["Team", "Enterprise"], "why": "Their contract allows either."}
Account owner → Agentanswer
The person's answer comes back as the result of the ask_human call, and the run resumes.Team, 40 seats. They are not ready for Enterprise yet.
Agent → Quotes API
With the answer, the agent makes the call it was missing information for.{"tool": "create_quote", "arguments": {"customer": "Acme", "plan": "Team", "seats": 40}}
Quotes API → Done
The tool's result is the outcome of the run.{"status": "sent", "quote": "Q-5521", "to": "billing@acme.example"}

Act, then review

The agent acts at once against a saved checkpoint; a person reviews afterwards, and a rejection restores the saved state.

Human-in-the-Loop: Act, then review diagramTask, Agent, Checkpoint, CRM API, Reviewer. Step captions follow the diagram.TaskAgentCheckpointCRM APIReviewerHuman-in-the-Loop: Act, then review diagramTask, Agent, Checkpoint, CRM API, Reviewer. Step captions follow the diagram.TaskAgentCheckpointCRM APIReviewer

All steps

Rejected, then rolled back

Approved after the fact

Parts of this pattern
Task Input / output
The task that starts the run, such as 'Reassign departing rep Sam's 120 contacts to Dana'.
Agent Agent
Plans the change, saves a checkpoint first, then makes the change at once without waiting for anyone.
Checkpoint Memory
The values of every record before the change, kept until the review window ends, so the change can be undone.
CRM API Tool
The real system the agent changes; the same API writes the old values back on a rollback.
Reviewer Human
A person who reviews the change after it has happened, and approves it or rejects it to roll it back.
Task → Agent
The task goes to the agent.Sam is leaving. Reassign Sam's 120 contacts to Dana.
Agent → Checkpointsnapshot
Before anything changes, the agent saves the current values of everything the change will touch.checkpoint cp-311: 120 contacts, owner = Sam (ids 4001 to 4120)
Checkpoint → CRM APIapply
Once the snapshot is saved, the change is applied at once; on a rollback, the same path writes the saved values back.{"tool": "bulk_update", "arguments": {"ids": "4001-4120", "owner": "Dana"}} (on rollback: owner = Sam, from cp-311)
CRM API → Reviewerreview
The change is already live; the reviewer gets the before-and-after diff and the checkpoint id after the fact.Review cp-311: 120 contacts moved from Sam to Dana (bulk_update ok). Approve or reject within 24 hours.
Reviewer → Checkpointreject
A rejection, with a reason, goes back to the checkpoint, whose saved values are then written back.{"kind": "reject", "checkpoint": "cp-311", "reason": "The 12 enterprise accounts go to Priya, not Dana."}

← → neighboursesc atlas

Safety & Oversight

Human-in-the-Loop

Also known as Approval gate · Interrupt/resume · HITL

Pause before high-stakes or irreversible actions so a person can approve, edit or reject; then resume.

No single inventor · see source

No single inventor: human oversight of automated decisions long predates LLM agents. OpenAI's 2025 guide ('Plan for human intervention') and LangGraph's interrupts made the approve, edit or reject pause standard for agents.

Measured over one run

Approval gate

Nodes
6. Boxes in the diagram: models, tools, stores, people and plain code.
LLM calls
2. Model invocations in the first scenario: each time a model or agent node starts thinking, plus each branch it decides.
Tool calls
1. Messages sent to a tool node in the first scenario.
Steps
4. Steps in the first scenario, with repeated steps counted every time they run.
Scenarios
3. Distinct example runs you can switch between on the diagram.
Loop
no. Whether the flow cycles back on itself until a condition is met.
Parallel
no. Whether two or more messages are in flight at the same time.
Branch
yes. Whether the flow takes one path out of several, chosen at run time.
Human in the loop
yes. Whether a person appears as a node, to approve, correct or answer.
Autonomy
low. How much the model decides the control flow itself, from fixed code paths (low) to open-ended (high).
Cost
low. Relative token spend per request.
Latency
high. Relative wall-clock time per request.

Agent asks

Nodes
5. Boxes in the diagram: models, tools, stores, people and plain code.
LLM calls
4. Model invocations in the first scenario: each time a model or agent node starts thinking, plus each branch it decides.
Tool calls
1. Messages sent to a tool node in the first scenario.
Steps
4. Steps in the first scenario, with repeated steps counted every time they run.
Scenarios
2. Distinct example runs you can switch between on the diagram.
Loop
yes. Whether the flow cycles back on itself until a condition is met.
Parallel
no. Whether two or more messages are in flight at the same time.
Branch
yes. Whether the flow takes one path out of several, chosen at run time.
Human in the loop
yes. Whether a person appears as a node, to approve, correct or answer.
Autonomy
low. How much the model decides the control flow itself, from fixed code paths (low) to open-ended (high).
Cost
low. Relative token spend per request.
Latency
high. Relative wall-clock time per request.

Act, then review

Nodes
5. Boxes in the diagram: models, tools, stores, people and plain code.
LLM calls
1. Model invocations in the first scenario: each time a model or agent node starts thinking, plus each branch it decides.
Tool calls
2. Messages sent to a tool node in the first scenario.
Steps
4. Steps in the first scenario, with repeated steps counted every time they run.
Scenarios
2. Distinct example runs you can switch between on the diagram.
Loop
no. Whether the flow cycles back on itself until a condition is met.
Parallel
no. Whether two or more messages are in flight at the same time.
Branch
no. Whether the flow takes one path out of several, chosen at run time.
Human in the loop
yes. Whether a person appears as a node, to approve, correct or answer.
Autonomy
low. How much the model decides the control flow itself, from fixed code paths (low) to open-ended (high).
Cost
low. Relative token spend per request.
Latency
high. Relative wall-clock time per request.

Counted from the first scenario's timeline when the site is built.

Problem

An agent that can call real tools can also do real damage: issue a refund, email a customer, delete a table, deploy to production. Models misread instructions and get fooled by injected text, and some actions cannot be undone. Full autonomy is a bad fit when the cost of one wrong call is high.

How it works

Put a person at the points that matter. The agent works normally until it proposes an action. A risk check then looks at the call. This can be a rule (any refund over $100), a tool-level flag (all destructive tools) or a classifier. Low-risk calls go straight to the tool. High-risk calls go to the reviewer instead.

At that point the run pauses. The agent’s state is checkpointed, which can be hours or days of waiting, and the reviewer sees the exact action, the agent’s rationale and its effects. They choose one of three outcomes: approve, so the tool runs; edit, so it runs with corrected arguments; or reject with a reason, which goes back to the agent so it can replan. The run then resumes from the checkpoint and the result is returned.

Frameworks expose this as an interrupt: a call that stops the graph, saves state, and later resumes with the human’s answer as its return value.

The same pause serves other purposes, shown in the variant tabs. In Agent asks, the agent itself stops to ask a person for information it is missing, with no risky action involved. In Act, then review, the agent acts at once against a saved checkpoint, and a person reviews afterwards and can roll the change back. That only suits changes that can be undone.

When to use

  • Actions are irreversible, costly or customer-visible.
  • The agent is new, or its error rate is not yet known.
  • Regulation or policy requires a human decision.
  • Failing to meet a confidence threshold, or exceeding several retries, should hand the case to a person.

When to avoid

  • Actions are cheap, reversible and frequent. Constant approval trains reviewers to click yes.
  • Nobody can respond within the time the task allows.
  • The reviewer lacks the context to judge, so a gate adds delay without safety.

Trade-offs

Safety costs latency: a run can sit for hours waiting for a reply. You also need checkpointing so a paused run survives restarts, plus a review interface. The gain is bounded blast radius for exactly the actions you choose.

Failure modes

  • Approval fatigue: too many requests make people rubber-stamp them. Gate only genuinely risky calls and show the key facts first.
  • Thin context: the reviewer sees a raw tool call and cannot judge it. Show the reason, the effect and whether it is reversible.
  • Lost state: the run cannot resume after a long wait. Persist state outside the process.
  • Gate bypass: a risky call takes a path the check does not cover. Enforce the gate at the tool layer, not just in the prompt.

Build it with your AI

Approval gate

Paste this into Claude Code, Cursor, ChatGPT or any AI coding assistant.

Implement the Human-in-the-Loop pattern in this codebase, using the LLM client and language we already use.

## Goal

Pause an agent before any high-risk tool call so a person can approve, edit or reject it, then resume the run from exactly where it stopped.

## How it works

1. When the agent proposes a tool call, classify it with the Risk classifier prompt: high if it is irreversible, moves money, messages people outside the company or changes production data; otherwise low. Rules may mark some tools as always high.
2. Run low-risk calls at once and return the result to the agent.
3. For a high-risk call, save the run's state to durable storage, then send the reviewer the Approval request message with the tool, the arguments, the rationale and the effect.
4. Wait for a reply of approve, edit (with changed arguments) or reject (with a reason). Nothing runs while the run waits.
5. On approve, run the call as proposed; on edit, run it with the edited arguments; on reject, return the reason to the agent as the tool result so it replans.

## Prompts

### Approval request message

```markdown
**{{agent_name}} wants to take an action that needs your approval.**

Action: `{{tool_name}}`
Arguments: {{arguments}}
Why: {{rationale}}
Effect if approved: {{effect}}
Reversible: {{reversible}}

Reply with one of: approve, edit (give the changed arguments), reject (give a reason).
```

### Risk classifier prompt

```markdown
Classify the risk of this proposed tool call.

<tool>{{tool_name}}</tool>
<arguments>{{arguments}}</arguments>

High risk means it is irreversible, moves money, sends messages to people
outside the company, or changes production data. Otherwise it is low.
Respond in JSON: {"risk": "high" | "low", "reason": "one sentence"}
```

## Stop rule

The run ends when the agent finishes its task. A pending approval expires after 24 hours (configurable); on expiry, treat it as a rejection with the reason 'no reviewer response' and tell the requester.

## Requirements

- Persist the paused state so a restart or a deploy cannot lose a pending approval, and resume each run exactly once.
- If the risk classifier's reply is not valid JSON, retry once, then treat the call as high risk.
- Validate edited arguments against the tool's schema before running them; if they fail, send the edit back to the reviewer.
- Record who approved, edited or rejected each call, when, and the arguments that finally ran, as an audit log.
- Add a test with a stub agent that proposes a $480 refund and a fake reviewer that rejects it; assert the refund tool was never called and the agent's next input contains the rejection reason.

Agent asks

Paste this into Claude Code, Cursor, ChatGPT or any AI coding assistant.

Implement the Human-in-the-Loop pattern (Agent asks variant) in this codebase, using the LLM client and language we already use.

## Goal

Let an agent pause mid-task to ask a person for information it cannot find or safely guess, and resume with the answer as if it were a tool result.

## How it works

1. Give the agent an ask_human tool that takes a question and optional choices, and tell it when to use it with the Agent instructions for asking.
2. When the agent calls ask_human, save the run's state to durable storage and send the question to the person through your usual channel (chat, email or a task inbox).
3. When the person answers, load the saved state and return the answer to the agent as the result of the ask_human call.
4. Let the agent continue its loop, calling its other tools as normal.

## Prompts

### Agent instructions for asking

```markdown
You work for {{team}}. You have tools, including ask_human.

Call ask_human only when you are missing information that you cannot
find with your other tools and that you must not guess, such as a
customer's choice, a price or a date.
Ask one specific question, give the choices if there are any, and say
in one line why you need it. Then wait for the answer.
Never use ask_human to get approval for work you can do yourself.
```

## Stop rule

The run ends when the agent finishes its task. An unanswered question expires after 24 hours (configurable); the run then ends with a status saying it is waiting on a person, and the question stays visible.

## Requirements

- Cap questions per run (default 3). On a 4th ask_human call, send nothing to the person; return a tool error telling the agent to continue on a stated assumption, and to state that assumption in its result.
- Require a single, specific question with the reason it is needed; reject an empty question as a tool error.
- Persist the paused state so a restart cannot lose a pending question, and resume each run exactly once.
- Log every question, its answer, who answered and how long it took.
- Add a test with a stub agent that calls ask_human once and then create_quote, and a fake person who answers 'Team'; assert the run paused once and create_quote received plan Team.

Act, then review

Paste this into Claude Code, Cursor, ChatGPT or any AI coding assistant.

Implement the Human-in-the-Loop pattern (Act, then review variant) in this codebase, using the LLM client and language we already use.

## Goal

Let an agent make reversible changes without waiting, by saving a checkpoint before each change and letting a person review afterwards and roll it back.

## How it works

1. Allow this mode only for tools whose effects can be undone; send irreversible calls through an approval gate instead.
2. Before each change, save a checkpoint: the current values of every record the call will touch, with a checkpoint id.
3. Run the call at once, then put a review item in the reviewer's queue with the checkpoint id and a before-and-after diff.
4. On approve, mark the review done and keep the checkpoint until it expires.
5. On reject, write the saved values back through the same tool, check that each record now matches the checkpoint, and tell the agent and the requester.

## Prompts

### Approval request message

```markdown
**{{agent_name}} wants to take an action that needs your approval.**

Action: `{{tool_name}}`
Arguments: {{arguments}}
Why: {{rationale}}
Effect if approved: {{effect}}
Reversible: {{reversible}}

Reply with one of: approve, edit (give the changed arguments), reject (give a reason).
```

### Risk classifier prompt

```markdown
Classify the risk of this proposed tool call.

<tool>{{tool_name}}</tool>
<arguments>{{arguments}}</arguments>

High risk means it is irreversible, moves money, sends messages to people
outside the company, or changes production data. Otherwise it is low.
Respond in JSON: {"risk": "high" | "low", "reason": "one sentence"}
```

## Stop rule

A change is final when it is approved or its review window ends (default 24 hours), when the checkpoint is discarded. A rejection after that window must be fixed by a new change, not a rollback.

## Requirements

- Make the review window a parameter (default 24 hours).
- Before restoring a record, check it has not changed since the agent's change; if it has, do not overwrite it, and flag it for the reviewer.
- Make the restore idempotent, so retrying it after a failure is safe.
- Log every change, review decision and restore with the checkpoint id.
- Add a test with a stub agent that reassigns 3 contacts and a fake reviewer that rejects the change; assert all 3 contacts are back with their original owner and the restore ran once.

Prompt templates

Approval gate · Act, then review

Approval request message
**{{agent_name}} wants to take an action that needs your approval.**
Action: `{{tool_name}}`
Arguments: {{arguments}}
Why: {{rationale}}
Effect if approved: {{effect}}
Reversible: {{reversible}}
Reply with one of: approve, edit (give the changed arguments), reject (give a reason).
Risk classifier prompt
Classify the risk of this proposed tool call.
<tool>{{tool_name}}</tool>
<arguments>{{arguments}}</arguments>
High risk means it is irreversible, moves money, sends messages to people
outside the company, or changes production data. Otherwise it is low.
Respond in JSON: {"risk": "high" | "low", "reason": "one sentence"}

Agent asks

Agent instructions for asking
You work for {{team}}. You have tools, including ask_human.
Call ask_human only when you are missing information that you cannot
find with your other tools and that you must not guess, such as a
customer's choice, a price or a date.
Ask one specific question, give the choices if there are any, and say
in one line why you need it. Then wait for the answer.
Never use ask_human to get approval for work you can do yourself.

Related patterns

Composes well with:

Sources

  1. A practical guide to building agents (OpenAI, 2025)
  2. Interrupts (LangGraph docs)

From the Agentic Atlas by Joran Kikke · Edit on GitHub · Suggest a correction