DataAIHub
DataAIHubNews · Research · Tools · Learning
AI Agents

Human in the Loop Guide

How to design approval workflows, interrupt/resume semantics, and human review gates for production AI agents - so irreversible actions never run without oversight.

50 min readIntermediateUpdated Jul 16, 2026

Quick Summary

Human-in-the-loop pauses an agent before high-risk actions so a person can approve, edit, or reject them before execution continues.

One Analogy

Like a merge request that cannot deploy until a reviewer clicks approve - the pipeline waits with full context, then resumes from the same checkpoint.

Engineering Rule

Any irreversible or externally visible side effect needs an interrupt gate until measured success rate and audit requirements justify automation.

TL;DR

  • Human-in-the-loop (HITL) pauses agent execution at defined interrupt points so a person can approve, edit, or reject the next action before it runs.

  • Interrupt/resume requires durable state - the agent must checkpoint mid-run, survive process restarts, and continue with the human's decision attached.

  • Gate write actions, not every token - read-only research and drafting can stay autonomous; refunds, emails, deploys, and DB writes should interrupt.

  • Approval UX is part of the system - timeouts, escalation, partial edits, and audit logs matter as much as the graph node that pauses.

  • Progressive autonomy is the production path - start with broad gates, measure false positives and latency, then automate only what evals and risk tolerate.

Why This Matters

Agents that only generate text are low risk. Agents that call tools with side effects are not. A mistaken refund, a wrong customer email, or a bad production config change is a support incident or a security event - not a mildly wrong chatbot reply.

HITL is how teams ship useful agents before the model is perfect. You keep the speed of autonomous planning and tool selection, then insert a human gate where mistakes are expensive. That pattern shows up in support desks (approve refund), finance ops (approve wire), SRE (approve runbook step), and coding agents (approve PR merge).

Without interrupt/resume, "ask a human" usually means restarting the whole run after the person replies. That wastes tokens, loses tool results, and breaks multi-step context. Production HITL is a control-flow feature of the orchestrator, not a prompt that says "please wait for approval."

The Problem Human-in-the-Loop Solves

Fully autonomous loops fail in production when:

  1. Actions are irreversible - money moves, emails send, tickets close, credentials rotate.

  2. Policy is nuanced - "refund under $50" may be auto; edge cases need judgment the model lacks.

  3. Trust is not yet earned - eval success is 80%, not 99%. You still want value from the agent without unbounded risk.

  4. Compliance requires a named approver - regulated workflows need a human signature in the audit trail.

  5. Context is ambiguous - the agent proposes a plan, but only a domain expert can choose among valid options.

Prompting the model to "be careful" does not create a hard stop. Guardrails can block invalid outputs, but they cannot apply organizational judgment. HITL is the missing control: execution waits until a human decision arrives.

How We Got Here

HITL for agents is not a new invention - it is the agent-era form of maker-checker and approval workflows that ops and finance already use:

Diagram: Evolution of human-in-the-loop

flowchart LR
    A[Manual tickets] --> B[BPMN / saga approvals]
    B --> C[Chatbots ask clarifying Qs]
    C --> D[Tool-calling agents]
    D --> E[Interrupt + durable resume]
    E --> F[Progressive autonomy]

HITL for agents reuses maker-checker and workflow approvals, then adds interrupt/resume semantics.

Era Pattern Gap for agents
Manual / ticket queues Human does the whole task No LLM leverage
BPMN / workflow approvals Durable pause before writes Steps were hand-authored, not model-proposed
Chatbot "are you sure?" Conversational confirm Not a hard stop; no checkpoint
Tool-calling agents Model proposes side effects Needs runtime interrupt before execute
Production HITL LangGraph interrupt + checkpointer Paired with durable execution and evals

Public guidance that shaped the pattern: LangGraph Human-in-the-Loop, OpenAI Agents SDK HITL, and Anthropic — Building effective agents (prefer workflows and human gates over unbounded autonomy).

