关于 ag2-hitl
为使用 ag2 构建 Agent 的开发者提供人工介入模式指导。介绍运行中收集用户输入,以及在特定工具执行前请求批准两种机制。
主要功能
- 01通过 context.input() 收集输入
- 02用 hitl_hook 接收人工回复
- 03通过中间件审批工具调用
- 04自定义提示与拒绝消息
- 05超时及拒绝路径处理
适用场景
- 01执行高风险操作前请求确认
- 02在对话中补充凭据或上下文
- 03定稿前收集人工审阅意见
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ ag2ai/ ag2-skills --skill ag2-hitl安装后在 Claude Code 中输入/ag2-hitl确认已加载。
详细介绍
Human-in-the-loop(人在环中)
适用场景
- Agent 在执行有风险的操作前应请求确认。
- Agent 需要在对话中途向用户获取信息(密码、API 密钥、缺失的上下文)。
- 特定的工具调用需要在运行前获得人工批准(不可逆/昂贵/敏感)。
- 质量保证——展示草稿,并在定稿前获取人工修改/批准。
有两种不同的机制,请根据意图选择:
| 需求 | 使用方式 |
|---|---|
| 工具提出开放式问题并等待用户输入答案 | 在工具内部使用 context.input(),并在 Agent 上设置 hitl_hook |
| 在特定工具调用的函数体运行前批准/拒绝该调用 | approval_required() 工具中间件 |
模式 1 — 使用 context.input() 提出开放式问题
工具通过 Context.input(message, timeout=...) 请求输入。Agent 必须设置一个知道如何收集该输入的 hitl_hook。
from 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)
钩子会收到一个 HumanInputRequest(提示词位于 event.content 中),并返回 HumanMessage 或普通 str(框架会通过 HumanMessage.ensure_message 将 str 包装起来)。支持 def 和 async def 钩子。
也可以在构造完成后注册钩子:
agent = 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_hook 会发出 RuntimeWarning("You already set HITL hook, provided value overrides it")。请只在一个位置设置钩子,以避免该警告。
钩子支持依赖注入——Context、Inject、Variable、Depends 的用法与工具中相同。
如果调用了 context.input(),但没有注册钩子,框架会引发 HumanInputNotProvidedError。
模式 2 — 使用 approval_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),
)
当 Agent 调用 delete_account 时,用户会看到以下提示(默认 allow_always=True):
Agent wants to call the tool:
`delete_account`, {"user_id": "abc-123"}
Please approve or deny this request.
Y/N/Always?
匹配前会先将答案转换为小写。y、yes 或 1 会批准本次调用;always 会批准本次调用以及同一上下文中该工具的后续所有调用(它会设置一个针对每个上下文的绕过标记)。其他任何答案都会拒绝调用;Agent 会收到 denied_message(默认值为 "User denied the tool call request"),并可据此调整。默认 timeout 为 30 秒。设置 allow_always=False 可移除“Always”选项(此时提示只显示 Y/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.mdx(context.input、hitl_hook)、website/docs/user-guide/tools/approval_required.mdx(approval_required中间件)。 - 通用工具中间件:
website/docs/user-guide/tools/tool_middleware.mdx。另请参阅ag2-middleware,了解如何通过BaseMiddleware.on_human_input()在 Agent 层级拦截 HITL。 - HITL 钩子对依赖注入的支持与工具相同——请参阅
../ag2-add-custom-tool/references/dependency_injection.md。
常见陷阱
approval_required()未设置hitl_hook— 中间件会调用context.input(),因此 Agent 需要一个钩子。否则会看到HumanInputNotProvidedError。- 忘记处理拒绝路径 —
context.input()会返回钩子返回的内容。如果你只对“是”进行分支处理,那么任何其他回答(包括沉默/默认值)都会让操作继续。务必进行验证。 - 在异步 UI 中使用同步
input()—input()会阻塞事件循环。对于任何非 CLI 应用,请使用异步钩子(async def)和异步输入收集器(WebSocket、消息队列)。 - 未设置超时 —
context.input(prompt)可能会一直等待。在任何生产环境路径中,都应传入timeout=60.0(秒)。 - 装饰器钩子会覆盖构造函数钩子 — 如果两者都设置,装饰器会优先生效,并发出
RuntimeWarning。请选择其中一种设置位置。 - 以为
HumanMessage会自动进入对话历史 — 它会作为发起请求的工具的返回值进入历史,但通过ctx.input()收集的运行中输入不是单独的用户轮次,而是保留在工具的作用域中。