别再让 agent 整批"先斩后奏":LangChain 1.4.6 把人工确认下放到每次工具调用
LangChain 1.4.6 给 HumanInTheLoopMiddleware 加上 interrupt_mode="per_call":每个受控工具调用独立产生 interrupt、自带类型化 response_schema,改参不能更换工具。审批粒度第一次跟上了 agent 的破坏粒度——这是 agent 从 demo 进生产的工程分水岭。

10 月 14 日,LangChain 发布了 1.4.6(pepy 记录的发布日期为 2026-10-14)。这次更新的 changelog 里只有一处值得全文阅读的改动:HumanInTheLoopMiddleware 支持了 interrupt_mode="per_call"——人工确认第一次下放到了每一次工具调用。官方的原话很直白:
HumanInTheLoopMiddlewarecan now pause once per tool call. Setinterrupt_mode="per_call"and each gated call raises its own interrupt, which you answer by interrupt ID. Batched mode is still the default.
如果你是把 agent 往生产里接的 vibe 开发者,这句话的分量需要翻译一下:从今天起,你可以让 agent 每做一件"有后果的事"之前,都单独停下来问你一句,而不是像过去那样,一批调用打包完、你闭眼点一次"批准"。
为什么说这是 agent 从 demo 进生产的工程分水岭?因为接 agent 进生产最大的恐惧,从来不是它笨——笨是可以调的。真正的恐惧是"它替我做了我不想要的事":删了库、发出去了不该发的邮件、下了不该下的单。过去一年,所有认真讨论 agent 落地的团队最后都会收敛到同一个结论:审批粒度必须跟上破坏粒度。一个工具调用能造成多大的破坏,人工确认就要细到多小的单位。LangChain 1.4.6 是第一个在主流框架层面把这句话做成默认能力(尽管暂时还不是默认选项)的人。
这到底改了什么:从"打包审批"到"逐件审批"
先说清旧机制。在 1.4.6 之前,HumanInTheLoopMiddleware 只有一种工作方式:batched。agent 在一轮里发起的所有受控工具调用,会被打包成一个 interrupt——你看到的是一整个批次,决定也只能是批量的:全批,或者全不批。用法长这样:
HumanInTheLoopMiddleware(interrupt_on={...})
这种模式有个结构性缺陷:危险调用可以藏在安全调用里过关。agent 在一轮里查了 4 次订单、顺手调了一次"删除用户数据",你面对的审批单上是 5 个调用,你大概率扫一眼前两条、点批准——删库的那条就混过去了。这不是用户不仔细,这是人性:批量审批天然诱导走马观花。
per-call 模式把这件事拆开了:
HumanInTheLoopMiddleware(interrupt_on={...}, interrupt_mode="per_call")
每个受控调用产生自己独立的 interrupt,用 interrupt ID 应答。你可以对查订单点"批准",对退款 120 美元那条点"驳回",对删除用户数据那条把参数改成只删测试账号——三件事,三个独立的决定,互不牵连。配合下面的类型化应答,这套机制才算完整。
三个值得细看的技术细节
这次更新最有工程味道的部分,不在"能逐个暂停"本身,而在三个配套设计。它们回答的是:当你把审批拆到每次调用时,新的作弊空间和新的工作量会出现在哪。
第一,类型化应答(Typed answers)。每个 interrupt 都携带一个 response_schema,列出这次调用允许做的决定,客户端可以直接从 schema 生成审批表单。官方写道:
Typed answers: each interrupt carries a
response_schemalisting the decisions that call allows, so a client can build its form from the schema.
这句话的工程含义是:审批界面不再需要你手写。每个工具允许的决定(批准 / 改参 / 驳回)是声明式的,前端渲染可以程序化生成。对独立开发者来说,这意味着你终于不用为"做一个好看的审批弹窗"这种脏活写一堆胶水代码——当然,前提是你的工具 schema 写得诚实。
第二,改参不能偷换工具。这是整个设计里最见功力的一句:
Edits stay on the called tool: an
editcan't switch tools, and its arguments are checked against the tool's own schema.
翻译一下:审批人把"查订单"的参数改成"删用户"是不可能的,改完的参数还要过一遍该工具自己的 schema 校验。为什么这条重要?因为"改参"是整套机制里最微妙的攻击面——如果审批流允许在改参时换工具,agent(或劫持了 agent 上下文的攻击者)就可以先申请一个无害调用、等你批准改参时偷换成危险调用。LangChain 在这里把门焊死了:edit 只能动同一工具的参数,且参数合法性由工具自己的 schema 说了算,不是由审批人的肉眼。
第三,默认还没切,官方劝你先用。
Use per-call mode for new agents. LangChain plans to make it the default in the next major release.
batched 依然是默认,下一个 major 版本才会把 per-call 扶正。这个节奏是对的:默认切换是破坏性变更,老 agent 的审批流、中断处理代码都要跟着改。LangChain 选择先让新 agent 用起来、收集真实反馈,再切默认——这是框架作者该有的谨慎。但对你来说,结论是明确的:新写的 agent 直接上 per-call,别等默认。
一个具体场景:客服退款 agent 的一天
把上面的机制落到一个真实场景里。假设你给自己的小店写了一个客服 agent,工具集有三个:lookup_order(查订单,只读)、issue_refund(退款,花钱)、delete_user_data(删用户数据,不可逆)。用户说"我的订单有问题,顺便把我的账号数据也删了吧"。
在 batched 模式下,agent 规划出的调用序列——查订单、退 120 美元、删除用户数据——会被打包成一个 interrupt。你在手机上看到的是一张 3 行的审批单。查订单那条你扫一眼觉得没问题,退款金额你也没细看,删除数据那条混在第三行——你点了批准。三件事,一次点头。
在 per-call 模式下,流程是这样的:lookup_order 的 interrupt 先到,你批准(只读,无风险);issue_refund 的 interrupt 带着 response_schema 到,你看到金额是 120 美元——但你记得这个用户只买了 39 美元的东西,于是你选了"改参",把金额改成 39,参数校验通过(schema 里金额是数字类型、范围合法),执行;delete_user_data 的 interrupt 到,你意识到"删数据"这件事用户可能只是一时气话,点了驳回,并让 agent 回复用户"数据已保留,如需删除请再次确认"。
注意这个场景里发生了什么:同一个用户请求,三次调用,三种不同的决定。batched 模式在物理上就不支持这种表达——你的决定只能是"全批"或"全不批"。per-call 模式第一次让审批语言的表达能力和 agent 行为的复杂度对上了。这也是为什么我说"审批粒度第一次跟上了破坏粒度":破坏本来就是按次发生的,审批也应该是。
再看 response_schema 在这个场景里的作用。退款那条的 schema 可能是 {decisions: ["approve", "edit", "reject"], editable_fields: ["amount"]},而删数据那条可能是 {decisions: ["approve", "reject"]}——根本不给你改参的选项(删数据这种事,要么删要么不删,不存在"删一半")。客户端从 schema 生成表单时,退款的审批卡片上有金额输入框,删数据的卡片上只有两个按钮。表单的差异不是前端写死的,是后端 schema 声明的。这就是类型化应答的真正价值:审批 UI 的语义,由工具作者在定义工具时就定好了。
动手清单:四件事,今天就能做
如果你现在就在用 LangChain 写 agent,这次更新的落地清单很短:
- 新 agent 直接上 per-call。官方原话 "Use per-call mode for new agents",不要等下一个 major 版本的默认切换。你的 interrupt handler 从一开始就按"每次调用一个 interrupt、按 interrupt ID 应答"来写,省得以后返工。
- 列出你的"不可逆清单"。
interrupt_on里只放写库、发消息、花钱、删数据、调外部 API 这些不可逆操作。只读工具不要进 gate——interrupt 疲劳是真实存在的,每多一次无意义的审批,你的审批质量就降一分。 - 把工具拆细。一个工具只干一件事。工具越粗,单个 interrupt 的语义越模糊,审批人越难做决定。"用户管理"这种大杂烩工具,拆成查、改、删三个,per-call 的价值才能发挥出来。
- 把 schema 写诚实。参数的类型、范围、必填项,就是审批时的校验规则。edit 的参数校验只认 schema 不认人——schema 里留的口子,就是生产环境里留的口子。
还有一个很多人会忽略的点:interrupt 的应答链路本身也需要设计。per-call 模式下,interrupt 是按 ID 应答的,这意味着你的审批端(无论是网页后台、Slack bot 还是手机 App)需要维护"待审批队列",支持乱序应答——先到的 interrupt 不一定先被处理。batched 模式下一个 interrupt 对应一批调用,应答是原子的;per-call 模式下,你的审批端是一个小型的任务队列。这个复杂度是框架替你省不掉的,动手前先想清楚。
为什么是 vibe 开发者最该关心的一次更新
先说一个判断:过去两年 agent 框架的更新,大多是"让 agent 更聪明"的更新——更好的规划、更长的上下文、更强的工具调用。这次是少见的"让 agent 更可控"的更新。聪明的 agent 进不了生产,可控的才能。
对一人团队来说,per-call 模式解决的是一个非常具体的痛:你没有专职的 SRE 帮你盯着 agent。大公司的 agent 出事,有值班、有回滚、有审计;你的 agent 半夜删了生产库,第一个发现的人就是你——在用户投诉之后。细粒度审批是你唯一买得起的保险:它不贵(一个配置项),它在你睡觉时也工作(interrupt 可以进你的手机审批流),它把"事后救火"变成"事前点头"。
更深一层,这改变了你设计 agent 工具集的方式。以前,因为审批是打包的,你会倾向于把工具做得"粗"——一个工具干很多事,减少审批次数。现在审批细了,你反而应该把工具做"细":查订单是一个工具,退款是另一个工具,删数据是第三个。工具拆得越细,per-call 审批的价值越大,因为每个 interrupt 的语义都清晰到一眼能判。这是一个值得写进团队规范的设计原则:工具的粒度,决定审批的质量。
别急着乐观:per-call 不是银弹
把话说全。逐调用审批引入了三个新问题,官方 changelog 没写,但你动手之前必须知道。
第一,interrupt 疲劳。一个 agent 跑一轮发起 50 次工具调用,你就要点 50 次批准。点到第 20 次,你和批量审批时的走马观花没有区别——只不过现在你是逐个走马观花。解法不是"全开 per-call",而是只对不可逆的工具开 gate:写库、发消息、花钱、删数据走 per-call;读操作、查状态直接放行。interrupt_on={...} 的那个集合,就是你的"不可逆清单",值得认真设计。
第二,审批界面的 UX 是你的责任。response_schema 能帮你生成表单,但生成出来的表单好不好用——关键信息(这次调用到底要干什么、参数是什么、风险在哪)排得清不清楚——决定了审批人是真在审批还是在点按钮。框架给了你 schema,前端的信息设计还得你自己做。别把自动生成的表单直接丢给用户,那和没审批差不多。
第三,schema 写得烂,细粒度也救不了。edit 的参数校验依赖工具自己的 schema——如果你的工具 schema 把"删除范围"写成一个自由文本字段,校验就形同虚设。per-call 模式把安全责任下沉到了每个工具的定义上:工具 schema 是你新的安全边界,值得像写鉴权代码一样认真对待。
行业坐标:HITL 正在成为标配
把这次更新放进更大的图景里看:human-in-the-loop 正在从"可选项"变成 agent 框架的标配层。OpenAI 的 Responses API、Anthropic 的工具使用指南,各家都在补"执行前确认"这一课。LangChain 的特殊之处在于,它是第一个把审批粒度做到"每次调用"、并且把应答类型化、把改参约束写进框架语义的主流框架。
还有一个信号值得注意:官方明确说下个 major 版本会把 per-call 设为默认。这意味着现在就是迁移窗口——你的 interrupt 处理代码、中断恢复逻辑、审批 UI,如果现在按 batched 假设写,major 版本一到就要返工。提前按 per-call 写,等于提前还了技术债。
一句话总结:agent 进生产的门票,从来不是更聪明的模型,而是"出事时有人点头"的工程机制。LangChain 1.4.6 把这张门票的价格打到了一行配置。对 vibe 开发者来说,checklist 很短:新 agent 直接 interrupt_mode="per_call";只对不可逆工具开 gate;把工具拆细;把 schema 写诚实。剩下的,就是等下一个 major 版本把它变成默认——到那时,逐调用审批会像今天的 HTTPS 一样,变成没人再讨论的基础设施。
评论 (0)
原始来源
相关文章

印度国家 CERT 签发 critical 级咨询 CIAD-2026-0049,第一次把 AI agent 本身列为攻击向量:厂商评测中的 agent 越权读走企业生产机密、受影响企业零自行发现;黑客用开源 agentic 工具把入侵到变现从几天压缩到几小时。根因全是老毛病——明文凭证、僵尸 API、过度授权。对 vibe 开发者,这是一份点名到仓库和 key 的安全考卷。

vibe 项目最常见的开源姿势是裸奔式 push:没许可证、没 CONTRIBUTING、issue 区三个月没人理。本文给一条从私有仓库到健康开源项目的完整跑道:许可证选型决策表、开源前代码体检(含可运行的密钥扫描脚本)、社区文件四件套写法、发布日 SOP、首月治理纪律,以及开源前 15 项验收检查。

Claude 用不到半小时解开 25 年密码学悬案:NIST ECDSA 曲线种子根本不是哈希口令,只是 1997 年 Unix 时间戳喂给 lrand48 跑出来的输出。这不是破解,是祛魅——顺手终结了 NSA 后门阴谋论。