【AI学习从零至壹】langchain1.0中间件
在 LangChain 1.0 中,中间件(Middleware)是一种革命性的架构模式,它彻底改变了我们处理 AI Agent 生命周期的方式。想象中间件就像给 Agent 穿上了一件"钢铁侠战衣"——这套战衣能够自动感知并拦截每一次肌肉收缩(Tool Call)和每一次脑电波爆发(Model Call),而 Agent 本身甚至不需要知道战衣的存在。
langchain1.0中间件
LangChain 1.0 中间件概念与对比分析
第一部分:LangChain 1.0 中间件概念解析
什么是中间件?
在 LangChain 1.0 中,中间件(Middleware) 是一种革命性的架构模式,它彻底改变了我们处理 AI Agent 生命周期的方式。
想象中间件就像给 Agent 穿上了一件"钢铁侠战衣"——这套战衣能够自动感知并拦截每一次肌肉收缩(Tool Call)和每一次脑电波爆发(Model Call),而 Agent 本身甚至不需要知道战衣的存在。
中间件的核心优势
非侵入式设计:Agent 核心逻辑保持纯净,不需要知道日志、安全或计费逻辑的存在。
可组合性:你可以像搭积木一样叠加多个中间件:一层负责日志,一层负责安全,一层负责计费。
统一拦截点:LangChain 1.0 提供了标准化的钩子函数,让你能够在 Agent 生命周期的关键节点插入自定义逻辑。
四大法宝钩子函数
-
wrap_model_call(最强拦截器)- 用途:动态换模型、修改 Prompt、记录日志
- 执行时机:包裹在每次 LLM 调用周围
-
wrap_tool_call- 用途:工具错误重试、PII 脱敏、权限检查
- 执行时机:包裹在每次工具执行周围
-
before_agent/after_agent- 用途:准备上下文、加载记忆、清理现场
- 执行时机:Agent 执行前后
-
before_model/after_model- 用途:轻量级钩子,简单的预处理和后处理
- 执行时机:每次模型调用前后
第二部分:LangChain 0.3 回调地狱代码示例
让我们看看旧时代的"回调地狱"是如何折磨开发者的:
from langchain.agents import AgentType, initialize_agent, AgentExecutor
from anygent.core.call_back import MyBaseCallbackHandler
from typing import Callable
class Agent():
def __init__(self, system_setup:str, user_prompt:str, llm_name:str, thought_call_back:Callable):
self.user_prompt = user_prompt
# 回调函数封装 - 第一步复杂性
self.callback_handler = MyBaseCallbackHandler(thought_call_back)
# 大模型初始化
llm = self.get_llm(llm_name)
# 系统身份设置 - 第二步复杂性
agent_kwargs = {
"prefix": f"{system_setup}",
"suffix": "Question:{input}\n{agent_scratchpad}"
}
# 第三步复杂性:必须手动为每个工具绑定回调
for tool in tools:
tool.callbacks = [self.callback_handler] # 工具单独绑定回调函数
# 第四步复杂性:初始化agent时的回调绑定
agent = initialize_agent(
tools,
llm,
agent=AgentType.ZERO_SHOT_REACT_DESCRIPTION,
agent_kwargs=agent_kwargs,
verbose=False,
callbacks=[self.callback_handler] # 这里也要绑定
)
# 第五步复杂性:还要给内部链绑定回调 - 双重确保
agent.agent.llm_chain.callbacks = [self.callback_handler]
agent.agent.llm_chain.llm.callbacks = [self.callback_handler]
# 第六步复杂性:AgentExecutor 也要绑定
self.agent_exe = AgentExecutor(
agent=agent.agent,
tools=agent.tools,
verbose=False,
max_iterations=30,
max_execution_time=1000,
early_stopping_method="force",
handle_parsing_errors=True,
callbacks=[self.callback_handler] # 再次绑定
)
def run(self):
# 第七步复杂性:调用时还要传回调
result = self.agent_exe.invoke(
{"input": self.user_prompt},
callbacks=[self.callback_handler] # 这里还要传一次!
)
return result["output"]
回调地狱的七大罪状
- 重复绑定:同样的回调处理器要在 7 个不同的地方重复绑定
- 侵入式设计:Agent 内部逻辑被回调代码严重污染
- 遗漏风险:只要漏掉一个绑定点,就会丢失重要的调试信息
- 性能负担:大量无用的回调触发,即使不需要也会执行
- 调试困难:回调链路过长,难以追踪执行顺序
- 维护噩梦:修改回调逻辑需要同时更新多个地方
- 耦合度高:回调逻辑与业务逻辑紧密耦合,无法分离
第三部分:LangChain 1.0 中间件重构代码示例
现在让我们看看如何用 LangChain 1.0 的中间件优雅地解决同样的问题:
from langchain.agents import create_agent
from langchain.agents.middleware import AgentMiddleware
from typing import Callable, Any
class ThoughtMiddleware(AgentMiddleware):
"""
Middleware to capture agent thoughts and tool observations
Replaces the old MyBaseCallbackHandler with elegance
"""
def __init__(self, callback: Callable[[str], None]):
self.callback = callback
def wrap_model_call(self, request, handler):
# 拦截模型调用 - 优雅地捕获思考过程
response = handler(request)
content = response.content if hasattr(response, "content") else str(response)
self.callback(content) # 将思考过程传递给用户
return response
def wrap_tool_call(self, request, handler):
# 拦截工具调用 - 优雅地捕获观察结果
response = handler(request)
self.callback(f"obversation: {response}") # 格式化观察结果
return response
class Agent():
def __init__(self, system_setup:str, user_prompt:str, llm_name:str, thought_call_back:Callable):
self.user_prompt = user_prompt
# 大模型初始化 - 保持简洁
llm = self.get_llm(llm_name)
# 初始化 Middleware - 一行代码搞定所有拦截
self.middleware = [ThoughtMiddleware(thought_call_back)]
# 创建 Agent - LangChain 1.0 的优雅方式
# 一行代码替换掉 initialize_agent + AgentExecutor 的复杂组合
self.agent_runner = create_agent(
model=llm,
tools=tools,
system_prompt=system_setup, # 系统提示词直接参数化
middleware=self.middleware # 中间件列表,可扩展
)
def get_llm(self, llm_name:str):
llm_list = ["claude-sonnet-4", "gemini-2.5-flash", "gemini-2.5-pro", "gemini-3-pro-preview"]
assert llm_name in llm_list, f"llm必须是{llm_list}之一, 你传入的是{llm_name}"
if llm_name == "claude-sonnet-4":
llm = ClaudeSonnet4().get_llm()
elif llm_name in ["gemini-2.5-pro", "gemini-3-pro-preview", "gemini-2.5-flash"]:
llm = GeminiSeries(llm_name).get_llm()
else:
raise ValueError(f"不支持的模型:{llm_name}")
return llm
def run(self):
# 简洁的调用方式 - 无需重复传递回调
result = self.agent_runner.invoke({
"messages": [{"role": "user", "content": self.user_prompt}]
})
return result.get("output", result)
# 使用示例 - 简洁优雅
if __name__ == "__main__":
def thought_callback(data):
print(f"[Middleware Log]: {data}")
agent = Agent(
system_setup="你是一个ai助手,帮助回答用户的问题",
user_prompt="现在几点了",
llm_name="gemini-3-pro-preview",
thought_call_back=thought_callback
)
result = agent.run()
print(f"Final Result: {result}")
中间件重构的七大优势
- 一行配置:所有拦截逻辑集中在一个 Middleware 类中
- 非侵入式设计:Agent 核心逻辑保持纯净,不受回调污染
- 零遗漏风险:Middleware 自动拦截所有相关调用,无需手动绑定
- 性能优化:只在需要时执行拦截逻辑,避免无用回调
- 调试友好:清晰的拦截点,易于追踪和调试
- 维护简单:修改拦截逻辑只需更新 Middleware 类
- 高度解耦:拦截逻辑与业务逻辑完全分离,可独立测试
第四部分:回调函数与中间件对比分析
架构设计对比
| 特性 | LangChain 0.3 回调 | LangChain 1.0 中间件 |
|---|---|---|
| 设计模式 | 侵入式回调 | 非侵入式切面 |
| 代码耦合 | 高耦合,业务逻辑被污染 | 低耦合,保持业务纯净 |
| 配置复杂度 | 需要在7个地方重复绑定 | 一行代码配置所有拦截 |
| 可维护性 | 修改需要更新多个绑定点 | 修改只需更新Middleware类 |
| 扩展性 | 困难,容易遗漏绑定点 | 简单,可叠加多个中间件 |
| 调试难度 | 回调链路过长,难以追踪 | 清晰的拦截点,易于调试 |
| 性能影响 | 大量无用回调触发 | 只在需要时执行拦截 |
实际开发体验对比
LangChain 0.3 回调地狱:
# 开发者内心OS:"我要在哪里绑定回调?"
# "工具要绑定,Agent要绑定,链要绑定,执行器还要绑定..."
# "完了,我漏掉了一个绑定点,调试信息不完整!"
# "修改回调逻辑?我要改7个地方..."
LangChain 1.0 中间件天堂:
# 开发者内心OS:"我只需要写一个Middleware类"
# "一行代码就能搞定所有拦截"
# "想加新功能?再写一个Middleware叠加上去"
# "调试?看Middleware的拦截点就行了"
中间件的革命性意义
从"回调地狱"到"中间件天堂",这不仅仅是API的升级,更是思维方式的转变:
- 从被动到主动:以前是"哪里需要回调就绑哪里",现在是"我想拦截什么就写什么Middleware"
- 从分散到集中:以前回调逻辑分散在各个角落,现在所有拦截逻辑集中管理
- 从耦合到解耦:以前业务逻辑和回调逻辑纠缠不清,现在两者完全分离
- 从复杂到简单:以前需要记忆7个绑定点,现在只需要记住"写Middleware,加列表"
最佳实践建议
- 每个Middleware只做一件事:日志Middleware只负责日志,安全Middleware只负责安全
- Middleware命名要清晰:
ThoughtMiddleware、SafetyMiddleware、LoggingMiddleware - 保持Middleware纯净:不要在Middleware里写业务逻辑
- 利用组合而非继承:通过叠加多个Middleware实现复杂功能
- 测试你的Middleware:Middleware可以独立测试,确保拦截逻辑正确
总结
LangChain 1.0的中间件系统彻底解决了0.3版本的"回调地狱"问题,让AI Agent的开发从"到处绑定回调"的噩梦,变成了"写Middleware,加列表"的优雅体验。
中间件 = 控制 + 优雅 + 可维护
这就是LangChain 1.0带给我们的革命性改变。
更多推荐


所有评论(0)