What Is Human-in-the-Loop?

Human-in-the-loop for agents is an orchestration pattern where the runtime:

  1. Runs the agent until a configured interrupt condition (tool, risk score, amount threshold, or explicit node).
  2. Persists checkpointed state (messages, tool args, plan).
  3. Surfaces a review payload to a human (UI, Slack, ticket, API).
  4. Accepts a decision: approve, reject, or edit.
  5. Resumes the graph from the same point with that decision as input.

This is distinct from:

Pattern Who decides When
Human-on-the-loop Human monitors; intervenes optionally After or during autonomous run
Human-in-the-loop Human must decide at a gate Before the gated action executes
Human-out-of-the-loop No human in the path Full automation

Most production agent systems use a mix: autonomous for reads and drafts, HITL for writes above a threshold, and human-on-the-loop dashboards for monitoring.

Engineering Insight

💡 Key Idea - HITL is not "chat with the user mid-task." It is a suspendable workflow with typed decisions, timeouts, and durable checkpoints - closer to a saga or approval BPMN step than to another LLM turn.

How Human-in-the-Loop Works

Frameworks implement HITL as interrupt points in a state machine. LangGraph exposes interrupt() / interrupt_before so a node pauses before (or after) tools run. OpenAI Agents SDK and similar runtimes support approval callbacks for sensitive tools. The conceptual flow is the same across stacks.

Diagram: HITL interrupt control flow

flowchart TD
    A[User goal] --> B[Agent reasons / selects tool]
    B --> C{Action gated?}
    C -->|No| D[Execute tool]
    D --> E[Observe result]
    E --> B
    C -->|Yes| F[Checkpoint state]
    F --> G[Notify human reviewer]
    G --> H{Decision}
    H -->|Approve| I[Execute tool]
    H -->|Edit args| J[Apply edits then execute]
    H -->|Reject| K[Record rejection / replan]
    I --> E
    J --> E
    K --> B

Safe steps run autonomously; gated writes checkpoint, wait for a human decision, then resume.

What the diagram shows: the agent loop is unchanged for safe steps. On a gated action, state is frozen, a human decides, and the loop resumes with that decision - without replaying prior tool calls.

Interrupt lifecycle

Diagram: HITL interrupt lifecycle

stateDiagram-v2
    [*] --> Running
    Running --> Running: safe tool / observe
    Running --> Interrupted: gated write
    Interrupted --> Running: approve / edit
    Interrupted --> Replan: reject
    Interrupted --> TimedOut: deadline
    Replan --> Running
    TimedOut --> Escalated
    TimedOut --> Rejected
    Running --> Done: final answer
    Done --> [*]

Interrupted must be a durable status so approvals can arrive after deploys or hours of delay.

Lifecycle notes: Interrupted must be a durable status (not an in-memory flag). Resume injects the decision and continues from the same edge - see durable execution. Timeouts need an explicit policy (reject, escalate, or rare auto-approve).

Interrupt payloads

A useful interrupt package includes:

  • Proposed action - tool name + validated arguments
  • Why gated - policy rule, risk score, amount, destination
  • Context summary - last N messages, relevant tool results, customer ID
  • Alternatives - reject, edit fields, escalate to another role
  • Deadline - timeout behavior if no response

Resume semantics

On resume, the orchestrator loads the checkpoint, injects the human decision into state, and continues from the interrupt edge. Idempotency matters: if the process crashed after approval but before the tool finished, resume must not double-execute the side effect. Pair HITL with durable execution patterns (idempotency keys, exactly-once side-effect wrappers).

Architecture

Diagram: HITL system architecture

flowchart TB
    User[User / API] --> Orch[Orchestrator / graph]
    Orch --> LLM[LLM policy]
    Orch --> Policy[Policy engine]
    Orch --> Tools[Tool registry]
    Orch --> CP[Checkpoint store]
    Policy -->|gate| Interrupt[Interrupt node]
    Interrupt --> Review[Review UI / Slack / ticket]
    Review --> Decision[Decision API + authz]
    Decision --> Orch
    Orch --> Audit[Audit log]
    Tools --> Ext[Payments, email, deploys]

