news 2026/6/26 7:36:30

DeepAgents 之interrupt_on人工审批

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
DeepAgents 之interrupt_on人工审批

目录

  • 一、30 秒看懂 interrupt_on
  • 二、核心概念
  • 三、易混淆对比(重点)
  • 四、interrupt_on 配置详解
  • 五、四种人工决策类型
  • 六、条件中断 when 谓词
  • 七、中断后的处理流程
  • 八、子智能体与 interrupt_on
  • 九、实战示例
  • 十、避坑清单
  • 十一、一张表总结

一、30 秒看懂 interrupt_on

interrupt_on = 「哪些工具调用必须先经过人工审批」的配置表 Agent 准备调用敏感工具 → 暂停(interrupt)→ 等人 approve/edit/reject/respond → 用 Command(resume=...) 恢复 → Agent 继续跑

类比:

角色对应
Agent想执行操作的员工
interrupt_on敏感操作清单
人工审批主管点头 / 改参数 / 拒绝
Command(resume=...)把审批结果交回系统

二、核心概念

2.1 interrupt_on 是什么

interrupt_oncreate_deep_agent的一个参数:

interrupt_on:dict[str,bool|InterruptOnConfig]|None=None

含义:工具名 → 是否/如何中断的映射。

配置了 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✅ 必须保存暂停时的图状态,否则无法 resume
thread_id✅ 必须同一对话线程,resume 时要与首次 invoke 相同
version="v2"✅ 推荐读取result.interrupts、resume 的标准写法
HumanInTheLoopMiddleware自动有 interrupt_on 时框架自动安装,无需手写

三、易混淆对比(重点)

3.1 interrupt_on vs permissions mode=“interrupt”

两者都能「暂停等人」,但入口和粒度不同:

