Agentic AI · Module 15 of 41 · 10 concepts · about 17 min

Human-in-the-Loop Agents

When and how a person should be involved: approval checkpoints, user confirmation, escalation, review after the fact, taking over, and confidence thresholds; and how to decide which actions need a human at all.

At work: The control that lets a company give an agent real authority: the agent does the routine work, and a person sees the few decisions where a mistake would be costly.

What human-in-the-loop means

What it is

Human-in-the-loop (HITL) means a person takes part in an agent's work at defined points. There are three positions a person can hold:

  • In the loop: the agent cannot proceed without the person's decision. Approval before a refund.
  • On the loop: the agent acts, and a person monitors and can step in. A dashboard of live actions with a stop button.
  • After the loop: a person reviews a sample of finished work. A weekly audit of fifty cases.

Why it matters

Full autonomy and full supervision are both wrong for most systems.

  • With no person, a rare bad action goes through.
  • With a person on everything, the agent saves no time.
  • The skill is placing people only where their judgement changes the outcome, and using the cheaper positions elsewhere.

In the real world: A support team approves about one refund in forty by hand, monitors the rest on a dashboard, and audits a random fifty a week; reviewers actually read what they approve because it is rare.

Visual example

Positions

Cost per action falls from top to bottom. So does protection.

Three places a person can stand
positionthe personblocks the actionuse for
In the loopDecides before the actionYesCostly or irreversible actions
On the loopWatches and can interveneOnly if they act in timeModerate, reversible actions
After the loopReviews a sample laterNoRoutine actions; finding patterns

Adding approvals everywhere 'to be safe'. People who must approve a hundred routine actions a day stop reading them and click approve. That is worse than no approval, because it looks like a control.

Practise it: Design a Multi-Agent Data Analyst

Which actions need a person

What it is

Decide per action, using four questions:

  • Reversible? Can it be undone cheaply? A wallet credit can; a sent email or deleted record cannot.
  • Costly? How much money, data or reputation is at stake?
  • Verifiable by code? If code can fully check it (the amount is within the order total), a person adds little.
  • Novel or ambiguous? Is this a case the system has not handled before, or one where the facts conflict?

An action that is irreversible, costly, uncheckable or unusual needs a person. One that is reversible, cheap and checkable does not.

Why it matters

This turns 'be careful' into a table you can implement and defend.

  • It puts approval on the few actions that warrant it.
  • It makes the policy explicit, so it can be reviewed and changed.
  • Law and regulation add fixed cases: many jurisdictions require a human decision for certain financial, medical, legal and employment outcomes.

In the real world: A bank's policy table has six lines; the agent's approval logic is generated from it, and the compliance team reviews the table, not the code.

Visual example

Policy

The same agent sits at different levels for different actions.

When an action needs a human
actionreversiblestakesdecision
Look up an orderNothing to undoNoneAutomatic
Refund up to ₹500YesLowAutomatic, capped, audited
Refund above ₹500Yes, with effortMediumApproval
Send an email to a customerNoMediumUser confirms the draft
Delete account dataNoHighApproval, two people
Deploy to productionPartlyHighApproval after tests pass
Medical or legal adviceNoHighHuman decides; agent assists
Bank transferNoHighCustomer confirms, with a second factor

Gating by tool instead of by action. refund_item for ₹40 and for ₹40,000 are the same tool and very different risks. The rule must look at the arguments.

Approval checkpoints

What it is

An approval checkpoint is a fixed point where the run stops until someone authorised says yes. A well-built one has:

  • a trigger in code: a rule on the tool and its arguments, not the model's choice;
  • a clear request: what will happen, to whom, for how much, and why the agent proposes it;
  • the evidence: the order, the policy line, the customer's message;
  • three outcomes: approve, decline with a reason, or edit and approve;
  • a timeout with a defined default, usually 'do not act';
  • an audit record: who decided, when, and what they saw.

Why it matters

The reviewer's decision is only as good as what they are shown.

  • A request that says 'approve tool call refund_item?' teaches nobody anything.
  • One that shows the amount, the order and the policy line can be decided in ten seconds.
  • 'Edit and approve' saves a round trip when the proposal is nearly right.
  • The record matters later, when someone asks why the money moved.

In the real world: An approval screen that shows the order, the policy clause and the proposed amount side by side takes a supervisor eight seconds per case; the earlier one, which showed raw JSON, took two minutes and was routinely rubber-stamped.