Policy and humans dispose of model-proposed writes; the checkpoint store makes resume reliable.

Component Responsibility
Orchestrator Graph/loop with interrupt nodes and resume API
Checkpoint store Persist state across process restarts (Postgres, Redis)
Policy engine Decide which tools/amounts require approval
Review UI / channel Slack, web console, ticketing system
Decision API Accept approve / reject / edit with authz
Audit log Who approved what, when, with which payload
Timeout & escalation Auto-reject, auto-approve (rare), or escalate

Policy should live outside the LLM when possible: amount thresholds, allowlists of tools, environment flags (prod vs sandbox). The model proposes; policy and humans dispose. Frameworks that expose this cleanly include LangGraph and OpenAI Agents SDK - compare stacks in Best AI Agent Frameworks.

Step-by-Step Flow

Consider a support agent: "Customer C-8842 wants a refund for order O-991 ($240)."

  1. Ingest goal - Orchestrator starts a thread with customer context and tools: search_orders, issue_refund, send_email.

  2. Read phase (autonomous) - Agent calls search_orders, confirms charge and eligibility. No interrupt.

  3. Propose write - Agent prepares issue_refund(order_id="O-991", amount=240, reason="duplicate charge").

  4. Policy check - Amount > $50 → interrupt. State checkpointed under thread_id.

  5. Notify reviewer - Slack message to #refunds with order summary, agent rationale, Approve / Edit / Reject buttons.

  6. Human decides - Agent edits amount to $120 (partial refund) and approves.

  7. Resume - Orchestrator loads checkpoint, applies edited args, executes refund once with idempotency key.

  8. Continue - Agent may draft a confirmation email; email send may be a second gate or auto if policy allows.

  9. Audit - Store: proposed args, edited args, approver identity, timestamps, tool result.

  10. Metrics - Increment approval latency, edit rate, rejection rate for later policy tuning.

Real Production Example

A LangGraph-style interrupt/resume pattern for gated refunds:

from typing import Annotated, Literal, TypedDict
from langgraph.checkpoint.memory import MemorySaver
from langgraph.graph import END, StateGraph
from langgraph.types import Command, interrupt
from langchain_core.tools import tool
from langchain_openai import ChatOpenAI

class AgentState(TypedDict):
    messages: list
    refund_result: str | None

@tool
def search_orders(customer_id: str) -> str:
    """Look up recent orders for a customer."""
    return '[{"id": "O-991", "total": 240, "status": "paid"}]'

def should_gate_refund(amount: float) -> bool:
    return amount >= 50.0

def agent_node(state: AgentState):
    """LLM decides next action; omitted for brevity — returns a structured proposal."""
    # In production: bind_tools + parse tool_calls from the model.
    return {
        "messages": state["messages"]
        + [
            {
                "role": "assistant",
                "content": "Proposing refund",
                "tool_calls": [
                    {
                        "name": "issue_refund",
                        "args": {
                            "order_id": "O-991",
                            "amount": 240.0,
                            "reason": "duplicate charge",
                        },
                    }
                ],
            }
        ]
    }

def gated_refund_node(state: AgentState):
    last = state["messages"][-1]
    args = last["tool_calls"][0]["args"]

    if not should_gate_refund(args["amount"]):
        result = payments.refund(**args, idempotency_key=f"refund:{args['order_id']}")
        return {"refund_result": result}

    # Pause for human. Payload is what the UI renders.
    decision = interrupt(
        {
            "type": "approve_refund",
            "proposed": args,
            "policy": "amount >= 50 requires human approval",
        }
    )
    # decision shape: {"action": "approve"|"reject"|"edit", "args": {...}}

    if decision["action"] == "reject":
        return {"refund_result": "REJECTED_BY_HUMAN"}

    final_args = decision.get("args", args)
    result = payments.refund(
        **final_args,
        idempotency_key=f"refund:{final_args['order_id']}",
    )
    return {"refund_result": result}

