LangChain HumanInTheLoopMiddleware:为智能代理嵌入人类判断

引言

随着大语言模型(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 后直接人工确认" 有本质区别:

  • 细粒度控制 :不是整个对话流程停止,而是只阻塞特定工具调用(如 DELETETRANSFER)或特定意图的消息。
  • 状态透明:人类决策者可以看到当前 Agent 的意图、待执行的参数以及上下文,做出更准确的判断。
  • 可编程回路 :审批结果(批准、修改、拒绝并替换行为)可以无缝注入回 Agent 的后续推理中,形成真正的 Human--Agent 协作闭环

该中间件的核心机制依赖于一个 interrupt_on 函数 ,你可以自定义在什么条件下触发中断。例如:当 Agent 准备调用 send_email 工具时,中间件捕获该调用,将调用参数渲染给用户,并等待用户输入 approvereject 或提供修改指令。只有收到明确指令后,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 实例,或者将其与其他中间件(如 ToolRetryMiddlewareSummarizationMiddleware)一起使用。例如:第一个中间件只在"金额 > 1000"时请求组长审批,第二个中间件在"金额 > 10000"时请求总监审批。通过分层的中间件配置,你可以构建出契合组织结构的审批链。

最佳实践提示 :在生产环境中,尽量避免让 interrupt_function 中包含会阻塞很久的等待(比如直接在里面调用 input())。应该使其返回一个信号,由外层的执行引擎处理异步审批,以保持 Agent 实例本身是可恢复的、无状态的。

相关推荐
sunywz1 小时前
【AI RAG知识库】05.【导入】【节点5】node_item_name_recognition
人工智能
彩云回1 小时前
长文档处理(Long Document Processing)策略
人工智能
AIGS0011 小时前
企业AI落地:从RAG到AgentRAG的技术跃迁
java·人工智能·人工智能ai大模型应用
程序员cxuan1 小时前
Claude Code 焚诀(一):Claude Code 六种心智模型
人工智能·后端
FlagOS智算系统软件栈1 小时前
FlagOS技术工作坊:当AI开始“自己部署自己”,大模型基础设施进入“无人驾驶”时代
人工智能
一楼的猫2 小时前
AI写作检测机制技术深度解析:200+维度检测、叙事指纹与网文AI辅助怎么过审
人工智能·学习·安全·chatgpt·ai写作
心中有国也有家2 小时前
鸿蒙 Flutter 本地存储实战:Hive CE 从入门到精讲
人工智能·hive·flutter·华为·harmonyos
雪隐2 小时前
用Flutter做背单词APP-01服务器?不存在的,我家里就有
前端·人工智能·后端
仙逆GPT2 小时前
使用 Codex 处理项目时,怎样减少无关修改和重复返工?
人工智能·chatgpt·codex
Web极客码2 小时前
跨国网络抖动下的 AI 爬虫流调优:我如何用 Claude 打造“智能退避”资讯清洗管道
网络·人工智能·爬虫