Human-in-the-Loop Design for AI Agent Executors: Tool Approval, Structured Q&A, and Non-Blocking Intervention
This article explains how to design human intervention into AI agent executors. It covers why agents need bidirectional control flow, core design principles, pre-tool-call interception, input contracts for tool approval, structured Q&A, and information collection, a dual-channel protocol for long-running human input, non-blocking implementation, security, auditability, observability, and a production checklist.
1. Background: Agents Are Bidirectional Control Flows, Not Single-Direction Text Generation
Traditional conversational applications only generate text, so their side effects are almost zero. In such systems, an asynchronous quality check mode that generates first and reviews afterward is usually safe enough. Agents are different. While producing reasoning content, an agent can also initiate real tool calls to read or write files, execute commands, and access external APIs. This introduces three fundamental changes.
First, actions have external side effects. A write, delete, publish, or payment call may be impossible to roll back after execution. If text is wrong, it can be regenerated; if a side effect is wrong, recovery is often the only option.
Second, the control flow may reverse direction. An agent not only outputs content to the user. It may also stop midway to request authorization, ask for missing information, or ask the user to choose among several options. This requires a bidirectional channel between the frontend and backend, not just a simple request-response model.
Third, error propagation paths become longer. One model misjudgment can be amplified through multiple stages: planning, tool selection, parameter filling, and execution. The earlier an interception occurs, the lower the cost. Placing a human confirmation point before a tool call is the most cost-effective intervention location.
Therefore, human intervention is not an optional product-experience feature. It is a first-class citizen in agent executor architecture.
2. Design Goals and Core Principles
The design goal can be summarized as follows: provide unified, non-blocking, and recoverable human confirmation and guidance at any critical point during agent execution. This covers three interaction scenarios: tool approval, structured Q&A, and information collection.
Under this goal, six principles are recommended.
- Unified mechanism. All interaction types, including approval, single selection, multiple selection, and free-text input, should converge into the same interception entry point and the same message protocol. Avoid building separate parallel implementations, such as one channel for approval and another for question answering. Multiple mechanisms lead to multiple timeout logics, multiple state-management systems, and multiple analytics definitions, which eventually diverge.
- Non-blocking. While waiting for human input, background tasks, event-push connections, heartbeats, and timeout timers must continue working. Implementing approval with synchronous blocking waits is one of the most common architectural defects in practice.
- Structured protocol. Interaction payloads must be structured objects with explicit type identifiers, not concatenated free text. Only structured payloads allow the frontend to render consistent controls and allow the backend to perform parameter-level validation and auditing.
- Safe by default. When there is no response, a timeout, or a protocol exception, the default behavior should be to deny execution rather than allow it. The principle of least privilege should be reflected in the default value of approval decisions.
- Idempotent and recoverable. Repeated submission of the same decision request should take effect only once. After a process restart or connection loss, tasks in a “pending human handling” state should be recoverable.
- Auditable. Every human decision should leave a complete record of who made the decision, when it was made, what operation it concerned, what decision was made, and why.
3. Unified Intervention Entry: Interception Before Tool Calls
The core design is to insert a decision callback before a tool is actually executed. Agent frameworks often expose a pre-tool-use hook that allows the host program to take over the decision of whether to allow execution. The host’s return value determines the subsequent flow:
# Illustrative pseudocode: pre-tool-use decision callbackfrom dataclasses import dataclassfrom typing import Literal@dataclassclass Decision:behavior: Literal["approve", "deny", "feedback"]reason: str | None = None # Required for deny or feedbackupdated_input: dict | None = None # Optional: approve with corrected tool inputasync def on_tool_use(tool_name: str, tool_input: dict, ctx) -> Decision:policy = ctx.policy_engine.evaluate(tool_name, tool_input)if policy.auto_approve:return Decision("approve")if policy.auto_deny:return Decision("deny", reason=policy.message)# Escalate to a human decision: send an approval request and wait asynchronouslyreturn await ctx.human_gate.request_approval(tool_name=tool_name, tool_input=tool_input, timeout=ctx.timeout)
The three decision semantics must be strictly distinguished:
- approve (allow execution): allow the original parameters, or allow execution after a human has corrected the parameters through
updated_input. The value of the second case is often underestimated. An approver does not have to choose only between fully approving and fully rejecting. Directly correcting parameters can significantly reduce round trips. - deny (reject execution): terminate this invocation and feed the rejection reason back to the agent. The reason must enter the model context. Otherwise, the model cannot understand why the operation failed and may retry the same dangerous operation with an equivalent tool.
- feedback (reject with corrective guidance): this differs from
denybecause the returned content is actionable guidance, such as “the path must be inside the working directory” or “create the directory before writing.” This allows the agent to adjust its plan instead of retrying in place.
4. Input Contracts for Three Interaction Scenarios
4.1 Tool Approval
An approval payload should include the tool name, full input parameters, risk level, expected impact scope, and call source, such as which sub-agent initiated the call. For frontend rendering, it is recommended to show a parameter diff comparison: display the parameters requested by the model alongside historical averages, allowlist rules, or baseline configurations so that a human can identify abnormal values within seconds. For example, the deletion scope may change from a single file to an entire directory.
4.2 Structured Q&A
A minimal contract for structured Q&A can be designed as follows:
{"type": "question","question": "Please select the target environment for this release.","options": ["staging", "production"],"multiple": false}
This supports three modes. If options is absent, the interaction falls back to free-text input. If options is present and multiple is false, it is a single-selection question. If multiple is true, it is a multiple-selection question.
There are three practical rules for option design:
- Keep the number of options between 2 and 4 to avoid cognitive load.
- Options should be mutually exclusive and non-overlapping.
- Always provide an “Other / custom” escape path to prevent incomplete option sets from blocking the workflow.
The model’s tendency to ask questions must also be constrained. In practice, a model may prefer asking one more question to feel safer, which leads to interaction fatigue. It is recommended to state clearly in the system prompt that the model should ask only when missing information would change the execution result. On the executor side, set a maximum number of questions per task. If the limit is exceeded, continue with a predefined default strategy.
4.3 Information Collection
This scenario applies to inputs that cannot be enumerated, such as keys, target paths, and business parameters. The payload should declare the field name, type, whether it is sensitive, and validation rules. For sensitive fields, the handling principle is strict: values may be uploaded upward, but they must be masked in all downstream events and logs. A full value must never be displayed in an approval interface.
5. Dual-Channel Message Protocol
Because the time required to wait for human input is unpredictable and may last several minutes or even cross days, a single HTTP request-response model cannot support it. The recommended combination is as follows:
- Downstream: the server uses SSE (Server-Sent Events) to continuously push events to the frontend. Event types should at least include
text_delta,tool_use,approval_request,question,status, anderror. - Upstream: the frontend uses HTTP POST to submit decisions. Each submission carries
request_idas an idempotency key. The server uses it to discard duplicate submissions.
Downstream event example:
{ "id": 128, "event": "approval_request","data": { "request_id": "ap_9f2", "tool": "file_write","input": {"path": "/data/out.csv"}, "risk": "write","expires_at": 1730000000 } }
Upstream decision example:
POST /decisions{ "request_id": "ap_9f2", "behavior": "feedback","reason": "Write to the tmp subdirectory under the working directory." }
The protocol layer must handle three additional things:
- Event sequencing and resumable delivery. When a client reconnects, it should request missing events using the last received event ID to avoid losing intermediate state.
- Heartbeats and idle timeouts. These prevent intermediate gateways from closing long-lived connections.
- Timeout fallback. If an approval request expires without a decision, it should be handled as “deny and record,” and the timeout event should be explicitly pushed to the frontend.
6. Non-Blocking Implementation
In implementation, avoid blocking waits inside an asynchronous event loop. The correct approach is to model a human decision as a pending asynchronous Future. The interception callback creates the Future and immediately yields control. The event loop continues processing heartbeats and event pushes. When the decision POST request arrives, the server validates request_id and the current state, completes the Future, and allows the agent workflow to resume.
Three details must be implemented:
- Idempotency: if the same
request_idis submitted again, return the first result directly. - Cancellation: if the frontend page is closed or the user actively terminates the task, cancel the Future and inject a termination signal into the agent.
- State persistence: persist states such as
pending_approvalandpending_input. After a process restart, pending requests can be replayed.
7. Security, Auditability, and Observability
Security requires three hard constraints:
- Content in tool outputs must be treated as untrusted data. If such content contains instructions such as “ignore previous instructions,” it must not affect the approval policy.
- Approval-policy decisions must be based on structured parameters, not on the model’s self-described intent.
- Permission decisions may only be made by the host-side policy engine or by a human. The model has no authority to approve itself.
For auditing, it is recommended to record the tool name, parameter snapshot, decision result, reason, operator identifier, and timestamp for every decision. This creates a tamper-resistant approval trail and supports compliance traceability.
For observability, build a metrics loop around human intervention: approval request volume, human rejection rate, correction success rate after feedback, average waiting time, timeout rate, and number of interactions per task. Among these, “correction success rate after feedback” best reflects policy quality. If this value is low, it may indicate that rejection reasons are unclear or that policy rules are disconnected from actual business conditions.
8. Production Checklist
- Do all interaction types converge into a single interception entry point?
- Does no response or timeout result in denial rather than approval?
- Are rejection reasons fed back into the agent context?
- Do decision requests have an idempotency key and state persistence?
- Does the approval payload include parameter diff comparison and sensitive-information masking?
- Are the number of questions and approval frequency per task limited?
- Are event-sequence resumption and heartbeat mechanisms in place?
- Are approval trails and intervention-related metrics retained?
Treat human intervention as a core component of the executor rather than an external patch. Only then can an agent remain autonomous while still maintaining safety boundaries around critical operations.
