🧠 LangChain 中间件(Middleware)实战:实现“模型调用守卫”

在上一章我们已经实现了一个基础 Agent。
那么如果我们想实现类似前端路由守卫的能力,比如:

  • 在模型调用前/后做处理
  • 动态修改请求(如切换模型、调整参数)
  • 做日志、监控、策略控制

👉 这时候就需要用到 Middleware(中间件)

⚙️ 一、如何定义一个中间件?

LangChain 提供了装饰器:

1
@wrap_model_call

定义方式如下:

1
def middleware(request, handler): ...

👉 必须接收两个参数:


📦 参数1:request(ModelRequest 对象)

request 表示一次完整的模型调用请求,是一个对象(不是 dict):

1
2
3
4
5
6
7
ModelRequest(
model=..., # 当前使用的模型
messages=[...], # 对话内容(最重要)
invocation_params={}, # temperature 等参数
metadata={}, # 自定义信息
tags=[]
)

🧠 messages(最关键字段)

1
request.messages

结构如下:

1
2
3
4
5
6
[
SystemMessage(...),
HumanMessage(content="你好"),
AIMessage(...),
HumanMessage(content="帮我写代码")
]

👉 获取用户输入:

1
request.messages[-1].content

⚠️ 更推荐的写法(更严谨):

1
2
3
4
5
6
from langchain_core.messages import HumanMessage

def get_user_input(messages):
for msg in reversed(messages):
if isinstance(msg, HumanMessage):
return msg.content

🔁 参数2:handler(核心执行器)

handler 是一个函数,表示:

👉 “继续执行模型调用流程”

你可以理解为:

1
handler(request) = 真正调用 LLM

🚨 非常重要的规则

👉 在 middleware 中:

  • ✅ 可以修改 request
  • ✅ 必须调用 handler(request)
  • ❌ 不要自己调用 llm.invoke()

🔄 二、Middleware 执行流程

当调用:

1
agent.invoke(...)

实际流程如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
用户输入

封装为 ModelRequest

进入 middleware(wrap_model_call)

你可以修改 request(如切模型)

handler(request)

模型执行

返回结果

👉 核心思想:

💡 Middleware = “模型调用前的拦截与改写”


🚀 三、完整 Demo:根据问题复杂度切换模型

1️⃣ 定义分类模型(判断问题复杂度)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
from pydantic import BaseModel, Field
from langchain_core.prompts import ChatPromptTemplate

# 定义结构化输出
class QuestionComplexity(BaseModel):
"""问题复杂度分类"""
type: str = Field(
description="问题类型:simple=简单,complex=复杂",
pattern="^(simple|complex)$"
)

# 提示词
classifier_prompt = ChatPromptTemplate.from_messages(
[
("system", """
你是专业的问题分类器,请严格按规则判断:
简单问题(simple):日常聊天、常识、单轮问答、一句话可回答
复杂问题(complex):需要推理、计算、代码、多步骤、专业知识、长文本理解
只返回分类结果,不要解释。
"""),
("human", "{question}"),
]
)

# 绑定结构化输出
structured_llm = llm.with_structured_output(QuestionComplexity)

# 构建 chain
classifier_chain = classifier_prompt | structured_llm

2️⃣ 定义中间件(核心逻辑)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@wrap_model_call
def middleware(request, handler):
# 1️⃣ 获取用户问题
question = request.messages[-1].content

# 2️⃣ 分类
res = classifier_chain.invoke({"question": question})

# 3️⃣ 根据复杂度切换模型
if res.type == "complex":
print("🚀 切换高级模型")
request.model = advanced_llm
else:
print("🪶 使用默认模型")

# 4️⃣ 继续执行模型调用
return handler(request)

3️⃣ 挂载中间件到 Agent

1
2
3
4
5
6
agent = create_agent(
model=llm,
tools=[get_weather, get_nangua],
system_prompt="你是一个聊天助手,可以回答用户问题!",
middleware=[middleware],
)

🧠 四、总结

通过 Middleware,我们可以在不修改 Agent 主逻辑的情况下,实现:

  • ✅ 动态模型路由(simple / complex)
  • ✅ 请求改写(messages / params)
  • ✅ 统一日志与监控
  • ✅ 策略控制(限流 / 权限等)

💡 一句话理解

👉 Middleware = “模型调用前的可编程拦截层”


🤯 五、踩坑总结

  • request 是对象,不是 dict
  • messages 是 Message 对象列表,不是字符串
  • 不要直接调用 llm.invoke()(会绕过 pipeline)
  • 正确方式是:修改 request + 调用 handler(request)

🚀 下一步

  • 多模型路由(更复杂策略)
  • Tool Calling(Agent核心)
  • Memory(上下文管理)
  • LangGraph(流程编排)

By

GPT编写