新闻详情
DeepAgents 之interrupt_on人工审批
DeepAgents 之interrupt_on人工审批
目录一、30 秒看懂 interrupt_on二、核心概念三、易混淆对比重点四、interrupt_on 配置详解五、四种人工决策类型六、条件中断 when 谓词七、中断后的处理流程八、子智能体与 interrupt_on九、实战示例十、避坑清单十一、一张表总结一、30 秒看懂 interrupt_oninterrupt_on 「哪些工具调用必须先经过人工审批」的配置表 Agent 准备调用敏感工具 → 暂停interrupt→ 等人 approve/edit/reject/respond → 用 Command(resume...) 恢复 → Agent 继续跑类比角色对应Agent想执行操作的员工interrupt_on敏感操作清单人工审批主管点头 / 改参数 / 拒绝Command(resume...)把审批结果交回系统二、核心概念2.1 interrupt_on 是什么interrupt_on是create_deep_agent的一个参数interrupt_on:dict[str,bool|InterruptOnConfig]|NoneNone含义工具名 → 是否/如何中断的映射。配置了 interrupt_on框架自动做什么非空或与 permissions interrupt 合并后非空挂载HumanInTheLoopMiddleware工具被调用且命中规则图执行暂停返回result.interrupts用户Command(resume...)按决策执行/跳过/改参Agent 继续2.2 执行流程用户提问 │ ▼ Agent 推理 → 决定调用 write_file │ ▼ interrupt_on 命中 ──否──▶ 直接执行工具 │ 是 ▼ 暂停返回 interrupts含 tool 名、参数、允许的决策 │ ▼ 人工 approve / edit / reject / respond │ ▼ agent.invoke(Command(resume{decisions: [...]}), 同一 config) │ ▼ Agent 继续可能再次 interrupt直到结束2.3 必备前置条件条件是否必须说明checkpointer✅ 必须保存暂停时的图状态否则无法 resumethread_id✅ 必须同一对话线程resume 时要与首次 invoke 相同versionv2✅ 推荐读取result.interrupts、resume 的标准写法HumanInTheLoopMiddleware自动有 interrupt_on 时框架自动安装无需手写三、易混淆对比重点3.1 interrupt_on vs permissions mode“interrupt”两者都能「暂停等人」但入口和粒度不同对比项interrupt_onpermissionsmodeinterrupt配置方式按工具名配置按路径 操作类型配置作用对象任意工具自定义 内置仅内置文件系统工具粒度整个工具可用when细化路径级/secrets/**等HITL 中间件手动配或自动有规则时有 interrupt 规则时自动合并进 interrupt_on典型场景删库、发邮件、调支付 API敏感目录写入要审批能否同时用✅ 可以用户 interrupt_on 同工具名优先✅ 合并到同一 HITL 配置# 方式 A直接按工具名interrupt_on{write_file:True}# 方式 B按路径框架自动转成 write_file/edit_file 的 when 谓词permissions[FilesystemPermission(operations[write],paths[/secrets/**],modeinterrupt)]# 方式 C两者并存同工具名时 interrupt_on 参数覆盖 permissions 生成的配置3.2 approve / edit / reject / respond决策含义工具是否执行典型用途approve原样执行✅ 执行确认 Agent 提案edit改参数后执行✅ 执行改过的 args改收件人、改路径reject拒绝执行❌ 不执行返回拒绝消息给 Agent禁止删文件、禁止发信respond人直接充当工具返回结果❌ 不执行把人说的话当 ToolMessageask_user类工具易错点错误用法正确理解对delete_file用respond表示「拒绝」拒绝用rejectrespond是「人代替工具回答」reject不写message可以但有默认文案敏感操作建议写清楚「勿重试」3.3 interrupt_on 的 True / False / InterruptOnConfig配置值含义True开启中断默认允许 approve、edit、reject、respondFalse显式关闭该工具的中断即使父级/permissions 有规则{allowed_decisions: [...]}自定义允许哪些决策{allowed_decisions: [...], when: fn}自定义决策 条件中断interrupt_on{remove_file:True,# 全开fetch_file:False,# 不关心中断notify_email:{allowed_decisions:[approve,reject]},# 不许改参数}3.4 单工具中断 vs 批量中断场景行为Agent 一次只调 1 个敏感工具action_requests长度 1Agent 一次调多个敏感工具合并为一次 interruptaction_requests有多项resume 时decisions列表顺序必须与 action_requests 一致3.5 interrupt_on vs 工具内 interrupt()对比项interrupt_on工具内interrupt()配置位置create_deep_agent(interrupt_on...)自定义工具函数内部触发点工具即将被调用前工具执行过程中任意位置数据结构action_requestsreview_configs自定义 payloadresume 格式Command(resume{decisions: [...]})Command(resume{...})自定义适用标准 HITL 审批流工具内部多步确认本文聚焦interrupt_on工具内interrupt()属于进阶用法。3.6 主智能体 vs 子智能体 interrupt_on子智能体类型是否继承主 agent 的 interrupt_on声明式SubAgent字典✅ 默认继承子 agent 自己的interrupt_on完全覆盖CompiledSubAgent❌ 不继承在子图内部自己配AsyncSubAgent❌ 不继承在远程子 agent 配四、interrupt_on 配置详解4.1 参数签名agentcreate_deep_agent(modelqwen-plus,tools[my_tool,...],interrupt_on{tool_name:True|False|InterruptOnConfig,},checkpointerInMemorySaver(),# 必须)4.2 InterruptOnConfig 字段字段类型说明allowed_decisionslist[str]可选approve/edit/reject/respondwhenCallable[[ToolCallRequest], bool]返回True才中断False自动放行4.3 与 Middleware 的关系interrupt_on 非空或与 permissions interrupt 合并后非空 ↓ 自动追加 HumanInTheLoopMiddleware栈尾部 ↓ 工具调用前检查是否 interrupt ↓ 若中断后未 resumePatchToolCallsMiddleware 会在下次启动前修复悬空 tool_call组件角色HumanInTheLoopMiddleware执行 interrupt / resume 逻辑PatchToolCallsMiddleware中断取消后修补消息历史五、四种人工决策类型5.1 approve —— 原样批准decision{type:approve}工具按 Agent 原始参数执行。5.2 edit —— 改参数后执行decision{type:edit,edited_action:{name:notify_email,# 必须带工具名args:{to:teamcorp.com,subject:...,body:...},},}allowed_decisions里必须包含edit。5.3 reject —— 拒绝执行decision{type:reject,message:用户拒绝删除该文件请勿重试改问用户要归档哪个目录。,}Agent 收到的是拒绝反馈不会执行工具。5.4 respond —— 人代替工具回答decision{type:respond,message:用户说先用测试环境不要上生产。,}适合「问用户」类工具不要用来拒绝有副作用的操作。5.5 决策配置示例对照工具风险推荐 allowed_decisions删除、支付、发外部邮件[approve, edit, reject]中等风险写操作[approve, reject]只读确认类[approve]或[approve, reject]向用户提问可含respond六、条件中断 when 谓词默认interrupt_on里列出的工具每次调用都暂停。加when后只有谓词返回 True 才暂停。fromlangchain.tools.tool_nodeimportToolCallRequestdefwrites_outside_workspace(request:ToolCallRequest)-bool:只有写到 /workspace/ 外才 interrupt。pathrequest.tool_call[args].get(file_path,)returnnotpath.startswith(/workspace/)agentcreate_deep_agent(modelqwen-plus,interrupt_on{write_file:{allowed_decisions:[approve,edit,reject],when:writes_outside_workspace,},},checkpointerInMemorySaver(),)when 返回值行为True暂停等人审批False不中断直接执行省略when每次调用都中断注意request.tool_call[name]/request.tool_call[args]是标准取法与wrap_tool_call相同。七、中断后的处理流程7.1 标准两步法fromuuidimportuuid4fromlanggraph.checkpoint.memoryimportInMemorySaverfromlanggraph.typesimportCommand config{configurable:{thread_id:str(uuid4())}}# ① 首次调用resultagent.invoke({messages:[{role:user,content:删除 temp.txt}]},configconfig,versionv2,)# ② 检查中断ifresult.interrupts:ivresult.interrupts[0].value actioniv[action_requests][0]review{c[action_name]:cforciniv[review_configs]}print(待审批工具:,action[name])print(参数:,action[args])print(允许决策:,review[action[name]][allowed_decisions])# ③ 人工决策后恢复config 必须相同resultagent.invoke(Command(resume{decisions:[{type:approve}]}),configconfig,versionv2,)print(result.value[messages][-1].content)7.2 result.interrupts 结构字段含义result.interrupts中断列表非空表示暂停interrupts[0].value[action_requests]待审批的工具调用列表interrupts[0].value[review_configs]每个工具允许的决策类型action_requests[i][name]工具名action_requests[i][args]工具参数7.3 多工具批量审批# Agent 同时想 delete send_email两个都要审批decisions[{type:approve},# 对应 action_requests[0]{type:reject,message:禁止发邮件请勿重试},# 对应 action_requests[1]]resultagent.invoke(Command(resume{decisions:decisions}),configconfig,versionv2)规则len(decisions) len(action_requests)且顺序一一对应。7.4 流程图invoke(用户消息) │ ├─ result.interrupts 为空 → 直接拿 result.value[messages][-1] │ └─ result.interrupts 非空 │ ├─ 解析 action_requests ├─ 收集用户 decisions └─ invoke(Command(resume{decisions: [...]}), 同一 config) │ └─ 可能再次 interrupt循环直到结束八、子智能体与 interrupt_on8.1 SubAgent 覆盖主 agent 配置agentcreate_deep_agent(modelqwen-plus,interrupt_on{delete_file:True,read_file:False,# 主 agent读文件不需审批},subagents[{name:file-manager,description:文件管理,system_prompt:你是文件管理员,interrupt_on:{delete_file:True,read_file:True,# 子 agent读也要审批覆盖继承},}],checkpointerInMemorySaver(),)8.2 继承规则表场景interrupt_on 来源SubAgent 未配interrupt_on继承主 agent permissions 合并结果SubAgent 配了interrupt_on完全使用子 agent 自己的不合并主 agentgeneral-purpose 子智能体继承主 agent 的 interrupt_on子 agent 触发 interrupt 时处理方式与主 agent 相同检查result.interruptsCommand(resume...)恢复。九、实战示例示例 1最小可运行自定义工具 interrupt_onimportosfromuuidimportuuid4fromdeepagentsimportcreate_deep_agentfromlangchain.chat_modelsimportinit_chat_modelfromlangchain.toolsimporttoolfromlanggraph.checkpoint.memoryimportInMemorySaverfromlanggraph.typesimportCommand modelinit_chat_model(modelqwen-plus,model_provideropenai,api_keyos.getenv(ali_api_key),base_urlhttps://dashscope.aliyuncs.com/compatible-mode/v1,temperature0,)tooldefdelete_file(path:str)-str:删除文件。returnf已删除{path}agentcreate_deep_agent(modelmodel,tools[delete_file],interrupt_on{delete_file:True},checkpointerInMemorySaver(),)config{configurable:{thread_id:str(uuid4())}}resultagent.invoke({messages:[{role:user,content:删除 /tmp/a.txt}]},configconfig,versionv2,)ifresult.interrupts:actionresult.interrupts[0].value[action_requests][0]print(f待审批:{action[name]}{action[args]})okinput(批准y/n).strip().lower()ydecision{type:approve}ifokelse{type:reject,message:用户拒绝删除}resultagent.invoke(Command(resume{decisions:[decision]}),configconfig,versionv2)print(result.value[messages][-1].content)示例 2限制 allowed_decisionsagentcreate_deep_agent(modelmodel,tools[delete_file,notify_email],interrupt_on{delete_file:{allowed_decisions:[approve,reject]},# 不许改参数notify_email:True,},checkpointerInMemorySaver(),)示例 3条件中断仅危险路径fromlangchain.tools.tool_nodeimportToolCallRequestdefis_production_path(req:ToolCallRequest)-bool:pathreq.tool_call[args].get(file_path,)returnpath.startswith(/prod/)agentcreate_deep_agent(modelmodel,interrupt_on{write_file:{allowed_decisions:[approve,edit,reject],when:is_production_path,},},checkpointerInMemorySaver(),)示例 4与 permissions interrupt 配合敏感目录写入fromdeepagentsimportFilesystemPermission,create_deep_agentfromdeepagents.backendsimportFilesystemBackend agentcreate_deep_agent(modelmodel,backendFilesystemBackend(root_dir.,virtual_modeTrue),permissions[FilesystemPermission(operations[write],paths[/secrets/**],modeinterrupt),],# 无需再写 interrupt_on{write_file: True}框架会自动合并checkpointerInMemorySaver(),)等价于对/secrets/**的write_file/edit_file自动生成带when谓词的 interrupt 配置。十、避坑清单序号坑正确做法1不配checkpointer必须InMemorySaver()或持久化 checkpointer2resume 时换了thread_id必须用同一个config3多个待审批只传 1 个 decisiondecisions数量与action_requests一致4用respond表示「拒绝」拒绝用reject5reject不写 message敏感操作建议写清「勿重试」6以为没写 interrupt_on 就不能 HITLpermissions modeinterrupt也会自动装 HITL7CompiledSubAgent 期望继承主 agent interrupt_on在子图内部自己配8不用versionv2读interrupts/ resume 建议用 v2 API9interrupt 后程序直接结束必须循环interrupts→Command(resume...)→ 再 invoke10edit时漏edited_action.name必须包含工具名 完整 args十一、一张表总结11.1 我想实现 X → 怎么配需求配置方式某个自定义工具调用前要审批interrupt_on{tool_name: True}只允许批准/拒绝不许改参数{allowed_decisions: [approve, reject]}仅部分参数/路径才审批加when谓词敏感目录写入要审批FilesystemPermission(..., modeinterrupt)子 agent 读文件也要审批SubAgent 字典里单独配interrupt_on中断后继续执行Command(resume{decisions: [...]}) 同一config11.2 核心 API 速查API作用interrupt_on{...}声明哪些工具要人工审批checkpointer...保存暂停状态必须config{configurable: {thread_id: ...}}会话线程 IDagent.invoke(..., versionv2)标准调用result.interrupts判断是否暂停Command(resume{decisions: [...]})提交审批结果并恢复官方文档Human-in-the-loophttps://docs.langchain.com/oss/python/deepagents/human-in-the-loopPermissions interrupt与 interrupt_on 合并https://docs.langchain.com/oss/python/deepagents/permissions