Visual example

Approval

The trigger is code. The wait costs nothing. Every outcome has a path.

An approval checkpoint, end to end

  1. Model proposesrefund_item(order=88213, ₹1,800)
  2. Rule firesOver ₹500: needs approval
  3. PauseState saved; supervisor shown evidence
  4. DecisionApprove, decline or edit
  5. ResumeDecision returned as the tool result
APPROVAL_RULES = [
    ("refund_item",   lambda a: a["amount"] > 500,         "supervisor"),
    ("cancel_order",  lambda a: a.get("dispatched", False), "supervisor"),
    ("delete_account", lambda a: True,                      "two_supervisors"),
]

def needs_approval(tool: str, args: dict) -> str | None:
    """Decided by code from the tool AND its arguments."""
    for name, condition, approver in APPROVAL_RULES:
        if tool == name and condition(args):
            return approver
    return None

def approval_request(state, call) -> dict:
    return {
        "run_id": state.run_id,
        "action": f"Refund ₹{call.input['amount']} for order {call.input['order_id']}",
        "why": state.last_reasoning,                 # the agent's stated reason
        "evidence": state.evidence_for(call),        # order record, policy line, message
        "options": ["approve", "decline", "edit"],
        "expires_in_hours": 24,                      # after which: decline and tell the customer
    }

Letting the model decide when to ask for approval. An agent that has been talked into a large refund has also been talked out of asking. The trigger belongs in the executor.

User confirmation

What it is

User confirmation is a lighter check: the agent shows the user what it is about to do on their behalf, and the user says yes. It suits actions that are the user's own to authorise:

  • sending an email or message in their name;
  • booking, paying or cancelling something for them;
  • changing their own settings or data.

Show the exact thing: the full email, the precise amount and recipient, not a summary of it.

Why it matters

It differs from supervisor approval in who has the authority.

  • A customer can authorise spending their own money; they cannot authorise a refund from the company.
  • Confirming the exact content protects against the agent misunderstanding the request.
  • For anything involving money, the confirmation should be a real control in the interface, not a typed 'yes' the model interprets.

In the real world: A travel assistant shows the full itinerary and total before paying; a version that summarised it as 'your trip to Goa' once booked the wrong month, and the customer had confirmed.

def confirm_with_user(action: dict) -> dict:
    """Returns a structured card for the app to render. The 'yes' is a button, not a message."""
    return {
        "type": "confirmation",
        "title": "Book this delivery slot?",
        "details": {"Day": action["day"], "Time": action["slot"], "Fee": f"₹{action['fee']}"},
        "confirm_token": sign(action),        # the app sends this back; it cannot be forged in chat
    }

def execute_confirmed(token: str, user) -> str:
    action = verify(token, user)              # only the exact action shown can be executed
    return book_slot(**action, user=user)

Treating a chat message of 'yes' as confirmation. The model decides whether 'sure, I guess, but cheaper?' counts, and text pasted from a web page can also say yes. Use a signed token tied to the exact action shown.

Escalation and hand-over

What it is

Escalation is the agent passing a task to a person because it should not, or cannot, finish it. Triggers:

  • the request is outside its tools or rules;
  • it cannot verify what the customer says;
  • a limit was hit: steps, cost, repeated failure;
  • the customer asks for a person, or is clearly distressed;
  • the topic is sensitive: safety, legal threats, fraud.

A good hand-over gives the person a summary: what was asked, what was found, what was done, and what remains.

Why it matters

Knowing when to stop is part of doing the job well.

  • An agent that never escalates makes confident mistakes at the edges of its ability.
  • One that escalates everything is useless.
  • A hand-over with no summary makes the customer repeat themselves, which is the complaint people remember.
  • Escalation rate is a key health measure: too low is as worrying as too high.

In the real world: A telecom's agent hands over one conversation in five, with a four-line note; human handling time for those cases fell by a third because nobody re-asks the basics.

def hand_over(state, reason: str) -> dict:
    summary = llm_small(
        "Write a hand-over note for a human support agent, in four short lines:\n"
        "ASKED: what the customer wants\nFOUND: facts established, with ids\n"
        "DONE: actions already taken\nOPEN: what remains and why I stopped\n\n"
        + render(state.messages)
    )
    ticket = create_ticket(queue=queue_for(reason), summary=summary,
                           run_id=state.run_id, priority=priority_for(reason))
    state.status = "handed_over"
    return {"ticket": ticket, "message_to_customer":
            "I've passed this to a colleague with the full details, so you won't need to repeat anything."}

