一个 Agent 升级后,文字还能正常输出,工具参数却停在半截;另一个 Agent 能弹出审核窗口,却把批准结果对应到了错误的调用。
这类问题提醒我们:升级框架时,界面与执行协议需要一起检查。
本篇整理 2026-09-24 至 2026-10-23 官方发布的相关变化,重点是 10 月 14 日的逐调用审核,以及 10 月 22 日的工具参数增量协议。
这个月真正需要关注的两项变化
| 发布日期 | 组件 | 变化 | 重点检查 |
|---|---|---|---|
| 10 月 14 日 | langchain 1.4.6 |
新增逐工具调用的审核模式 | 审核决定与中断 ID 的对应关系 |
| 10 月 22 日 | langchain-core 1.7.0 |
工具参数使用 tool-call-delta 事件 |
客户端兼容性与参数组装方式 |
日期与版本来自 官方 Python Changelog。
Deep Agents 的 Overview 是持续更新的能力文档;本窗口内,所查发布记录没有列出新的 Deep Agents 版本,所以不把其已有文件、子 Agent 或 Skills 能力算作本月首发。
1. 为什么“增量”和“快照”不能混着处理?
新事件携带的是参数新增片段,旧协议中的相关事件会重复携带截至当时的参数内容。
官方还指出,从 langchain-core 1.6.13 开始,遍历 message.tool_calls 得到的是新增参数片段;需要完整解析结果时使用 .get()。
图 1:两次调用交错到达时,应分别维护参数状态;数据为编辑构造的示意。
假设服务端先后产生两个工具调用,一个查天气,一个查资料。
如果前端只维护一个字符串,把所有参数片段都追加进去,即使每条事件本身没有问题,最后也会把两次调用拼成一段错误数据。
同样,如果收到的是完整快照却仍用追加逻辑,重复前缀也会破坏结果。
一个可以直接运行的概念实验
下面代码只演示参数组装,不调用模型,也不是完整 LangChain 客户端:
import json
from collections import defaultdict
events = [
("call_A", '{"city":"'),
("call_B", '{"query":"'),
("call_A", '北京"}'),
("call_B", 'LangChain"}'),
]
buffers = defaultdict(str)
for call_id, fragment in events:
buffers[call_id] += fragment
parsed = {key: json.loads(value) for key, value in buffers.items()}
print(parsed)
本地 Python 实际运行得到两份独立参数:call_A 对应北京,call_B 对应 LangChain。
附加检查也确认:把两份累积快照当增量相加,会得到无法解析的 JSON。
真实客户端还要处理命名空间、调用生命周期、结束状态与错误,不能把这段教学代码直接当生产解析器。
参数正在生成,不代表工具已经执行;工具已经执行,也不代表任务最终成功。
建议在界面中分别表示“参数生成中”“等待审核”“执行中”“已完成/失败”,并用真实执行结果更新状态。
从事件到工具执行,中间还有哪些状态?
把链路分成三层更容易定位:模型输出层产生文字和参数,Agent 执行层管理工具与中断,应用展示层把状态呈现给用户。
同一段工具参数可能已经被模型完整生成,却仍在等待审核;也可能工具正在运行,但还没有业务结果。
因此,下面是一份建议的应用状态机,不是 SDK 原生枚举:
collecting_args → args_ready → validated
├─ review_required → approved / rejected
└─ review_not_required
↓
running
┌────────┼────────┐
succeeded failed outcome_unknown
args_ready 只说明收齐参数,validated 说明参数符合工具定义,二者都不应直接显示为“任务已完成”。
发生网络超时时,如果无法判断工具是否已经产生外部效果,应进入 outcome_unknown,先查业务结果,再决定重试。
多 Agent 场景为什么还要保留 namespace?
仅以调用 ID 保存状态,在“同一标识出现于不同子图或运行”的应用设计中可能发生覆盖。
应用应根据实际协议约定保留 run、namespace 和调用标识,避免把不同作用域的片段合并。
下面是编辑设计的归一化事件实验,tuple 不是官方原始事件格式:
import json
from collections import defaultdict
events = [
(("main",), "c1", '{"city":"'),
(("worker",), "c1", '{"q":"'),
(("main",), "c1", '北京"}'),
(("worker",), "c1", 'LangChain"}'),
]
buffers = defaultdict(str)
for namespace, call_id, fragment in events:
buffers[(namespace, call_id)] += fragment
results = {key: json.loads(value) for key, value in buffers.items()}
assert results[(("main",), "c1")] == {"city": "北京"}
assert results[(("worker",), "c1")] == {"q": "LangChain"}
print("two isolated calls passed")
这段代码已在本地运行通过;不完整 JSON 的拒绝与快照替换模式也已单独验证。
它仍有刻意保留的边界:没有实现连接重放去重,也没有假设任意到达顺序都能恢复。
如果协议会重放事件,是否可去重必须依据它提供的事件标识或游标;不能仅凭片段文字相同就删除,因为两个相同片段也可能都是有效内容。
直接消费 SDK 投影,还是自己解析原始事件?
官方区分参数生成投影 message.tool_calls 与工具执行投影 stream.tool_calls,前者关注模型参数,后者关注执行生命周期。
来源:Event Streaming。
普通应用优先使用与版本匹配的高层投影,减少自己维护协议细节的范围;确实需要原始事件时,再明确每个字段的作用域、结束信号与兼容版本。
这里不建议在每个片段到达时反复执行 json.loads 并将异常当作工具失败:半段参数本来就可能不是合法 JSON。
2. 升级顺序:先让客户端听得懂
官方升级说明要求先更新流的消费者,再更新服务端。
其列出的 JavaScript UI 包兼容下限是 1.2.6;Python 消费端 langchain-core 是 1.6.13,之后再升级服务端至 langchain-core 1.7.0,并配合 langchain 1.4.7 或更新版本。
来源:升级顺序与兼容提醒。
在自己的项目里,先列出所有消费事件流的地方:网页、后台任务、SDK 封装和测试工具。
只升级一个主界面,遗漏某个老版本脚本,仍可能出现“后台正常、某个入口不动”的情况。
验证时至少覆盖:无工具回答、单工具调用、多调用交错、工具异常,以及中途断开后的实际恢复策略。
3. 逐调用审核:界面应该认 ID,不应猜顺序
新增的 per_call 模式让每个待审调用产生自己的中断,通过中断 ID 提交决定。
目前 batched 仍是默认模式,Deep Agents 使用的是批量模式,不能假设安装新版本后所有审核行为都会自动切换。
来源:Human-in-the-loop Overview。
图 2:审核粒度发生变化,但执行依赖与事务语义仍需应用负责。
例如,模型提出“读取订单、生成归档、删除旧订单”三项动作。
逐调用审核能让应用更细致地展示待办,但它不会自动保证“归档已经写入成功,才允许删除”。
这个先后约束应由工作流或执行工具检查,不能靠审核卡片在视觉上排在前面来实现。
官方指南还特别提醒:批量提交多个决定时,部分决定可能已经执行而另一些仍待处理;在没有核实状态前整体重交,可能重复执行工具。
来源:逐调用恢复与校验说明。
接入时,需要保存哪几类标识?
最容易混淆的是三个标识:业务任务标识回答“这是哪件事”,thread_id 回答“恢复哪段图状态”,中断 ID 回答“这次决定对应哪个待审位置”。
它们承担不同职责,不应都由一个随机字符串临时替代。
建议的审核记录可以是:
{
"task_id": "archive-orders-2026-10",
"thread_id": "thread-archive-17",
"interrupt_id": "interrupt-42",
"tool_call_id": "call-7",
"tool_name": "write_archive",
"args_revision": "v2",
"review_status": "pending"
}
这是应用层记录示例,不是官方响应的完整结构。
参数修订号的意义是防止审批错版本:用户眼前查看的是 v2,后端不能在参数被另一个步骤改成 v3 后,仍把这次批准套到新的内容上。
暂停之后,究竟如何恢复?
官方说明恢复需要原来的线程,中断节点在恢复时可能从头重跑,因此节点内中断之前的代码必须考虑重复执行。
在概念上,正确顺序是:读取待审状态 → 校验当前用户权限和答案格式 → 提交对应中断的决定 → 从执行状态确认结果。
不要把“审核请求返回 200”直接当成外部动作完成,也不要在刷新后把本地缓存的批准按钮原样恢复。
如果工具需要“先写归档,再删订单”,应让删除工具核对归档结果的业务标识和可读状态,而不是只核对曾经存在过一个批准记录。
恢复失败时怎么查?
| 现象 | 首先核对 | 为什么 |
|---|---|---|
| 恢复后像新任务 | 原 thread_id 与持久化后端 | 线程映射错误会丢失恢复位置 |
| 同名工具批准错对象 | 中断 ID、调用 ID 与参数版本 | 名称不足以区分并发待审调用 |
| 用户拒绝后仍被执行 | 决定类型及策略是否在恢复时改变 | 审核显示与执行策略可能不一致 |
| 重连后动作重复 | 外部结果、执行记录和幂等键 | 状态未知时直接重交可能产生第二次效果 |
| 内存测试能恢复,重启后不行 | 是否用了内存 checkpointer | 进程内状态不等于持久化状态 |
审核界面的小型验收清单
- 同时存在两个同名工具调用时,是否仍能分清对应的参数与中断?
- 用户修改参数后,真正执行的是否是修改后的值?
- 某次调用已经完成,再次刷新页面时是否仍显示可批准?
- 未回答的调用是否保持待审,而不是被当作默认同意?
- 重连后能否依据服务端状态恢复,而不是仅凭本地按钮状态?
这些是本文建议的验收项,不是框架保证的全部边界。
从哪里开始改,收益最直接?
有流式界面的项目,先核对消费端版本和参数拼装逻辑;有审核功能的项目,再检查中断 ID、恢复流程与重复执行。
本篇运行了本地参数、快照和命名空间实验;未调用模型 API,未执行真实审批,也未进行生产升级。
如果你遇到“文本正常但工具卡片不更新”,欢迎附上脱敏版本组合、事件类型与调用生命周期,我们可以一起排查。
Agent 工作流准备进入企业环境?
如果你需要评估人工审核、权限、恢复与重复执行的项目范围,可以在企业项目需求入口说明部署约束、三个验收任务和可投入人员,先明确交付边界,再判断是否需要技术服务。
具体报错仍欢迎在本帖附脱敏复现;需求登记不代表社区已接受项目或承诺交付。


