人工在环工作流
人工在环(HITL)的概念
人工在环(Human-In-The-Loop,HITL)工作流允许在代理执行敏感或关键操作前暂停执行,让人类审查和批准这些操作。这在许多生产场景中至关重要,特别是涉及删除、修改关键数据或调用外部 API 的情况。
HITL 工作流
配置敏感操作
基本配置
from deepagents import create_deep_agent
agent = create_deep_agent(
model="anthropic:claude-3-5-sonnet-20241022",
tools=[delete_file, modify_database, send_email],
interrupt_on={
"delete_file": True, # 删除需要批准
"modify_database": True, # 数据库修改需要批准
"send_email": {"allowed_decisions": ["approve", "reject"]}, # 邮件:只能批准/拒绝
}
)
决策类型
三种可能的人工决策:
| 决策 | 说明 | 何时使用 |
|---|---|---|
approve | 使用原始参数执行 | 低风险、简单操作 |
edit | 修改参数后执行 | 需要微调的操作 |
reject | 完全跳过操作 | 需要完全避免的操作 |
按操作的细粒度控制
agent = create_deep_agent(
interrupt_on={
# 删除操作:所有决策都允许
"delete_file": {
"allowed_decisions": ["approve", "edit", "reject"]
},
# 修改操作:允许批准或编辑,但不能拒绝
"modify_database": {
"allowed_decisions": ["approve", "edit"],
},
# 发送操作:只能批准,这是强制的
"send_critical_alert": {
"allowed_decisions": ["approve"],
},
# 低风险操作:无需审批
"read_file": False,
}
)
处理中断
与中断交互
from langchain_core.utils.uuid import uuid7
from langgraph.types import Command
# 创建一个配置来追踪线程
config = {"configurable": {"thread_id": str(uuid7())}}
# 第一次调用 - 代理遇到敏感操作并暂停
result = agent.invoke(
{"messages": [{"role": "user", "content": "删除临时文件 /tmp/cache/*"}]},
config=config,
version="v2", # 启用中断功能
)
# 检查是否有中断
if result.get("interrupts"):
print("⚠️ 代理需要人类审批")
interrupt_value = result["interrupts"][0].value
action_requests = interrupt_value["action_requests"]
review_configs = interrupt_value["review_configs"]
# 显示待处理的操作
for action in action_requests:
print(f"\n操作:{action['name']}")
print(f"参数:{action['args']}")
print(f"描述:{action.get('description', '')}")
# 获取人类决策
human_decisions = get_human_decisions(action_requests)
# 继续执行
final_result = agent.invoke(
Command(resume={"decisions": human_decisions}),
config=config,
version="v2",
)
print(f"\n执行结果:{final_result['messages'][-1].content}")
获取人类决策的实现
def get_human_decisions(action_requests: list) -> list[dict]:
"""从人类获取对每个操作的决策"""
decisions = []
for i, action in enumerate(action_requests):
print(f"\n【操作 {i+1}/{len(action_requests)}】")
print(f"工具:{action['name']}")
print(f"参数:{json.dumps(action['args'], indent=2, ensure_ascii=False)}")
while True:
choice = input("请选择(A=批准/E=编辑/R=拒绝):").upper()
if choice == "A":
decisions.append({"type": "approve"})
break
elif choice == "E":
edited_args = edit_arguments(action['args'])
decisions.append({
"type": "edit",
"args": edited_args
})
break
elif choice == "R":
decisions.append({"type": "reject"})
break
else:
print("无效选择,请重试")
return decisions
def edit_arguments(original_args: dict) -> dict:
"""允许用户编辑操作参数"""
edited_args = original_args.copy()
for key in edited_args:
current_value = edited_args[key]
print(f"\n编辑参数 '{key}'")
print(f"当前值:{current_value}")
new_value = input("新值(留空保持不变):").strip()
if new_value:
edited_args[key] = new_value
return edited_args
实际应用例子
例1:数据删除确认
from deepagents import create_deep_agent
def delete_records(query: str, limit: int) -> str:
"""删除匹配查询的数据库记录"""
# 实际的删除逻辑
return f"已删除 {limit} 条记录"
def list_records(query: str) -> list[dict]:
"""列出将被删除的记录预览"""
# 返回将被删除的记录
return [{"id": 1, "name": "record1"}, ...]
agent = create_deep_agent(
system_prompt="""你是数据管理助手。
当用户要求删除记录时:
1. 首先使用 list_records 显示将被删除的内容
2. 然后调用 delete_records""",
tools=[delete_records, list_records],
interrupt_on={
"delete_records": {
"allowed_decisions": ["approve", "edit", "reject"]
}
}
)
# 使用工作流
config = {"configurable": {"thread_id": "deletion-task-001"}}
result = agent.invoke(
{"messages": [{"role": "user", "content": "删除 2024 年之前的旧记录"}]},
config=config,
version="v2",
)
if result.get("interrupts"):
# 显示预览和等待人类决策...
pass