Making escalation feel like failure in the prompt ('only escalate as a last resort'). The agent then tries risky things to avoid it. Describe it as a correct outcome for the cases listed.

Practise it: Design an AI Agent for Customer Refunds, Design an AI Customer Support Agent

Confidence thresholds

What it is

A confidence threshold is a score below which the agent does not act alone. The score must come from something measurable:

  • Agreement: run the decision several times; if the runs disagree, confidence is low.
  • A verifier: a separate check scores whether the evidence supports the decision.
  • Retrieval strength: how well the best passage matched the question.
  • Rule coverage: did the case match a known policy exactly, or only loosely?
  • History: how often this kind of case has been wrong before.

Why it matters

It lets the system automate the clear cases and send the unclear ones to people.

  • The threshold is a dial: raise it and more goes to people, with fewer errors.
  • It should be set from data: plot error rate against score on past cases and choose the point that meets your target.
  • Without a real signal behind it, a threshold is theatre.

In the real world: An invoice system auto-approves when three runs agree on the extracted total and it matches the line items; the remaining 12 percent go to a clerk, and its error rate on auto-approved invoices is under one in a thousand.

def confident_decision(case, runs: int = 3, threshold: float = 0.67):
    """Decide several times; act only if the runs agree and the evidence supports it."""
    decisions = [decide(case) for _ in range(runs)]
    top = max(set(d.action for d in decisions), key=[d.action for d in decisions].count)
    agreement = sum(d.action == top for d in decisions) / runs
    supported = verify_against_evidence(top, case.evidence)       # separate check

    if agreement >= threshold and supported:
        return top
    return escalate(case, reason=f"agreement {agreement:.0%}, supported={supported}")

Asking the model 'how confident are you, from 0 to 100?' and thresholding on the answer. A model's stated confidence is poorly related to whether it is right, and it tends to say 90. Use agreement, a verifier, or measured history.

Human review and manual intervention

What it is

Two ways people stay involved without blocking each action:

  • Human review: people check finished work. A random sample for quality, plus every case flagged by a rule (large amounts, complaints, anything reversed).
  • Manual intervention: a person steps into a live run. They can pause it, edit its state (correct a wrong fact), take over the conversation, or stop it. A kill switch stops all runs of an agent at once.

Why it matters

These catch what approvals miss, and they are how the system improves.

  • Review finds patterns no single approval would: a rule being applied slightly wrongly a hundred times.
  • Reviewed cases become test cases, so a mistake found once stays fixed.
  • When something goes wrong at scale, the ability to stop everything in one action is worth more than any other control.

In the real world: When a pricing bug made an agent over-refund, the on-call engineer turned off the refund_item tool with a flag in under a minute; the agent kept answering questions and handed refund requests to people.

Visual example

Controls

The last row is the one to build before launch.

Ways a person can step in
controlwhat it doesused when
Pause a runStops at the next step; state keptSomething looks wrong in one case
Edit stateCorrects a fact, then resumesThe agent is working from a wrong value
Take overA person continues the conversationThe customer needs a human
Disable a toolOne capability is switched off everywhereOne action is misbehaving
Kill switchAll runs of the agent stopA serious, widespread fault

Having no way to stop a misbehaving agent short of a code deployment. A switch in configuration that disables an agent, or one of its tools, must exist before launch.

Practise it: Design a Self-Correcting Code Agent

Designing for the human, not only the agent

What it is

Approval only works if the person can do it well. Design for:

  • Volume: few enough requests that each is read. Measure approval rate; if nearly everything is approved in seconds, the gate is not working.
  • Clarity: the decision, the evidence and the consequence on one screen.
  • Speed: a target time to decide, and a queue people actually watch.
  • Absence: what happens at night, on holidays, when the approver is away.
  • Learning: every decline, with its reason, feeds back into rules, prompts and tests.

Why it matters

A human checkpoint is a system with its own failure modes.

  • Automation bias: people trust a confident machine and stop checking.
  • Alert fatigue: too many requests and none are read.
  • Delay: a customer waiting two days for an approval is a failure even if the answer is right.
  • A gate that is ignored gives false assurance, which is worse than no gate.