def route_after_agent(state: AgentState) -> Literal["gated_refund", "end"]:
    last = state["messages"][-1]
    if last.get("tool_calls"):
        return "gated_refund"
    return "end"

builder = StateGraph(AgentState)
builder.add_node("agent", agent_node)
builder.add_node("gated_refund", gated_refund_node)
builder.set_entry_point("agent")
builder.add_conditional_edges(
    "agent",
    route_after_agent,
    {"gated_refund": "gated_refund", "end": END},
)
builder.add_edge("gated_refund", END)

# Use PostgresSaver / SqliteSaver in production — MemorySaver is for demos.
graph = builder.compile(checkpointer=MemorySaver())

config = {"configurable": {"thread_id": "support-C-8842"}}

# First invoke hits interrupt and returns an interrupt payload.
paused = graph.invoke(
    {"messages": [{"role": "user", "content": "Refund order O-991"}], "refund_result": None},
    config=config,
)

# Later, after UI collects a decision:
resumed = graph.invoke(
    Command(resume={"action": "edit", "args": {"order_id": "O-991", "amount": 120.0, "reason": "partial"}}),
    config=config,
)

Key production details: policy outside the model, interrupt payload for the UI, idempotency on the side effect, and a durable checkpointer keyed by thread_id.

Design Decisions

Decision Option A Option B When to choose
Gate placement Before every write tool Threshold / risk-based Thresholds for scale; every write early in rollout
Decision types Binary approve/reject Approve / reject / edit Prefer edit when args are often almost right
Reviewer channel In-app console Slack / email / ticket Match where ops already works
Timeout Auto-reject Escalate / hold forever Auto-reject for money moves; escalate for customer SLAs
Who can approve Same user Separate role Separate role for compliance and fraud control
Autonomy growth Static policy Policy tuned from metrics Raise thresholds when edit/reject rates drop

Comparisons

HITL vs Guardrails

Dimension HITL Guardrails
Who decides Named human (or role) Programmatic rules / models
Latency Minutes–hours typical Milliseconds–seconds
Strength Judgment, fraud edge cases, compliance sign-off Schema, PII, allowlists, rate limits
Weakness Reviewer fatigue; not scalable alone Cannot apply org nuance
Compose Gate after guardrails pass Block invalid proposals before humans see them

Use guardrails to shrink the interrupt queue; use HITL for irreversible or gray-area actions. They are complementary, not substitutes.

HITL vs Human-on-the-Loop

Dimension Human-in-the-Loop Human-on-the-Loop
Default path Blocks until decision Runs autonomously
Intervention Required at the gate Optional / exception-driven
Best for Writes, money, external messages Monitoring dashboards, low-risk drafts
UX Explicit approve/edit/reject Alerts, kill switches, sampled review

Most production systems use both: HITL on privileged tools, human-on-the-loop observability for the rest of the agent loop.

HITL vs Agent Memory

Dimension HITL Agent Memory
Purpose Pause for a decision Persist knowledge across turns/sessions
Stores Checkpoint + decision record Facts, preferences, embeddings
Resumes Continues the same run with a decision Recalls context in future runs
Does not Replace long-term knowledge Create a hard stop before side effects

HITL needs durable checkpoints (durable execution); agent memory is a different layer. You often want both: remember customer prefs, still require approval on refunds.

Decision tree: when to gate

Decision tree: When to require human approval

flowchart TD
    A[Proposed tool call] --> B{Side effect irreversible or external?}
    B -->|No| C[Auto-execute]
    B -->|Yes| D{Covered by hard guardrails alone?}
    D -->|Yes| C
    D -->|No| E{Volume too high for review?}
    E -->|Yes| F[Raise auto-approve band or redesign]
    E -->|No| G[HITL interrupt + durable resume]

Gate irreversible writes; keep reads autonomous; fix policy before drowning reviewers.

