引言
随着大语言模型(LLM)在复杂任务编排中的应用越来越广泛,一个核心矛盾逐渐浮现:智能代理(Agent)的自主性与安全性/准确性之间如何平衡? 当代理可以自主调用 API、修改数据库、发送邮件时,一次幻觉就可能造成不可逆的业务损失。传统的做法是让用户在外层"确认执行",但这打断了代理工作流的连贯性,也失去了决策透明的机会。
LangChain 作为当下最主流的 LLM 应用框架,在其最新的 Middleware 体系 中提供了一个优雅的解决方案------ HumanInTheLoopMiddleware 。它不是粗暴地全局开关,而是在代理的工具调用、消息生成等关键节点注入人类判断,让监督成为工作流的一部分。通过本篇文章,你将深入理解这一中间件的设计哲学、核心原理、代码集成方式,以及它真正发力的业务场景。
关键字
- LangChain
- Human-in-the-Loop (HITL)
- Middleware
- Agent 工作流
- 工具调用审批
- 人类反馈
文章目录
- [1. HumanInTheLoopMiddleware 核心解读](#1. HumanInTheLoopMiddleware 核心解读)
- [2. 代码实战:从零集成 HumanInTheLoopMiddleware](#2. 代码实战:从零集成 HumanInTheLoopMiddleware)
- [2.1 环境准备与依赖安装](#2.1 环境准备与依赖安装)
- [2.2 创建 Agent 并挂载中间件](#2.2 创建 Agent 并挂载中间件)
- [3. 典型使用场景与最佳实践](#3. 典型使用场景与最佳实践)
- [3.1 高风险工具调用审批](#3.1 高风险工具调用审批)
- [3.2 模型生成内容的二次审核](#3.2 模型生成内容的二次审核)
- [3.3 多级审批与组合中间件](#3.3 多级审批与组合中间件)
1. HumanInTheLoopMiddleware 核心解读
在 LangChain 的设计中,Middleware 是插入在 Agent 执行流每个步骤(step)前后的钩子(hook)。HumanInTheLoopMiddleware 则专门负责在某些关键步骤完成后、进入下一步之前,暂停并等待人类输入或审批。
这与传统的 "prompt 后直接人工确认" 有本质区别:
- 细粒度控制 :不是整个对话流程停止,而是只阻塞特定工具调用(如
DELETE、TRANSFER)或特定意图的消息。 - 状态透明:人类决策者可以看到当前 Agent 的意图、待执行的参数以及上下文,做出更准确的判断。
- 可编程回路 :审批结果(批准、修改、拒绝并替换行为)可以无缝注入回 Agent 的后续推理中,形成真正的 Human--Agent 协作闭环。
该中间件的核心机制依赖于一个 interrupt_on 函数 ,你可以自定义在什么条件下触发中断。例如:当 Agent 准备调用 send_email 工具时,中间件捕获该调用,将调用参数渲染给用户,并等待用户输入 approve、reject 或提供修改指令。只有收到明确指令后,Agent 才继续执行。
这种模式让 LangChain Agent 从"全自动黑箱"转变为"可监督的协作者",是通往生产级应用的关键一步。
2. 代码实战:从零集成 HumanInTheLoopMiddleware
下面我们通过一个完整的示例,展示如何在 LangChain 中为一个 Agent 挂载 HumanInTheLoopMiddleware,并实现"高风险工具调用前必须人工审批"的逻辑。
2.1 环境准备与依赖安装
确保你已经安装了 LangChain 的最新版本(示例基于 langchain ≥ 0.3 且包含 middleware 模块)。
2.2 创建 Agent 并挂载中间件
我们首先定义一个包含两个工具的基础 Agent:一个安全的 your_read_email_tool 函数,和一个有风险的 your_send_email_tool 函数,示例将对有风险的 your_send_email_tool 函数进行HITL操作。
python
from langchain_openai import ChatOpenAI
from langchain.tools import tool
from langchain.agents import create_openai_agent, AgentExecutor
from langchain_experimental.agents.middleware import HumanInTheLoopMiddleware
from langgraph.checkpoint.memory import InMemorySaver
# 模拟工具
@tool
def your_read_email_tool(email_id: str) -> str:
"""读取邮件(低风险,无需人工)"""
return f"邮件{email_id}内容:这是一封通知邮件"
@tool
def your_send_email_tool(recipient: str, subject: str, body: str) -> str:
"""发送邮件(高危,必须人工审批)"""
return f"已发送至{recipient},主题:{subject}"
tools = [your_read_email_tool, your_send_email_tool]
# 主模型
llm = ChatOpenAI(model="gpt-4o", temperature=0)
# 会话存储(中断后保存状态必备)
checkpointer = InMemorySaver()
# 人工介入中间件配置
hitl_mw = HumanInTheLoopMiddleware(
interrupt_on={
"your_send_email_tool": {
"allowed_decisions": ["approve", "edit", "reject"],
# 可选:自定义弹窗提示文案
"prompt": "即将发送对外邮件,请人工审核:确认/修改内容/拒绝发送"
},
"your_read_email_tool": False, # 不拦截
}
)
# 创建Agent,必须传入checkpointer
agent = create_openai_agent(
llm=llm,
tools=tools,
checkpointer=checkpointer,
middleware=[hitl_mw]
)
agent_executor = AgentExecutor(agent=agent, tools=tools, verbose=True)
# 会话ID,用于持久化中断状态
config = {"configurable": {"thread_id": "thread_001"}}
# 触发高危工具,流程会暂停等待人工输入
result = agent_executor.invoke(
{"input": "给客户发送一封合作通知邮件"},
config=config
)
print(result["output"])
核心参数解析:

实际开发中,你可以将 agent.invoke 的返回状态传递给前端 UI,前端将用户的"批准/拒绝/修改"按钮操作转化为结构化的审批消息,然后调用 agent.resume 继续执行。这样就在不破坏 Agent 流程完整性的前提下,实现了人类的精准介入。
3. 典型使用场景与最佳实践
3.1 高风险工具调用审批
这是最直观的场景:Agent 被赋予了修改数据库、发送生产邮件、操作 CI/CD 等高风险能力。通过 HumanInTheLoopMiddleware,你可以在代码中集中定义一份"高风险操作白名单",任何不在白名单中的工具调用都必须经过人工审批。这比在 prompt 里反复强调"不可以擅自删除"更可靠。
3.2 模型生成内容的二次审核
不仅仅是工具调用,LangChain 的中间件同样可以在模型生成最终回复前 提供干预点。例如,在客服场景中,当 Agent 生成了包含退款承诺的回复时,你可以拦截并交由值班人员确认,避免幻觉导致的过度承诺。实现时只需在中断函数中检查 action.type 是否为 "generate" 或检查即将输出的消息内容。
3.3 多级审批与组合中间件
LangChain 的 Middleware 是可堆叠的 。你可以组合多个 HumanInTheLoopMiddleware 实例,或者将其与其他中间件(如 ToolRetryMiddleware、SummarizationMiddleware)一起使用。例如:第一个中间件只在"金额 > 1000"时请求组长审批,第二个中间件在"金额 > 10000"时请求总监审批。通过分层的中间件配置,你可以构建出契合组织结构的审批链。
最佳实践提示 :在生产环境中,尽量避免让
interrupt_function中包含会阻塞很久的等待(比如直接在里面调用input())。应该使其返回一个信号,由外层的执行引擎处理异步审批,以保持 Agent 实例本身是可恢复的、无状态的。