HumanInTheLoopMiddleware `when` docstring describes nonexistent modes and incorrect runtime fields

该问题不是运行时报错,而是 InterruptOnConfig.when 的 docstring 与实际实现不一致。文档仍描述已移除的 "batch" / "per_call" 双模式,但实际代码只有一个 after_model 路径。优先排查你阅读的文档版本与安装的 langchain 版本是否匹

快速结论:该问题不是运行时报错,而是 InterruptOnConfig.when 的 docstring 与实际实现不一致。文档仍描述已移除的 "batch"/"per_call" 双模式,但实际代码只有一个 after_model 路径。优先排查你阅读的文档版本与安装的 langchain 版本是否匹配。

适用环境:langchain 包(非 langchain-community),涉及 HumanInTheLoopMiddlewareInterruptOnConfig。Issue 未确认操作系统、Python、CUDA、显卡等环境信息。

最快修复方案:暂无确认的一步修复方案。若你是下游使用者,请以当前安装版本的源码实现为准,忽略 docstring 中关于 "batch" / "per_call" 模式及 wrap_tool_call 钩子的描述。

注意事项:本问题属于文档与实现不一致;Issue 中已有人认领修复但尚未确认合并。若你计划编写依赖 when 谓词的代码,注意 runtime.tools 在当前路径下始终为空列表,不要依赖其判断可用工具。

问题场景

用户在使用 LangChain 的 HumanInTheLoopMiddleware 编写人工审批中间件时,阅读 InterruptOnConfig.when 的 docstring,发现其中描述的两种模式("batch""per_call")以及请求结构(request.runtime 为节点级 Runtimewrap_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 及 commit 36be77b0f121f737b3ecfcec13238504f438f1a6)。
  • 检查源码中 HumanInTheLoopMiddleware.__init__ 是否仅接受 interrupt_on 参数(无 interrupt_mode 参数)。
  • 若你引用的是在线文档,核对文档对应版本与本地安装版本是否一致。

解决步骤

  1. 不修改代码的情况下,若你只是阅读文档而困惑,请直接查看本地安装包中 middleware.py 的源码实现,以源码为准。
  2. 若你已编写自定义 when 谓词,请检查是否依赖了 request.runtime.tools 的内容判断;当前实现中该字段恒为空,请改为基于 request.runtime.tool_call_id 或消息内容做判断。
  3. 若你需要向官方反馈,可关注 #40139 的进展;Issue 评论中维护者已同意修正 docstring 并补充请求结构的测试覆盖。

验证方法

运行以下最小复现示例(来自 Issue),确认 request.runtimeToolRuntimerequest.toolNonerequest.runtime.tool_call_id 等于当前调用的 idrequest.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 == []

参考来源

langchain-ai/langchain #40139

GamsGo AI

AI 工具推荐

想把多个 AI 模型放在一个入口?

GamsGo AI 集成 ChatGPT、DeepSeek、Gemini、Claude、Midjourney、Veo 等常用模型,适合写作、绘图、视频和日常 AI 工作流。

了解 GamsGo AI

推广链接:通过此链接购买,我可能获得佣金,不影响你的价格。

这个方案解决了吗?

celebrityanime
celebrityanime
文章: 22184

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注