Loading...
Loading...
Pause an AG2 `Agent` mid-run to collect human input via `context.input()`, or gate a tool call with `approval_required()` middleware. Use when the user wants the agent to ask for confirmation, request missing info (passwords, API keys, data), or have a human approve sensitive / irreversible / expensive tool calls (sending emails, deleting records, payments).
npx skill4agent add ag2ai/ag2-skills ag2-hitl| Need | Use |
|---|---|
| Tool asks an open question and waits for a typed answer | |
| Approve / deny a specific tool call before its body runs | |
context.input()Context.input(message, timeout=...)hitl_hookfrom ag2 import Agent, Context, tool
from ag2.events import HumanInputRequest, HumanMessage
@tool
async def execute_query(context: Context) -> str:
answer = await context.input(
"Are you sure you want to run this query? (yes/no)",
timeout=60.0,
)
if answer.strip().lower() != "yes":
return "Query cancelled."
return "Query executed successfully."
def hitl_hook(event: HumanInputRequest) -> HumanMessage:
print(f"Agent asks: {event.content}")
return HumanMessage(content=input("Your answer: "))
agent = Agent("dba", tools=[execute_query], hitl_hook=hitl_hook)HumanInputRequestevent.contentHumanMessagestrstrHumanMessage.ensure_messagedefasync defagent = Agent("dba", tools=[execute_query])
@agent.hitl_hook
async def async_hitl_hook(event: HumanInputRequest) -> HumanMessage:
answer = await collect_from_ui(event.content)
return HumanMessage(content=answer)@agent.hitl_hookRuntimeWarning"You already set HITL hook, provided value overrides it"ContextInjectVariableDependscontext.input()HumanInputNotProvidedErrorapproval_required()from ag2 import Agent, tool
from ag2.config import OpenAIConfig
from ag2.middleware import approval_required
@tool(middleware=[approval_required()])
def delete_account(user_id: str) -> str:
"""Deletes a user account by ID permanently."""
return f"Account {user_id} deleted."
agent = Agent(
"support",
config=OpenAIConfig(model="gpt-4o-mini"),
tools=[delete_account],
hitl_hook=lambda event: input(event.content),
)delete_accountallow_always=TrueAgent wants to call the tool:
`delete_account`, {"user_id": "abc-123"}
Please approve or deny this request.
Y/N/Always?yyes1alwaysdenied_message"User denied the tool call request"timeoutallow_always=FalseY/N?approval_required()context.input()hitl_hook@tool(middleware=[approval_required(
message="⚠️ Run `{tool_name}` with {tool_arguments}? (y/n)",
denied_message="Operation blocked by user.",
)])
def send_email(to: str, subject: str, body: str) -> str:
"""Send an email to the given address."""
...{tool_name}{tool_arguments}@tool(middleware=[approval_required()])
async def schedule_report(name: str, context: Context) -> str:
"""Schedule a report — asks the user for the cadence, then runs after approval."""
cadence = await context.input("How often? (daily / weekly / monthly)")
return f"Scheduled '{name}' on {cadence} cadence."context.input()website/docs/user-guide/context/human_in_the_loop.mdxcontext.inputhitl_hookwebsite/docs/user-guide/tools/approval_required.mdxapproval_requiredwebsite/docs/user-guide/tools/tool_middleware.mdxag2-middlewareBaseMiddleware.on_human_input()../ag2-add-custom-tool/references/dependency_injection.mdapproval_required()hitl_hookcontext.input()HumanInputNotProvidedErrorcontext.input()input()input()async defcontext.input(prompt)timeout=60.0RuntimeWarningHumanMessagectx.input()