Agent中间是什么?
简单来说,Agent中间件是一种软件设计模式,它像一个"套娃"一样包裹住Agent核心的执行流程。它在Agent执行的不同阶段(如收到用户请求时、调用大模型前/后、调用工具前/后)设置"钩子"(Hooks),让你可以插入自定义的逻辑来拦截、检查、修改、增强或终止Agent的行为。
它不仅解决单次执行的可靠性问题,其思想和机制已被各大主流框架采纳,作为构建多智能体协作系统的核心技术。
一个基础的Agent通常只负责"接收输入 -> 调用LLM -> 调用工具 -> 返回输出"。但在真实应用中,我们几乎总是需要处理一些与核心业务逻辑无关的"横切关注点",例如:
-
日志记录:记录每次Agent调用的输入、输出、耗时,用于调试和监控。
-
权限校验:在Agent执行动作前,检查当前用户是否有权限调用某个特定工具。
-
缓存:对于相同的或相似的LLM请求,直接返回缓存结果,节省成本和延迟。
-
限流/熔断:防止Agent过度调用昂贵的LLM或外部API。
-
输入/输出预处理/后处理:例如对用户输入进行敏感词过滤,或将Agent的输出格式化为统一的JSON结构。
-
错误处理与重试:当LLM调用或工具调用失败时,统一捕获异常并执行重试或降级策略。
如果没有中间件,这些逻辑就需要直接写在Agent的核心执行循环里,导致代码臃肿、难以维护、无法复用。中间件提供了一个优雅的解决方案:解耦横切关注点,并支持插件化、可组合的架构。
Middleware的核心工作原理
Agent中间件大多基于责任链模式(Chain of Responsibility) 实现。你可以把它想象成洋葱:
-
请求从外向内,经过层层中间件的预处理。
-
核心Agent执行完毕。
-
响应再从内向外,经过层层中间件的后处理。

LangChain 官方内置中间件
LangChain 官方提供了丰富的预置中间件供你按需选用。下表汇总了目前常用的官方中间件,方便你查阅和快速找到需要的功能:
| 中间件名称 | 核心作用 | 典型应用场景 |
|---|---|---|
| SummarizationMiddleware | 自动压缩历史对话,防止上下文超限 | 客服机器人、长期对话,避免因上下文过长而报错或费用激增 |
| HumanInTheLoopMiddleware | 敏感操作前暂停,等待人工审批 | 金融转账、删除数据库等高危操作,增加安全阀 |
| ModelCallLimitMiddleware | 限制模型调用次数,防止无限循环 | 控制成本,防止 Agent 陷入无意义的自我循环 |
| ToolCallLimitMiddleware | 限制工具调用次数 | 避免高频调用第三方付费 API(如天气、股票接口)导致成本失控 |
| ModelFallbackMiddleware | 主模型故障时自动切换到备用模型 | 提升服务的高可用性,当 GPT-4 宕机时可降级到 GPT-3.5 |
| ModelRetryMiddleware | 模型调用失败时自动重试 | 应对网络波动或 LLM 服务商的临时限流 |
| ToolRetryMiddleware | 工具调用失败时自动重试 | 第三方 API 不稳定时,自动重新尝试获取数据 |
| PIIMiddleware | 识别并自动脱敏或屏蔽个人敏感信息 | 合规审查,自动掩盖日志中的手机号、身份证等信息 |
| TodoListMiddleware | 为 Agent 装备任务规划和追踪能力 | 处理多步骤复杂任务时,保持条理和专注,不遗漏关键步骤 |
| LLMToolSelectorMiddleware | 在调用主模型前,用小模型筛选相关工具 | 当注册给 Agent 的工具数量非常多时,减少每次请求的 Token 消耗 |
| ContextEditingMiddleware | 在对话流中自动编辑或修剪上下文 | 自动删除旧的工具调用结果,只保留最新的有效信息 |
| OpenAIModerationMiddleware | 集成 OpenAI 审核端点,检查内容安全性 | 对用户输入、模型输出进行安全合规审查,适用于需要内容安全的场景 |
这些内置中间件覆盖了从稳定性、安全性、成本控制到上下文管理等多个核心维度。使用它们就像"搭积木",极大地简化了构建可靠生产级 Agent 的复杂性。
除了选择需要的功能模块,使用中间件还需要你亲自指定两个关键方面:
-
1. 声明顺序:定义中间件的执行逻辑
在
create_agent()的middleware=参数里,传入的是一个列表。这个列表的顺序至关重要,它直接定义了中间件的执行顺序 ,是"套娃"的最外层到最内层的顺序。改变顺序会直接影响最终的行为,比如日志记录(LoggingMiddleware)和重试机制(RetryMiddleware)的先后逻辑。 -
2. 自定义逻辑:编写业务特定的中间件
当内置功能无法满足特定业务需求时,需要编写自定义逻辑。
-
使用钩子函数 :LangChain 提供了
@before_agent,@before_model,@after_model,@wrap_model_call等一系列装饰器作为钩子,能精准地将逻辑注入到 Agent 执行的不同阶段。 -
处理复杂依赖 :如果自定义中间件需要声明与其他中间件的依赖关系(如必须在某个中间件之后执行),可以通过
after和before等机制进行约束。当项目规模变大,顺序变得难以管理时,可借助langchain-middleware-stack这类工具,通过声明式配置解决依赖问题。
-
示例:
from langchain.agents import create_agent, AgentState
from langchain.agents.middleware import before_model
from langchain.messages import RemoveMessage
from langchain_core.tools import tool
from langgraph.checkpoint.memory import InMemorySaver
from langgraph.runtime import Runtime
from agent.my_llm import llm
@tool
def get_weather(city: str) -> str:
"""
获取指定城市的天气
Args:
city (str): 要查询天气的城市名称
Returns:
str: 包含城市天气信息的字符串
"""
return f"{city}的天气是晴朗的,温度是25摄氏度"
@before_model
def trim_messages(state: AgentState, runtime: Runtime) -> dict:
# 打印当前消息
print("当前state:", state)
print("当前runtime:", runtime)
messages = state["messages"]
if len(messages) > 5:
# 保留最后3条消息
retain_msg_count = 3
# 如果倒数第3条消息是工具调用,那么就保留最后2条消息
if messages[-retain_msg_count].type == "tool":
retain_msg_count =2
#删除的消息
print("删除的消息:", messages[:-retain_msg_count])
# 删除消息
return {"messages": [RemoveMessage(id=msg.id) for msg in messages[:-retain_msg_count]]}
return None
agent = create_agent(
model=llm,
tools=[get_weather],
middleware=[trim_messages],
checkpointer=InMemorySaver()
)
config = {"configurable": {"thread_id": "session_001"}}
# 模拟对话
response1 = agent.invoke({"messages": [{"role": "user", "content": "你好,我是张三"}]}, config=config)
print(response1["messages"][-1].content)
print("***"*20)
response2 = agent.invoke({"messages": [{"role": "user", "content": "今天北京天气好吗?"}]}, config=config)
print(response2["messages"][-1].content)
print("***"*20)
response3 = agent.invoke({"messages": [{"role": "user", "content": "上海天气怎么样?"}]}, config=config)
print(response3["messages"][-1].content)
print("***"*20)
final_response = agent.invoke({"messages": [{"role": "user", "content": "我的名字叫什么?"}]}, config=config)
print(final_response["messages"][-1].content)