⚠ Common Mistakes

  1. HITL as a system-prompt instruction - "Ask the user before refunding" is not a hard stop. The runtime must block tool execution.

  2. No durable checkpoint - Approvals arrive minutes later; the process is gone. Pair with durable execution.

  3. Interrupting every step - Reviewers drown; they rubber-stamp. Gate high-risk actions only.

  4. Missing edit path - Force reject/re-run for a typo in amount. Edits save time and tokens.

  5. No timeout policy - Threads hang forever. Define reject, escalate, or notify on expiry.

  6. Ignoring double-execution - Resume after a network blip can fire the refund twice without idempotency keys.

  7. Approver = requester - Self-approval defeats the control for fraud and mistakes.

Where It Breaks Down

  • Latency-sensitive UX - Multi-minute approval kills chat-like product feel. Use async status ("waiting for review") and notify when done.

  • High volume - Thousands of gates per hour need auto-approve bands, batch review UIs, or stronger guardrails before human time.

  • Ambiguous proposals - Humans cannot decide from a bare tool JSON blob. Poor interrupt payloads cause wrong approvals.

  • Model gaming the gate - Agents may split one large refund into many small ones under the threshold. Cap cumulative risk per session.

  • Cross-agent handoffs - Ownership of who must approve gets unclear in multi-agent handoffs. Define approver role per action type, not per agent.

When NOT to Use Human-in-the-Loop

Skip or shrink HITL when:

  1. The action is read-only or trivially reversible - search, draft, internal summary with no external send.
  2. You already have hard programmatic blocks - schema validation and allowlists reject the bad cases; humans would only rubber-stamp.
  3. Volume makes review impossible - thousands of gates/hour without auto-approve bands; fix policy or tooling first.
  4. Latency UX cannot tolerate a pause - real-time voice or sub-second products; redesign the side effect out of the critical path.
  5. There is no durable resume path - without checkpoints, "ask a human" forces full reruns; fix durable execution before adding gates.
  6. Maker-checker is already satisfied elsewhere - a separate BPMN/Temporal approval owns the write; do not double-gate the same action.

Prefer progressive autonomy: broad gates early, then raise thresholds using edit/reject metrics - the path Anthropic and production LangGraph teams describe for earning trust.

Running in Production

Best Practice

Best Practices - Gate irreversible tools by policy, persist checkpoints, require role-separated approvers, log every decision, and measure approval latency and edit rate before widening autonomy.

Dimension Consideration
Scaling Checkpoint store and review queue are the bottlenecks. Horizontal workers + Postgres/Redis checkpointers.
Latency Agent pause time is dominated by humans (minutes–hours). Design async UX; do not hold HTTP requests open.
Cost Checkpoints are cheap vs. replaying full runs. Edits beat reject-and-retry for token cost.
Monitoring Track interrupt rate, time-to-decision, approve/edit/reject mix, timeout rate, post-approval tool errors.
Evaluation Replay traces with simulated approvals. Assert gated tools never execute without a decision record.
Security AuthZ on resume API, signed decision tokens, PII-minimized review payloads, full audit trail.

Important

Never treat a webhook or Slack button as authenticated by obscurity. Bind resume tokens to thread_id, reviewer identity, and expiry - and verify the proposed action hash so a forged resume cannot swap tool arguments.

Common Mistake

Building HITL without idempotent side effects. An approved refund that retries on resume will create duplicate payments. Wrap every gated write with an idempotency key derived from thread + action + args.

Frameworks & rankings

Concept guides

Learning path

If you understood this topic, read next:

Diagram: Learning path for HITL controls

flowchart LR
    A[Durable] --> B[HITL]
    B --> C[Handoffs]
    C --> D[Memory]
    D --> E[Guardrails]

Prerequisites: AI Agents · Tool Calling

Next topics: Durable Execution · Multi-Agent Handoffs · Guardrails

