LangChain 中间件与人工介入:构建可控的智能客服系统
·
1. 引言
在构建基于大模型的智能客服系统时,我们面临两个核心挑战:如何控制模型行为和何时需要人工介入。LangChain 提供了强大的中间件(Middleware)机制和人工中断(Human-in-the-Loop)功能,让开发者能够精确控制 Agent 的执行流程。
本文将深入探讨 LangChain 中间件的使用方式,并重点介绍如何在工具调用中实现人工介入,确保关键决策由人类把关。
2. LangChain 中间件机制
2.1 什么是中间件?
中间件是 LangChain Agent 执行流程中的拦截器,可以在模型调用前后插入自定义逻辑。它类似于 Web 开发中的中间件,能够:
- 过滤用户输入内容
- 修改模型输出
- 添加系统级功能(如日志、监控)
- 控制执行流程(跳转、终止)
2.2 中间件类型
LangChain 提供了三种主要中间件装饰器:
- @before_model - 在模型调用前执行
- @after_model - 在模型调用后执行
- @wrap_tool_call - 在工具调用前后执行
3. 中间件实战:智能客服内容守卫
3.1 内容过滤中间件
以下是一个实际的内容守卫中间件实现,用于过滤不当内容:
from langchain.agents.middleware import before_model
from langchain.messages import HumanMessage
@before_model
def content_guard(state, runtime):
"""过滤用户输入中的不当内容"""
last_msg = state["messages"][-1] if state.get("messages") else None
if not last_msg:
return None
content = str(getattr(last_msg, 'content', ''))
# 定义需要过滤的关键词
blocked = ["黄X", "X博", "违法"]
for word in blocked:
if word in content:
# 直接跳转到结束,返回预设回复
return {
"jump_to": "end",
"messages": [HumanMessage(content="抱歉,我不能处理这个请求。")]
}
return None
关键特性:
jump_to: "end"- 直接终止当前执行流程- 返回预设消息替代模型生成
- 实时检查用户输入,防止不当内容进入模型
3.2 自动签名中间件
from langchain.agents.middleware import after_model
from langchain.messages import AIMessage
@after_model
def auto_signature(state, runtime):
"""自动追加客服签名"""
msgs = state.get("messages", [])
if not msgs:
return None
last = msgs[-1]
# 只在 AI 回复且不是工具调用时添加签名
if last.type == "ai" and last.content and not (
hasattr(last, 'tool_calls') and last.tool_calls
):
return {"messages": [AIMessage(
content=last.content
+ "\n\n---\n菜鸟教程 RUNOOB 客服中心 | 工作时间 9:00-18:00"
)]}
return None
4. 人工介入(HITL)机制
4.1 为什么需要人工介入?
在某些关键场景下,完全依赖 AI 决策存在风险:
- 敏感操作(退款、转账)
- 法律合规问题
- 超出知识范围的问题
- 用户明确要求人工服务
4.2 实现人工介入的工具
LangChain 通过 interrupt 机制实现人工介入。以下是一个转接人工客服的工具示例:
from langchain.tools import tool
from langgraph.types import interrupt
@tool
def transfer_to_human(reason: str) -> str:
"""将用户转接给人工客服。
Args:
reason: 转接原因
"""
# 触发人工介入审批
approval = interrupt({
"action": "transfer_to_human",
"reason": reason,
"message": f"用户请求转接人工客服,原因:{reason}。是否转接?"
})
# 等待人工审批结果
if approval.get("confirmed"):
return (f"已为您转接人工客服,预计等待 {approval.get('wait_time', 3)} 分钟。"
f"工单号:TK-{approval.get('ticket_id', 'N/A')}")
return "转接已取消,我继续为您服务。"
4.3 中断处理流程
def chat(thread_id: str, message: str) -> str:
"""处理用户消息并返回回复"""
config = {"configurable": {"thread_id": thread_id}}
# 运行 Agent
result = agent.invoke(
{"messages": [HumanMessage(content=message)]},
config=config,
)
# 检查是否需要人工介入
state = agent.get_state(config)
if state.tasks and state.tasks[0].interrupts:
interrupt_info = state.tasks[0].interrupts[0].value
return f"[需要审批] {interrupt_info.get('message', '')}"
return result["messages"][-1].content
5. 完整智能客服系统实现
5.1 系统架构
from langchain.agents import create_agent
from langchain.chat_models import init_chat_model
from langchain.agents.middleware import before_model, after_model
from langgraph.checkpoint.sqlite import SqliteSaver
# 1. 初始化模型
model = init_chat_model("deepseek:deepseek-v4-flash", temperature=0)
# 2. 创建检查点(支持记忆和中断恢复)
checkpointer = SqliteSaver.from_conn_string("customer_service.db")
# 3. 定义工具集
tools = [search_kb, query_order, transfer_to_human]
# 4. 定义中间件链
middleware = [content_guard, auto_signature]
# 5. 创建带中间件和人工介入能力的 Agent
agent = create_agent(
model=model,
tools=tools,
middleware=middleware,
checkpointer=checkpointer,
system_prompt="""你是菜鸟教程 RUNOOB 的智能客服"小菜"。
## 你的职责
1. 热情接待每一位用户,用"您"称呼
2. 关于平台信息、课程内容、政策等问题,使用 search_kb 查询
3. 关于订单查询,使用 query_order 工具
4. 遇到无法解决的问题,使用 transfer_to_human 转接人工
## 行为准则
- 回答简洁,每次 2-3 句话
- 不知道的就查询知识库,查不到就诚实告知
- 保持友好亲切的语气""",
)
5.2 测试场景
if __name__ == "__main__":
user_id = "user_xiaoming"
print("=== 测试 1:正常知识库查询 ===")
print(chat(user_id, "Python3 教程有多少章?"))
# 输出:Python3 基础教程共 30 章,累计学习人次超 500 万。课程完全免费。
print("\n=== 测试 2:订单查询 ===")
print(chat(user_id, "我的订单 ORD-2024-001 状态是什么?"))
# 输出:订单 ORD-2024-001:VIP 年费会员 | 金额 ¥799 | 状态 已完成 | 日期 2024-01-15
print("\n=== 测试 3:触发人工介入 ===")
print(chat(user_id, "我要投诉,找你们领导!"))
# 输出:[需要审批] 用户请求转接人工客服,原因:我要投诉,找你们领导!。是否转接?
6. 中间件最佳实践
6.1 执行顺序管理
中间件的执行顺序很重要:
# 正确的顺序:输入验证 -> 业务逻辑 -> 输出格式化
middleware = [
input_validator, # 1. 验证输入
content_guard, # 2. 内容过滤
rate_limiter, # 3. 频率限制
business_logic, # 4. 业务处理
auto_signature, # 5. 添加签名
response_logger # 6. 记录日志
]
6.2 错误处理中间件
@before_model
def error_handler(state, runtime):
"""统一错误处理中间件"""
try:
# 检查状态是否有效
if not state.get("messages"):
return {
"jump_to": "end",
"messages": [HumanMessage(content="系统错误:消息为空")]
}
except Exception as e:
return {
"jump_to": "end",
"messages": [HumanMessage(content=f"系统错误:{str(e)}")]
}
return None
6.3 性能监控中间件
import time
from langchain.agents.middleware import wrap_tool_call
@wrap_tool_call
def performance_monitor(tool_call, runtime):
"""监控工具调用性能"""
start_time = time.time()
try:
result = yield tool_call
elapsed = time.time() - start_time
# 记录慢查询
if elapsed > 2.0: # 超过2秒
print(f"⚠️ 慢工具调用: {tool_call['name']} 耗时 {elapsed:.2f}秒")
return result
except Exception as e:
print(f"❌ 工具调用失败: {tool_call['name']}, 错误: {str(e)}")
raise
7. 人工介入的应用场景
7.1 敏感操作审批
@tool
def process_refund(order_id: str, amount: float) -> str:
"""处理退款申请(需要人工审批)"""
# 1. 验证订单信息
order = orders_db.get(order_id)
if not order:
return f"订单 {order_id} 不存在"
# 2. 触发人工审批
approval = interrupt({
"action": "refund_approval",
"order_id": order_id,
"amount": amount,
"customer": order["user"],
"message": f"用户 {order['user']} 申请退款 ¥{amount},订单 {order_id}。是否批准?"
})
# 3. 根据审批结果执行
if approval.get("approved"):
# 执行退款逻辑
return f"退款 ¥{amount} 已批准并处理,预计3-5个工作日到账。"
else:
reason = approval.get("reason", "审批未通过")
return f"退款申请未通过:{reason}"
7.2 复杂问题升级
@tool
def handle_complaint(issue: str, severity: str = "medium") -> str:
"""处理用户投诉(根据严重程度决定是否转人工)"""
# 根据严重程度决定处理方式
if severity == "high":
# 高严重度直接转人工
return transfer_to_human(f"高严重度投诉:{issue}")
elif severity == "medium":
# 中等严重度尝试自动处理,失败则转人工
try:
# 尝试自动处理逻辑
response = auto_handle_complaint(issue)
if "无法解决" in response:
return transfer_to_human(f"中等严重度投诉自动处理失败:{issue}")
return response
except Exception:
return transfer_to_human(f"中等严重度投诉处理异常:{issue}")
else:
# 低严重度完全自动处理
return auto_handle_complaint(issue)
8. 总结与建议
8.1 中间件使用要点
- 保持中间件轻量:避免在中间件中执行耗时操作
- 明确执行顺序:输入验证在前,输出格式化在后
- 错误隔离:每个中间件应该有独立的错误处理
- 可配置化:通过参数控制中间件行为
8.2 人工介入设计原则
- 明确触发条件:清晰定义何时需要人工介入
- 提供充分上下文:给审批人足够的信息做决策
- 设置超时机制:避免用户长时间等待
- 保留处理记录:所有人工介入都应记录日志
8.3 系统监控建议
# 在系统入口添加监控
def chat_with_monitoring(thread_id: str, message: str) -> str:
"""带监控的聊天接口"""
start_time = time.time()
try:
result = chat(thread_id, message)
# 记录性能指标
elapsed = time.time() - start_time
log_performance(thread_id, elapsed, len(message), len(result))
return result
except Exception as e:
# 自动触发人工介入处理异常
return transfer_to_human(f"系统异常:{str(e)}")
通过合理使用中间件和人工介入机制,我们可以在保持 AI 系统自主性的同时,确保关键决策的可控性和安全性。这种混合智能(Human-AI Collaboration)模式是构建可靠企业级 AI 应用的关键。
9. 扩展阅读
更多推荐

所有评论(0)