快速结论:该问题不是运行时报错,而是 InterruptOnConfig.when 的 docstring 与实际实现不一致。文档仍描述已移除的 "batch"/"per_call" 双模式,但实际代码只有一个 after_model 路径。优先排查你阅读的文档版本与安装的 langchain 版本是否匹配。
适用环境:langchain 包(非 langchain-community),涉及 HumanInTheLoopMiddleware 及 InterruptOnConfig。Issue 未确认操作系统、Python、CUDA、显卡等环境信息。
最快修复方案:暂无确认的一步修复方案。若你是下游使用者,请以当前安装版本的源码实现为准,忽略 docstring 中关于 "batch" / "per_call" 模式及 wrap_tool_call 钩子的描述。
注意事项:本问题属于文档与实现不一致;Issue 中已有人认领修复但尚未确认合并。若你计划编写依赖 when 谓词的代码,注意 runtime.tools 在当前路径下始终为空列表,不要依赖其判断可用工具。
问题场景
用户在使用 LangChain 的 HumanInTheLoopMiddleware 编写人工审批中间件时,阅读 InterruptOnConfig.when 的 docstring,发现其中描述的两种模式("batch" 和 "per_call")以及请求结构(request.runtime 为节点级 Runtime、wrap_tool_call 钩子)与实际的合并实现完全不一致。
报错原文
HumanInTheLoopMiddleware `when` docstring describes nonexistent modes and incorrect runtime fields
The docstring for InterruptOnConfig.when currently describes two modes and a request shape that do not match the merged implementation.
Actual behavior:
- The merged HumanInTheLoopMiddleware exposes no `interrupt_mode` option and implements no `wrap_tool_call` hook.
- There is no `interrupt_mode` parameter or other public mode selector. There is one relevant path: `after_model` iterates over tool calls, evaluates `_should_interrupt`, collects matching calls, and creates one batched HITL request.
- `runtime.tools` is always empty in this path.
原因分析
可能原因:在功能合并过程中,HumanInTheLoopMiddleware 的实现从”批次/逐调用”双模式演进为单一的 after_model 批处理路径,但 InterruptOnConfig.when 的 docstring 未同步更新,仍保留旧版设计描述。此外,_should_interrupt 在构造 ToolRuntime 时未填充 tools 字段,导致该字段在此路径下恒为空列表。
环境排查
- 确认 langchain 版本:检查安装版本是否包含合并后的
HumanInTheLoopMiddleware(参考 #37579 及 commit36be77b0f121f737b3ecfcec13238504f438f1a6)。 - 检查源码中
HumanInTheLoopMiddleware.__init__是否仅接受interrupt_on参数(无interrupt_mode参数)。 - 若你引用的是在线文档,核对文档对应版本与本地安装版本是否一致。
解决步骤
- 不修改代码的情况下,若你只是阅读文档而困惑,请直接查看本地安装包中
middleware.py的源码实现,以源码为准。 - 若你已编写自定义
when谓词,请检查是否依赖了request.runtime.tools的内容判断;当前实现中该字段恒为空,请改为基于request.runtime.tool_call_id或消息内容做判断。 - 若你需要向官方反馈,可关注 #40139 的进展;Issue 评论中维护者已同意修正 docstring 并补充请求结构的测试覆盖。
验证方法
运行以下最小复现示例(来自 Issue),确认 request.runtime 是 ToolRuntime、request.tool 为 None、request.runtime.tool_call_id 等于当前调用的 id、request.runtime.tools 为空列表。若以上断言通过,说明你的实现与合并后的行为一致,docstring 与代码不一致是文档本身的问题:
from langchain.agents.middleware import HumanInTheLoopMiddleware, InterruptOnConfig
from langchain_core.messages import AIMessage
from langgraph.prebuilt.tool_node import ToolRuntime
from langgraph.runtime import Runtime
captured = {}
def capture_request(request):
captured["request"] = request
return False
middleware = HumanInTheLoopMiddleware(
interrupt_on={
"test_tool": InterruptOnConfig(
allowed_decisions=["approve"],
when=capture_request,
)
}
)
state = {
"messages": [
AIMessage(
content="",
tool_calls=[
{"name": "test_tool", "args": {"value": 42}, "id": "tc-1", "type": "tool_call"}
],
)
]
}
assert middleware.after_model(state, Runtime()) is None
request = captured["request"]
assert request.tool is None
assert isinstance(request.runtime, ToolRuntime)
assert request.runtime.tool_call_id == "tc-1"
assert request.runtime.tools == []
参考来源
AI 工具推荐
想把多个 AI 模型放在一个入口?
GamsGo AI 集成 ChatGPT、DeepSeek、Gemini、Claude、Midjourney、Veo 等常用模型,适合写作、绘图、视频和日常 AI 工作流。
推广链接:通过此链接购买,我可能获得佣金,不影响你的价格。
这个方案解决了吗?
可以继续搜索完整报错,或查看同一工具的其他排查指南。