Key Takeaways

  • HITL is a suspendable control-plane gate, not a polite prompt asking the model to be careful.
  • Gate irreversible and externally visible writes; keep reads and drafts autonomous to avoid reviewer fatigue.
  • Interrupt/resume only works with durable checkpoints and idempotent side effects.
  • Pair with guardrails: programs shrink the queue; humans handle judgment and compliance.
  • Progressive autonomy: raise auto-approve thresholds only after edit/reject/timeout metrics justify it.
  • Clarify approver roles across multi-agent handoffs so specialists do not invent conflicting policies.
  • Compare orchestration stacks via Best AI Agent Frameworks and LangGraph vs CrewAI.

FAQs

Is human-in-the-loop the same as asking the user clarifying questions?

No. Clarifying questions are conversational turns. HITL is a hard interrupt before a side effect, with durable state and an explicit approve/reject/edit decision.

Do I need HITL if I have guardrails?

Yes for judgment and irreversible actions. Guardrails enforce rules; humans handle exceptions, fraud edge cases, and policy gray areas.

Which tools should require approval?

Start with anything that moves money, sends external messages, deletes data, changes IAM, or deploys. Expand or shrink based on eval risk and volume.

How do I avoid reviewer fatigue?

Raise auto-approve thresholds for low-risk bands, batch similar approvals, improve interrupt summaries, and fix root causes of frequent edits.

What happens if the reviewer never responds?

Define timeout: reject, escalate to a backup role, or (rarely) auto-approve within a tight policy. Never leave unbounded open threads without alerting.

Can the same user who triggered the agent approve the action?

Technically yes; for compliance-sensitive actions, require a different role (maker-checker).

How does HITL interact with streaming UIs?

Stream progress until the interrupt, then show a waiting state with the proposal. On resume, stream continuation. Do not block the original HTTP request for hours.

What should be in the interrupt payload?

Tool name, args, policy reason, short context, customer/account IDs, risk score, and links to source records - enough to decide without opening five other tools.

How do I test HITL in CI?

Simulate interrupt + resume with a fake decision API. Assert gated tools are not invoked before resume, and that edited args are what execute.

Does HITL work with multi-agent systems?

Yes. Put gates on tools or on handoff boundaries. Clarify which role owns approval so specialists do not each invent their own policy.

Is Slack a good approval channel?

It works for ops teams already in Slack. Harden with signed callbacks, role checks, and an audit mirror in your system of record.

How is this different from Temporal signals or BPMN approvals?

Same idea at the workflow layer. Agent HITL applies that pattern to LLM tool loops. Many teams combine Temporal/durable workflows with an LLM planner.

Can I auto-approve after N successful human approvals?

Yes as progressive autonomy - but reset or tighten policy when prompts, tools, or models change. Past approvals are not a permanent license.

What metrics prove HITL is working?

Low timeout rate, falling edit/reject rates over time, zero ungated writes in audit samples, and acceptable p50 time-to-decision for your SLA.

References

Further Reading

Next Topics

Learning Path

Continue Learning

Related Guides

Related Tools

ToolCategoryPurposeWebsiteBest For
LangGraph
FeaturedOpen SourceAPI
frameworksGraph-based orchestration runtime for long-running, stateful agents.langgraph.devMulti-agent orchestration
OpenAI Agents SDK
Open SourceAPI
frameworksOfficial OpenAI framework for tool-using agents with handoffs, guardrails, and tracing.openai.github.ioMulti-step agent workflows
CrewAI
NewOpen SourceAPI
frameworksMulti-agent framework with Crews, tasks, and event-driven Flows.crewai.comContent pipelines
AutoGen
Open SourceAPI
frameworksMicrosoft framework with AgentChat, Core, Extensions, and Studio for multi-agent systems.microsoft.github.ioConversational multi-agent apps
Semantic Kernel
Open SourceAPI
frameworksMicrosoft SDK for AI agents with plugins, connectors, and multi-language support.learn.microsoft.comEnterprise .NET apps

Related Rankings

Related Comparisons