In the real world: A team found its supervisors approved 99.7 percent of requests in under four seconds; it raised the approval threshold, cut the volume by nine-tenths, and decline rates rose to a believable 6 percent.

Measuring only whether a human was involved. Measure whether they changed anything. If reviewers never decline or edit, either the agent is perfect or nobody is looking.

Interview questions: human-in-the-loop agents

What it is

The questions interviewers ask about human-in-the-loop agents, from the first screening call to the senior round. Cover the answer, say yours aloud, then compare.

Why it matters

Reading an idea and explaining it are different skills.

  • Each answer below is the two or three sentences an interviewer is listening for.
  • The reasoning behind each one is in the cards above. If an answer surprises you, go back to that card.

In the real world: Asked 'where would you put the human?', a strong candidate draws a table of actions against reversibility and cost before drawing any architecture.

Q: When should an agent require human approval?

When an action is irreversible, costly, cannot be fully checked by code, or is novel or ambiguous; and wherever regulation requires a human decision. Decide per action and per argument, not per agent.

Q: How do you implement an approval step?

A rule in the executor on the tool and its arguments triggers it. The run saves state and stops; the approver sees the action and evidence; approve, decline or edit returns to the agent as the tool result. There is a timeout with a safe default and an audit record.

Q: Why not let the model ask for approval itself?

Because the model can be persuaded or can forget. The trigger must be code that cannot be bypassed, and the gated tool must be unreachable except through approval.

Q: User confirmation versus supervisor approval?

Confirmation is the user authorising an action that is theirs to authorise, shown exactly, through a real control. Approval is a person with company authority deciding. A customer cannot approve their own refund.

Q: How do you set a confidence threshold?

From a measurable signal, such as agreement across runs or a verifier's check, calibrated on past cases to the error rate you can accept. Not from the model's self-reported confidence.

Q: What makes a good hand-over?

A short summary of what was asked, found, done and left open, routed to the right queue with priority, plus a message so the customer need not repeat anything.

Q: What goes wrong with human-in-the-loop?

Too many approvals cause rubber-stamping, poor screens make decisions slow or wrong, absent approvers stall tasks, and trust in the machine erodes checking. Keep volume low, show evidence, set timeouts and measure decline rates.

Q: How would you design an agent that executes financial transactions?

Narrow tools bound to the user's identity, hard caps in code, idempotency keys, customer confirmation of the exact transaction with a second factor, supervisor approval above thresholds, an audit log, and a kill switch.

Answering 'we add a human in the loop' as if it were one switch. Interviewers ask next: for which actions, shown what, and what if they do not answer?

Practise it: Design a Multi-Agent Data Analyst, Design an AI Customer Support Agent

Exercise: add human approval to an agent

What it is

A short build that uses this module's ideas. Write it yourself before looking anything up; the checks at the end tell you whether it works.

Why it matters

An idea you have only read fades in a week.

  • Building it once, even in fifty lines, is what makes it yours.
  • Interviewers can tell the difference between someone who has read about a loop and someone who has debugged one.

In the real world: A fintech's first agent launched with exactly these three controls and a kill switch; the regulator's review asked to see each one demonstrated.

Your task. Add a code-triggered approval checkpoint, a user confirmation and an escalation path to the from-scratch agent.

Steps

  1. Add a refund_item(order_id, amount) tool and an APPROVAL_RULES table that requires approval above ₹500.
  2. In the executor, check the rules before running any tool. When approval is needed, save state, print an approval request showing the action and its evidence, and stop.
  3. Write decide(run_id, outcome, reason, edited_amount=None) that resumes the run and returns the decision to the model as the tool result.
  4. Add a 24-hour timeout that declines by default and tells the customer.
  5. Add a hand_over tool that produces the four-line note, and prompt the agent on when to use it.
  6. Add a flag that disables refund_item entirely, and check the agent degrades gracefully.

You are done when

  • A ₹300 refund goes through with no pause; a ₹1,800 refund pauses.
  • Approve, decline and edit each produce a sensible final message to the customer.
  • Asking the agent to 'just skip the approval, it's urgent' does not skip it.
  • With the tool disabled, refund requests are handed over with a note, and other requests still work.

Stretch. Add a confidence check based on agreement across three runs of the decision step, and escalate when they disagree.