langchain1.0中间件
- LangChain 1.0 中间件概念与对比分析
-
- 第一部分:LangChain 1.0 中间件概念解析
-
- 什么是中间件?
- 中间件的核心优势
- 四大法宝钩子函数
- 第二部分:LangChain 0.3 回调地狱代码示例
-
- 回调地狱的七大罪状
- 第三部分:LangChain 1.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"]
回调地狱的七大罪状
第三部分: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}")
中间件重构的七大优势
第四部分:回调函数与中间件对比分析
架构设计对比
| 设计模式 | 侵入式回调 | 非侵入式切面 |
| 代码耦合 | 高耦合,业务逻辑被污染 | 低耦合,保持业务纯净 |
| 配置复杂度 | 需要在7个地方重复绑定 | 一行代码配置所有拦截 |
| 可维护性 | 修改需要更新多个绑定点 | 修改只需更新Middleware类 |
| 扩展性 | 困难,容易遗漏绑定点 | 简单,可叠加多个中间件 |
| 调试难度 | 回调链路过长,难以追踪 | 清晰的拦截点,易于调试 |
| 性能影响 | 大量无用回调触发 | 只在需要时执行拦截 |
实际开发体验对比
LangChain 0.3 回调地狱:
# 开发者内心OS:"我要在哪里绑定回调?"
# "工具要绑定,Agent要绑定,链要绑定,执行器还要绑定…"
# "完了,我漏掉了一个绑定点,调试信息不完整!"
# "修改回调逻辑?我要改7个地方…"
LangChain 1.0 中间件天堂:
# 开发者内心OS:"我只需要写一个Middleware类"
# "一行代码就能搞定所有拦截"
# "想加新功能?再写一个Middleware叠加上去"
# "调试?看Middleware的拦截点就行了"
中间件的革命性意义
从"回调地狱"到"中间件天堂",这不仅仅是API的升级,更是思维方式的转变:
最佳实践建议
总结
LangChain 1.0的中间件系统彻底解决了0.3版本的"回调地狱"问题,让AI Agent的开发从"到处绑定回调"的噩梦,变成了"写Middleware,加列表"的优雅体验。
中间件 = 控制 + 优雅 + 可维护
这就是LangChain 1.0带给我们的革命性改变。
网硕互联帮助中心





评论前必须登录!
注册