Library
7 min read

Human in the loop and durable agents

An insurance claims agent reads a claim, pulls the policy, checks the hospital bill, and decides the payout is Rs 1.8 lakh. Company rule: anything over Rs 1 lakh needs an adjuster to approve. The adjuster is on leave until Monday. It is Friday evening.

The agent cannot hold a Python process open all weekend waiting for a click, and if the server is redeployed on Saturday, the run must not start again from the beginning and re-read everything. Two capabilities make this work: pausing for a person and durable execution. Every agent that touches money, customers or production eventually needs both.

Your answer

An agent run pauses for approval and the approver answers three days later. What must have been saved, and where, for the run to continue exactly where it stopped?

Where to put a person#

Not every step needs a human. Put approval where an action is expensive to undo or outward facing:

  • money moves (refunds, payouts, purchases)
  • something is sent to a customer or published
  • data is deleted or permissions change
  • the agent is uncertain, or a check failed twice

Everything else runs on its own. An agent that asks for approval on every step trains people to click "approve" without reading, which is worse than no approval at all.

Pause, save, resume#

1/6 Steps so far: read the claim, fetched the policy, checked the bill. All of that work is already in the state.

The same thing in code#

In LangGraph, a node calls interrupt() to pause. With a checkpointer, the state is saved, and a later call with Command(resume=...) and the same thread id continues from that node.

python
from langgraph.checkpoint.postgres import PostgresSaver   # durable, survives restarts
from langgraph.types import Command, interrupt
 
 
def approve_payout(state: ClaimState):
    if state["payout"] <= 100_000:
        return {"approved_amount": state["payout"]}
    decision = interrupt({                      # pauses the run here
        "action": "payout",
        "amount": state["payout"],
        "reasons": state["reasons"],
    })
    return {"approved_amount": decision["amount"] if decision["approved"] else 0}
 
 
graph = builder.compile(checkpointer=checkpointer)  # e.g. a PostgresSaver
config = {"configurable": {"thread_id": f"claim-{claim_id}"}}
 
graph.invoke({"claim_id": claim_id}, config)  # runs until the interrupt, then returns
 
# ...days later, from the approval API handler:
graph.invoke(Command(resume={"approved": True, "amount": 150_000}), config)

Other stacks have the same shape. The Anthropic SDK's tool runner lets you gate a tool inside its own function or inspect pending tool calls each turn. Hosted agent services expose per tool permission policies such as "always ask". The idea never changes: stop before the risky call, save everything, ask, resume.

Durable execution, beyond approvals#

Pausing for a person is one case of a bigger requirement. An agent run that lasts minutes to hours will meet deploys, crashed workers, rate limits and timeouts. Durable execution means the run's progress is recorded step by step, so any interruption resumes from the last completed step rather than from zero.

Two common ways to get it:

  • An agent framework with checkpoints, such as LangGraph with a database backed checkpointer.
  • A general durable workflow engine, such as Temporal, where each tool call or model call is an activity whose result is recorded, and the workflow replays from history after a failure. Teams that already run one for payments or orders often put agents on it too.

Two rules make either approach safe:

Make side effects idempotent. A step may run twice: once before a crash and once after resume. Refunds, emails and payouts carry an idempotency key, so the second run is a no-op.

Do not resume on stale facts. If an approval arrives three days later, re-check what might have changed (is the claim still open, is the order already refunded) before acting.

ChoiceWhat you gainWhat you payPick it when
Approve every stepMaximum control.Approval fatigue: people stop reading and click yes.Almost never. Only while first trusting a new agent.
Approve risky actions onlyPeople look carefully at the few decisions that matter.Deciding and maintaining what counts as risky.The default for agents that touch money, customers or production.
Checkpointed agent frameworkPauses and resumption built into the agent library.State storage to run, and checkpoints tied to that framework.Most agent products with approvals or long runs.
Durable workflow engineBattle tested retries, timers and replay for any long process.Another system to run and learn, with its own rules about determinism.Teams already running one, and agents inside critical business processes.
Good approval design or not?
The approval screen says "The agent wants to call issue_refund. Approve?"
Approvals expire after 48 hours and the run is cancelled cleanly
After resume, the agent re-sends the email it sent before the pause
The adjuster can change the payout amount before approving

Checkpoint#

Checkpoint

1. What makes it possible to resume an agent run three days after it paused?

2. Why does a payout step need an idempotency key in a durable agent?

3. Where should an agent ask for human approval?

Worth memorising
  • Approve the irreversible and the outward facing. Not every step.
  • Pause = save state, stop, ask. Resume = load state, continue.
  • Side effects get idempotency keys. Steps can run twice.
  • Re-check facts on resume, and expire approvals that sit too long.
Say this in 60 seconds

Agents that touch money, customers or production need a person to approve risky actions, and runs that last longer than a request need durable execution. I put approval only where actions are expensive to undo or outward facing, and show the approver the exact action with its reasons, allowing edits. When the agent reaches that step it saves its full state under a run id and stops, with no process waiting. When the decision arrives, possibly days later, any worker loads the state and continues from that step. LangGraph does this with interrupt and a checkpointer, and workflow engines like Temporal do it for any long process. Because steps can run twice, every side effect carries an idempotency key, and I re-check facts that may have changed before acting on a late approval.