对比项interrupt_onpermissions+mode="interrupt"
配置方式工具名配置路径 + 操作类型配置
作用对象任意工具(自定义 + 内置)仅内置文件系统工具
粒度整个工具(可用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/**"],mode="interrupt")]# 方式 C:两者并存,同工具名时 interrupt_on 参数覆盖 permissions 生成的配置

3.2 approve / edit / reject / respond

决策含义工具是否执行典型用途
approve原样执行✅ 执行确认 Agent 提案
edit改参数后执行✅ 执行(改过的 args)改收件人、改路径
reject拒绝执行❌ 不执行,返回拒绝消息给 Agent禁止删文件、禁止发信
respond人直接充当工具返回结果❌ 不执行,把人说的话当 ToolMessageask_user类工具

易错点:

错误用法正确理解
delete_filerespond表示「拒绝」拒绝用rejectrespond是「人代替工具回答」
reject不写message可以,但有默认文案;敏感操作建议写清楚「勿重试」

3.3 interrupt_on 的 True / False / InterruptOnConfig

配置值含义
True开启中断;默认允许 approve、edit、reject、respond
False显式关闭该工具的中断(即使父级/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长度 = 1
Agent 一次调多个敏感工具合并为一次 interruptaction_requests有多项
resume 时decisions列表顺序必须与 action_requests 一致

3.5 interrupt_on vs 工具内 interrupt()

对比项interrupt_on工具内interrupt()
配置位置create_deep_agent(interrupt_on=...)自定义工具函数内部
触发点工具即将被调用前工具执行过程中任意位置
数据结构action_requests+review_configs自定义 payload
resume 格式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 参数签名

agent=create_deep_agent(model="qwen-plus",tools=[my_tool,...],interrupt_on={"tool_name":True|False|InterruptOnConfig,},checkpointer=InMemorySaver(),# 必须)

4.2 InterruptOnConfig 字段

字段类型说明
allowed_decisionslist[str]可选:approve/edit/reject/respond
whenCallable[[ToolCallRequest], bool]返回True才中断;False自动放行

4.3 与 Middleware 的关系

interrupt_on 非空(或与 permissions interrupt 合并后非空) ↓ 自动追加 HumanInTheLoopMiddleware(栈尾部) ↓ 工具调用前检查是否 interrupt ↓ 若中断后未 resume,PatchToolCallsMiddleware 会在下次启动前修复悬空 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":"team@corp.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。"""path=request.tool_call["args"].get("file_path","")returnnotpath.startswith("/workspace/")agent=create_deep_agent(model="qwen-plus",interrupt_on={"write_file":{"allowed_decisions":["approve","edit","reject"],"when":writes_outside_workspace,},},checkpointer=InMemorySaver(),)
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())}}# ① 首次调用result=agent.invoke({"messages":[{"role":"user","content":"删除 temp.txt"}]},config=config,version="v2",)# ② 检查中断ifresult.interrupts:iv=result.interrupts[0].value action=iv["action_requests"][0]review={c["action_name"]:cforciniv["review_configs"]}print("待审批工具:",action["name"])print("参数:",action["args"])print("允许决策:",review[action["name"]]["allowed_decisions"])# ③ 人工决策后恢复(config 必须相同!)result=agent.invoke(Command(resume={"decisions":[{"type":"approve"}]}),config=config,version="v2",)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]]result=agent.invoke(Command(resume={"decisions":decisions}),config=config,version="v2")

规则: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_on

8.1 SubAgent 覆盖主 agent 配置

agent=create_deep_agent(model="qwen-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:读也要审批(覆盖继承)},}],checkpointer=InMemorySaver(),)

8.2 继承规则表

场景interrupt_on 来源
SubAgent 未配interrupt_on继承主 agent + permissions 合并结果
SubAgent 配了interrupt_on完全使用子 agent 自己的,不合并主 agent
general-purpose 子智能体继承主 agent 的 interrupt_on

子 agent 触发 interrupt 时,处理方式与主 agent 相同:检查result.interruptsCommand(resume=...)恢复。


九、实战示例

示例 1:最小可运行(自定义工具 + interrupt_on)

importosfromuuidimportuuid4fromdeepagentsimportcreate_deep_agentfromlangchain.chat_modelsimportinit_chat_modelfromlangchain.toolsimporttoolfromlanggraph.checkpoint.memoryimportInMemorySaverfromlanggraph.typesimportCommand model=init_chat_model(model="qwen-plus",model_provider="openai",api_key=os.getenv("ali_api_key"),base_url="https://dashscope.aliyuncs.com/compatible-mode/v1",temperature=0,)@tooldefdelete_file(path:str)->str:"""删除文件。"""returnf"已删除{path}"agent=create_deep_agent(model=model,tools=[delete_file],interrupt_on={"delete_file":True},checkpointer=InMemorySaver(),)config={"configurable":{"thread_id":str(uuid4())}}result=agent.invoke({"messages":[{"role":"user","content":"删除 /tmp/a.txt"}]},config=config,version="v2",)ifresult.interrupts:action=result.interrupts[0].value["action_requests"][0]print(f"待审批:{action['name']}{action['args']}")ok=input("批准?y/n:").strip().lower()=="y"decision={"type":"approve"}ifokelse{"type":"reject","message":"用户拒绝删除"}result=agent.invoke(Command(resume={"decisions":[decision]}),config=config,version="v2")print(result.value["messages"][-1].content)

示例 2:限制 allowed_decisions

agent=create_deep_agent(model=model,tools=[delete_file,notify_email],interrupt_on={"delete_file":{"allowed_decisions":["approve","reject"]},# 不许改参数"notify_email":True,},checkpointer=InMemorySaver(),)

示例 3:条件中断(仅危险路径)

fromlangchain.tools.tool_nodeimportToolCallRequestdefis_production_path(req:ToolCallRequest)->bool:path=req.tool_call["args"].get("file_path","")returnpath.startswith("/prod/")agent=create_deep_agent(model=model,interrupt_on={"write_file":{"allowed_decisions":["approve","edit","reject"],"when":is_production_path,},},checkpointer=InMemorySaver(),)

示例 4:与 permissions interrupt 配合(敏感目录写入)

fromdeepagentsimportFilesystemPermission,create_deep_agentfromdeepagents.backendsimportFilesystemBackend agent=create_deep_agent(model=model,backend=FilesystemBackend(root_dir=".",virtual_mode=True),permissions=[FilesystemPermission(operations=["write"],paths=["/secrets/**"],mode="interrupt"),],# 无需再写 interrupt_on={"write_file": True},框架会自动合并checkpointer=InMemorySaver(),)

等价于:对/secrets/**write_file/edit_file自动生成带when谓词的 interrupt 配置。


十、避坑清单

序号正确做法
1不配checkpointer必须InMemorySaver()或持久化 checkpointer
2resume 时换了thread_id必须用同一个config
3多个待审批只传 1 个 decisiondecisions数量与action_requests一致
4respond表示「拒绝」拒绝用reject
5reject不写 message敏感操作建议写清「勿重试」
6以为没写 interrupt_on 就不能 HITLpermissions mode="interrupt"也会自动装 HITL
7CompiledSubAgent 期望继承主 agent interrupt_on在子图内部自己配
8不用version="v2"interrupts/ resume 建议用 v2 API
9interrupt 后程序直接结束必须循环:interruptsCommand(resume=...)→ 再 invoke
10edit时漏edited_action.name必须包含工具名 + 完整 args

十一、一张表总结

11.1 我想实现 X → 怎么配?

需求配置方式
某个自定义工具调用前要审批interrupt_on={"tool_name": True}
只允许批准/拒绝,不许改参数{"allowed_decisions": ["approve", "reject"]}
仅部分参数/路径才审批when谓词
敏感目录写入要审批FilesystemPermission(..., mode="interrupt")
子 agent 读文件也要审批SubAgent 字典里单独配interrupt_on
中断后继续执行Command(resume={"decisions": [...]})+ 同一config

11.2 核心 API 速查

API作用
interrupt_on={...}声明哪些工具要人工审批
checkpointer=...保存暂停状态(必须)
config={"configurable": {"thread_id": ...}}会话线程 ID
agent.invoke(..., version="v2")标准调用
result.interrupts判断是否暂停
Command(resume={"decisions": [...]})提交审批结果并恢复

📚官方文档

  • Human-in-the-loop:https://docs.langchain.com/oss/python/deepagents/human-in-the-loop
  • Permissions interrupt(与 interrupt_on 合并):https://docs.langchain.com/oss/python/deepagents/permissions
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/6/26 7:35:25

如何一键解决Windows运行库缺失问题:VisualCppRedist AIO完整指南

如何一键解决Windows运行库缺失问题:VisualCppRedist AIO完整指南 【免费下载链接】vcredist AIO Repack for latest Microsoft Visual C Redistributable Runtimes 项目地址: https://gitcode.com/gh_mirrors/vc/vcredist 你是否曾经遇到过这样的情况&#…

作者头像 李华
网站建设 2026/6/26 7:32:49

离婚证公证可以代办吗?离婚证公证要几天能拿到?

很多小伙伴在办理离婚证公证时会遇到各种难题:人在异地回不了户籍地、身在国外无法回国、线下公证处排队耗时长……到底离婚证公证能不能代办?办理周期要多久?今天一文给你讲清楚,并推荐一个超实用的线上办理平台——慧办好公证小…

作者头像 李华
网站建设 2026/6/26 7:32:19

AI 庆泰电动袜机智能功率 MOSFET 完整选型方案

2026年随着 AI 技术在袜机控制系统中的深度渗透(如智能花型切换、张力自适应调节、断纱预测),电动袜机对功率 MOSFET 提出更高要求:高频化、低损耗、高可靠性。微碧半导体(VBsemi)基于多外延超结、SGT 及 T…

作者头像 李华
网站建设 2026/6/26 7:27:52

域名代购交易是否需要签订正规服务合同?

域名代购交易建议必须签订正规服务合同,尤其是精品域名、品牌域名、海外域名以及高金额交易,更不能省略。因为域名代购不是普通商品购买,而是带有明显“委托经纪”属性的交易服务,涉及委托权限、报价范围、服务费、成交佣金、资金…

作者头像 李华
网站建设 2026/6/26 7:27:26

开源中国完成数亿元 C 轮融资:Gitee 加速智能化研发效能革新

开源技术生态领军企业开源中国(开源共识〈上海〉网络技术有限公司)近日宣布完成数亿元 C 轮融资。本轮融资将进一步推动 Gitee 在企业级研发效能领域的创新,加速 AI DevSecOps 平台建设。一、 融资概况与股东结构 1. 本轮融资信息 融资轮次&a…

作者头像 李华
网站建设 2026/6/26 7:25:16

企业总部泛光照明落地指南:避开设计施工脱节的核心思路

很多企业总部、城市地标项目在启动外立面照明建设时,都会遇到两个核心问题:不知道照明工程公司有哪些,筛选泛光照明施工公司时没有明确的判断标准,投入大量精力招标后依然容易出现设计效果还原度低、后期运维成本高、能耗超标等问…

作者头像 李华