Appearance
中间件概览
本节解决什么问题
Middleware(中间件)是 LangChain 1.0 Agent 架构中的拦截器层。它允许你在 Agent 的请求-响应流程中插入自定义逻辑,而无需修改 Agent 的核心代码。
具体来说,Middleware 可以在多个时机介入:
- 请求前(before_agent / before_model):修改用户输入、检测敏感信息、记录日志
- 模型调用时(wrap_model_call):动态调整模型参数、过滤工具列表、注入上下文
- 工具调用时(wrap_tool_call):拦截工具执行、参数审计、结果改写
- 响应后(after_model / after_agent):处理模型输出、格式化结果、触发通知
这种设计实现了横切关注点(Cross-Cutting Concerns)的分离--安全、日志、审批等功能不再散落在业务代码中,而是集中到独立的 Middleware 模块。
先修知识
- 已完成 Agent 实战指南
- 了解 智能体 Agent 的基本概念
必须深刻理解,不能跳过:中间件是 Agent 循环的"洋葱层",不是请求拦截器
前端的请求拦截器(Axios interceptor、Express middleware)拦截的是一次 HTTP 请求。LangChain 的中间件拦截的是 Agent 的一次模型调用轮次。Agent 在循环中可能多次调用模型和工具,每次模型调用都会穿过中间件链。这意味着
wrap_model_call在一次agent.invoke()中可能被执行多次。
前端类比
如果你用过 Express 或 Koa,这个模型会非常熟悉。Express 的 app.use() 链和 Koa 的 async (ctx, next) => {} 模式本质上就是洋葱模型。LangChain 的 Middleware 与 Koa 的实现最为接近:
javascript
// Koa 中间件 - 等价概念
app.use(async (ctx, next) => {
console.log('请求前') // 前处理
await next() // 调用下一层
console.log('响应后') // 后处理
})LangChain 的 handler(request) 等价于 Koa 的 await next(),都是"穿透到下一层"的调用。
原生语义
Koa 中间件处理的是 HTTP 请求-响应周期(一次请求一次响应)。LangChain 中间件处理的是 Agent 循环中的一个模型调用轮次。Agent 在一次 invoke 中可能经历多轮"模型推理 -> 工具调用 -> 模型推理"循环,每一轮模型调用都会穿过 wrap_model_call 中间件。理解这一点才能正确设计中间件的行为和副作用。
洋葱模型:Middleware 执行机制
Middleware 采用经典的洋葱模型(Onion Model)执行。每个 Middleware 像洋葱的一层皮,请求从外层向内层穿透,到达 Agent/LLM 核心后,响应再从内层向外层返回。每一层都有机会在请求和响应两个阶段分别执行逻辑。
关键特征:
| 阶段 | 方向 | 说明 |
|---|---|---|
| 请求阶段 | 从上到下(外->内) | 按 middleware 列表的声明顺序执行 |
| 响应阶段 | 从下到上(内->外) | 按 middleware 列表的逆序执行 |
这意味着列表中第一个 Middleware 最先接触请求,也最后接触响应--它拥有"全局视角"。
如何添加 Middleware
通过 create_agent 的 middleware 参数传入 Middleware 列表:
python
import os
from langchain.agents import create_agent
from langchain.chat_models import init_chat_model
from langchain.agents.middleware import PIIMiddleware, SummarizationMiddleware
model = init_chat_model(os.environ["LLM_MODEL"])
agent = create_agent(
model=model,
tools=[search, calculator],
middleware=[
PIIMiddleware("email", strategy="redact"),
SummarizationMiddleware(
model=model,
trigger=("tokens", 4000), # 超过 4000 token 时触发摘要
keep=("messages", 20), # 保留最近 20 条消息
),
],
)middleware 接收一个列表,列表中的顺序就是执行顺序。Agent 创建后,Middleware 链即固定,不可在运行时动态修改。
如需根据环境动态配置,可以在创建前构建列表:
python
middlewares = []
if config.enable_logging:
middlewares.append(LoggingMiddleware())
if config.enable_pii_detection:
middlewares.append(PIIMiddleware("email", strategy="redact"))
agent = create_agent(
model=model,
tools=[search],
middleware=middlewares,
)旧版
SummarizationMiddleware(max_tokens_before_summary=500) 参数已弃用。使用 trigger=("tokens", N) 指定触发条件,keep=("messages", N) 指定保留策略。model 是必填参数。
python
# 旧版(已弃用)
SummarizationMiddleware(model="...", max_tokens_before_summary=500)
# 当前写法
SummarizationMiddleware(model=model, trigger=("tokens", 500), keep=("messages", 20))Middleware 排序策略
排序直接影响行为正确性和性能。推荐的排序原则:
外层(最先执行)
├── 1. 日志 / 追踪 ← 记录原始请求,方便调试
├── 2. 安全检查(PII / 内容过滤) ← 尽早拦截不合规请求
├── 3. 性能优化(缓存 / 摘要) ← 减少后续层的处理量
└── 4. 流程控制(人工审批) ← 只在必要时介入
内层(最接近 Agent/LLM)为什么这样排?
- 日志在最外层:能够捕获完整的请求-响应生命周期,包括其他 Middleware 的处理时间
- 安全检查靠前:尽早拦截包含敏感信息的请求,避免不必要的后续处理(如 LLM 调用)
- 人工审批靠后:只有通过了所有自动化检查的请求,才需要人工介入,减少审批负担
反面示例:
python
# 错误的排序 - 先摘要再 PII 检测
middleware=[
SummarizationMiddleware(...), # 摘要过程可能引入 PII
PIIMiddleware("email"), # 此时检测可能遗漏摘要中的 PII
]
# 正确的排序
middleware=[
PIIMiddleware("email"), # 先检测原始输入
SummarizationMiddleware(...), # 再进行摘要
]内置中间件全貌
LangChain 提供了 18 个内置中间件,覆盖安全合规、成本控制、流程管理、容错、工具增强等横切关注点:
核心安全与流程(3 个)
| 中间件 | 功能 | 适用场景 |
|---|---|---|
PIIMiddleware | PII 检测、脱敏、拦截 | 合规要求、数据安全 |
SummarizationMiddleware | 自动摘要长对话 | 控制 token 用量、降低成本 |
HumanInTheLoopMiddleware | 敏感操作人工审批 | 高风险工具调用、合规审计 |
容错与重试(3 个)
| 中间件 | 功能 | 适用场景 |
|---|---|---|
ModelRetryMiddleware | 模型调用失败自动重试 | 网络不稳定、API 限流 |
ToolRetryMiddleware | 工具执行失败自动重试 | 工具偶发超时或错误 |
ModelFallbackMiddleware | 主模型失败时切换备用模型 | 高可用要求、多 Provider 容灾 |
调用限制(2 个)
| 中间件 | 功能 | 适用场景 |
|---|---|---|
ModelCallLimitMiddleware | 限制模型调用次数上限 | 防止无限循环、控制成本 |
ToolCallLimitMiddleware | 限制工具调用次数上限 | 防止工具滥用 |
工具与上下文增强(6 个)
| 中间件 | 功能 | 适用场景 |
|---|---|---|
ContextEditingMiddleware | 编辑 / 裁剪上下文消息 | 精细控制发送给模型的内容 |
LLMToolSelectorMiddleware | 用 LLM 动态选择可用工具 | 工具数量多、按需加载 |
LLMToolEmulatorMiddleware | 用 LLM 模拟工具执行 | 测试、无真实 API 时预演 |
ProviderToolSearchMiddleware | 从 Provider 搜索可用工具 | 集成 Provider 工具生态 |
FilesystemMiddleware | 文件系统操作工具 | 文件读写、目录管理 |
FileSearchMiddleware | 文件搜索工具 | 大量文件中检索 |
高级能力(4 个)
| 中间件 | 功能 | 适用场景 |
|---|---|---|
SubagentMiddleware | 嵌套子 Agent | 复杂任务分解、专家委托 |
TodoListMiddleware | 自动维护待办列表 | 多步任务进度追踪 |
ShellToolMiddleware | Shell 命令执行工具 | 系统运维、自动化脚本 |
RubricGradingMiddleware | 基于评分标准的质量评估 [beta] | 输出质量打分、自动评估 |
标注
[beta]的中间件尚不稳定,API 可能在后续版本变更,生产环境谨慎使用。
详细的 PII / Summarization / HITL 用法请参考 内置中间件。
自定义中间件
当内置 Middleware 无法满足需求时,可以继承 AgentMiddleware 基类创建自定义 Middleware:
python
from langchain.agents.middleware import AgentMiddleware, ModelRequest, ModelResponse
class MyMiddleware(AgentMiddleware):
def wrap_model_call(self, request: ModelRequest, handler):
# 前处理:修改请求
modified_request = self.preprocess(request)
# 穿透到下一层(模型调用)
response = handler(modified_request)
# 后处理:修改响应
return self.postprocess(response)可用 Hooks
AgentMiddleware 提供以下 hook,按执行顺序排列:
| Hook | 触发时机 | 典型用途 |
|---|---|---|
before_agent | Agent 执行开始(整个循环之前) | 初始化上下文、记录开始时间 |
before_model | 每次模型调用之前 | 注入系统消息、修改工具列表 |
wrap_model_call | 包裹模型调用(可前后处理) | 修改请求 / 响应、计时、缓存 |
wrap_tool_call | 包裹工具调用(可前后处理) | 参数审计、结果改写 |
after_model | 每次模型调用之后 | 输出过滤、格式化 |
after_agent | Agent 执行结束(整个循环之后) | 清理资源、记录结束时间 |
详细用法请参考 自定义中间件。
性能考量
每个 Middleware 都会在请求-响应路径上增加额外开销。以下是典型的延迟参考:
| Middleware 类型 | 典型延迟 | 说明 |
|---|---|---|
| 日志记录 | < 1ms | 仅 I/O 写入 |
| PII 正则检测 | 1-10ms | 取决于正则复杂度和文本长度 |
| 对话摘要 | 1-5s | 需要额外的 LLM 调用 |
| 人工审批 | 不确定 | 取决于人工响应时间 |
优化建议:
- 只添加必需的 Middleware:每多一层,请求链路就多一次函数调用开销
- 利用短路逻辑:在 Middleware 内部尽早判断是否需要处理,避免无意义的计算
- 异步日志:如果日志 Middleware 涉及网络 I/O(如发送到远程服务),使用异步写入避免阻塞主链路
- 监控 Middleware 耗时:在日志 Middleware 中记录每层的处理时间,定位性能瓶颈
python
import time
class TimingMiddleware(AgentMiddleware):
def wrap_model_call(self, request, handler):
start = time.perf_counter()
response = handler(request)
elapsed = time.perf_counter() - start
print(f"内层处理耗时: {elapsed:.3f}s")
return response将 TimingMiddleware 放在不同位置,可以测量不同层级的耗时。
下一步
- 内置中间件 - PII / Summarization / HITL 的完整 API、以及 Model Retry / Tool Retry / Model Fallback 等容错中间件
- 自定义中间件 - 从零编写 Middleware,包括日志、限流、工具过滤等实用模式
- Agent 实战指南 - 回顾 Agent 的完整用法