【个人主页:玄同765】
大语言模型(LLM)开发工程师 |中国传媒大学·数字媒体技术(智能交互与游戏设计)
**深耕领域:**大语言模型开发 / RAG知识库 / AI Agent落地 / 模型微调
**技术栈:**Python / LangChain/RAG(Dify+Redis+Milvus)| SQL/NumPy | FastAPI+Docker ️
**工程能力:**专注模型工程化部署、知识库构建与优化,擅长全流程解决方案
「让AI交互更智能,让技术落地更高效」
欢迎技术探讨/项目合作! 关注我,解锁大模型与智能交互的无限可能!
一、什么是中间件?
1.1 中间件的核心思想
中间件(Middleware) 是一种软件设计模式,它允许你在请求处理流程的特定节点插入自定义逻辑,而无需修改核心业务的代码。
核心思想可以概括为:关注点分离(Separation of Concerns)
- 将横切关注点(日志、认证、限流等)从核心业务逻辑中剥离
- 通过可组合的拦截器增强系统灵活性
- 实现代码复用和模块化设计
1.2 中间件的典型应用场景
| 场景 | 说明 | 示例 |
|---|---|---|
| 日志记录 | 记录请求/响应信息 | 记录 API 调用日志 |
| 认证授权 | 验证用户身份和权限 | JWT Token 验证 |
| 限流控制 | 防止系统过载 | 限制 API 调用频率 |
| 错误处理 | 统一异常处理 | 全局异常捕获 |
| 数据转换 | 请求/响应数据加工 | JSON 序列化、加密解密 |
| 性能监控 | 统计执行时间 | 接口性能分析 |
二、FastAPI 中间件详解
2.1 FastAPI 中间件架构
FastAPI 基于 Starlette 框架,采用经典的**洋葱模型(Onion Model)**中间件架构:
请求进入
↓
┌─────────────────────────────────────┐
│ Middleware 1 (before) │
│ ┌───────────────────────────────┐ │
│ │ Middleware 2 (before) │ │
│ │ ┌─────────────────────────┐ │ │
│ │ │ Middleware 3 (before) │ │ │
│ │ │ ┌─────────────────┐ │ │ │
│ │ │ │ Route Handler │ │ │ │
│ │ │ │ (核心业务) │ │ │ │
│ │ │ └─────────────────┘ │ │ │
│ │ │ Middleware 3 (after) │ │ │
│ │ └─────────────────────────┘ │ │
│ │ Middleware 2 (after) │ │
│ └───────────────────────────────┘ │
│ Middleware 1 (after) │
└─────────────────────────────────────┘
↓
响应返回
2.2 创建 FastAPI 中间件
FastAPI 提供两种创建中间件的方式:
方式一:使用装饰器(推荐)
from fastapi import FastAPI, Request
from fastapi.responses import JSONResponse
import time
app = FastAPI()
@app.middleware("http")
async def add_process_time_header(request: Request, call_next):
"""
计算请求处理时间的中间件
"""
start_time = time.time()
# 调用下一个中间件或路由处理器
response = await call_next(request)
# 在响应头中添加处理时间
process_time = time.time() - start_time
response.headers["X-Process-Time"] = str(process_time)
return response
方式二:继承 BaseHTTPMiddleware
from fastapi import FastAPI, Request
from fastapi.responses import JSONResponse
from starlette.middleware.base import BaseHTTPMiddleware
import logging
# 配置日志
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
class LoggingMiddleware(BaseHTTPMiddleware):
"""
日志记录中间件
"""
async def dispatch(self, request: Request, call_next):
# 记录请求信息
logger.info(f"Request: {request.method} {request.url}")
# 调用下一个处理器
response = await call_next(request)
# 记录响应信息
logger.info(f"Response: {response.status_code}")
return response
app = FastAPI()
app.add_middleware(LoggingMiddleware)
2.3 FastAPI 中间件实战案例
案例 1:JWT 认证中间件
from fastapi import FastAPI, Request, HTTPException
from fastapi.responses import JSONResponse
from starlette.middleware.base import BaseHTTPMiddleware
import jwt
from datetime import datetime
class JWTAuthMiddleware(BaseHTTPMiddleware):
"""
JWT 认证中间件
验证请求头中的 Authorization Token
"""
def __init__(self, app, secret_key: str, exclude_paths: list = None):
super().__init__(app)
self.secret_key = secret_key
self.exclude_paths = exclude_paths or ["/login", "/register"]
async def dispatch(self, request: Request, call_next):
# 跳过不需要认证的路径
if request.url.path in self.exclude_paths:
return await call_next(request)
# 获取 Authorization Header
auth_header = request.headers.get("Authorization")
if not auth_header:
return JSONResponse(
status_code=401,
content={"detail": "Missing authorization header"}
)
try:
# 提取 Token
scheme, token = auth_header.split()
if scheme.lower() != "bearer":
raise ValueError("Invalid scheme")
# 验证 Token
payload = jwt.decode(token, self.secret_key, algorithms=["HS256"])
# 将用户信息添加到请求状态
request.state.user = payload
except jwt.ExpiredSignatureError:
return JSONResponse(
status_code=401,
content={"detail": "Token has expired"}
)
except Exception as e:
return JSONResponse(
status_code=401,
content={"detail": f"Invalid token: {str(e)}"}
)
# 继续处理请求
response = await call_next(request)
return response
# 使用中间件
app = FastAPI()
app.add_middleware(
JWTAuthMiddleware,
secret_key="your-secret-key",
exclude_paths=["/docs", "/openapi.json", "/login"]
)
案例 2:速率限制中间件
import time
from collections import defaultdict
from fastapi import Request
from starlette.middleware.base import BaseHTTPMiddleware
from fastapi.responses import JSONResponse
class RateLimitMiddleware(BaseHTTPMiddleware):
"""
基于内存的速率限制中间件
限制每个 IP 每分钟最多 60 次请求
"""
def __init__(self, app, max_requests: int = 60, window: int = 60):
super().__init__(app)
self.max_requests = max_requests
self.window = window
# 存储每个 IP 的请求记录: {ip: [(timestamp1), (timestamp2), ...]}
self.requests = defaultdict(list)
async def dispatch(self, request: Request, call_next):
client_ip = request.client.host
current_time = time.time()
# 清理过期的请求记录
self.requests[client_ip] = [
req_time for req_time in self.requests[client_ip]
if current_time - req_time < self.window
]
# 检查是否超过限制
if len(self.requests[client_ip]) >= self.max_requests:
return JSONResponse(
status_code=429,
content={
"detail": "Rate limit exceeded",
"retry_after": self.window
}
)
# 记录本次请求
self.requests[client_ip].append(current_time)
# 添加限流相关响应头
response = await call_next(request)
response.headers["X-RateLimit-Limit"] = str(self.max_requests)
response.headers["X-RateLimit-Remaining"] = str(
self.max_requests - len(self.requests[client_ip])
)
return response
案例 3:CORS 中间件(内置)
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
app = FastAPI()
# 配置 CORS
app.add_middleware(
CORSMiddleware,
allow_origins=["http://localhost:3000", "https://yourdomain.com"],
allow_credentials=True,
allow_methods=["GET", "POST", "PUT", "DELETE"],
allow_headers=["*"],
max_age=3600,
)
三、LangChain v1.0+ Agent 中间件详解
3.1 LangChain 中间件的设计背景
LangChain v1.0(2025 年 10 月 22 日发布)对 Agent 系统进行了彻底重构,中间件(Middleware) 是这次重构的核心特性之一。
为什么需要中间件?
在 v0.x 版本中,控制 Agent 行为主要依赖 Callbacks 机制:
# v0.x 时代的 Callbacks(已弃用)
from langchain.callbacks.base import BaseCallbackHandler
class MyCallback(BaseCallbackHandler):
def on_llm_start(self, serialized, prompts, **kwargs):
pass
def on_tool_start(self, serialized, input_str, **kwargs):
pass
Callbacks 的问题:
- ❌ 无法修改请求/响应数据
- ❌ 无法中断执行流程
- ❌ 控制能力有限
v1.0 中间件的优势:
- ✅ 完整的请求/响应控制
- ✅ 支持中断和重试
- ✅ 可组合、可复用
- ✅ 类型安全
3.2 LangChain 中间件架构
LangChain Agent 中间件采用**钩子函数(Hooks)**设计模式:
用户输入
↓
before_agent (Agent 执行前)
↓
before_model (调用模型前)
↓
wrap_model_call (包装模型调用)
↓
模型实际调用
↓
after_model (模型调用后)
↓
wrap_tool_call (包装工具调用)
↓
工具实际执行
↓
after_agent (Agent 执行后)
↓
返回结果
3.3 创建 LangChain 中间件
基础中间件结构
from langchain.agents.middleware import AgentMiddleware
from typing import Any, Dict, List, Optional
class MyMiddleware(AgentMiddleware):
"""
自定义 Agent 中间件
"""
async def before_agent(
self,
query: str,
context: Dict[str, Any]
) -> Dict[str, Any]:
"""
Agent 执行前调用
可以修改用户查询、添加上下文等
"""
print(f"[Before Agent] Query: {query}")
return {"query": query, "context": context}
async def before_model(
self,
messages: List[Dict[str, Any]],
context: Dict[str, Any]
) -> List[Dict[str, Any]]:
"""
调用模型前调用
可以修改消息列表、添加系统提示等
"""
print(f"[Before Model] Messages count: {len(messages)}")
return messages
async def after_model(
self,
response: Any,
context: Dict[str, Any]
) -> Any:
"""
模型调用后调用
可以处理模型输出、记录日志等
"""
print(f"[After Model] Response: {response}")
return response
async def wrap_tool_call(
self,
tool_name: str,
tool_input: Dict[str, Any],
context: Dict[str, Any]
) -> Any:
"""
包装工具调用
可以添加重试逻辑、超时控制等
"""
print(f"[Tool Call] {tool_name}: {tool_input}")
# 调用实际工具
result = await self.call_tool(tool_name, tool_input)
return result
注册中间件到 Agent
from langchain.agents import create_agent
from langchain_openai import ChatOpenAI
# 创建 LLM
llm = ChatOpenAI(model="gpt-4")
# 创建工具列表
tools = [...]
# 创建中间件列表
middleware = [
MyMiddleware(),
# 可以添加更多中间件...
]
# 创建带中间件的 Agent
agent = create_agent(
llm=llm,
tools=tools,
middleware=middleware
)
# 使用 Agent
result = await agent.ainvoke({"input": "查询北京天气"})
3.4 LangChain 内置中间件
LangChain v1.0 提供了丰富的内置中间件:
1. HumanInTheLoopMiddleware(人工审核)
from langchain.agents.middleware import HumanInTheLoopMiddleware
# 创建人工审核中间件
human_middleware = HumanInTheLoopMiddleware(
# 在以下情况下暂停等待人工确认
pause_on_tool_calls=True, # 工具调用前暂停
pause_on_completion=False, # 完成后不暂停
custom_prompt="请确认是否执行此操作:"
)
agent = create_agent(
llm=llm,
tools=tools,
middleware=[human_middleware]
)
2. ToolCallLimitMiddleware(工具调用限制)
from langchain.agents.middleware import ToolCallLimitMiddleware
# 限制最多调用 5 个工具
limit_middleware = ToolCallLimitMiddleware(max_calls=5)
agent = create_agent(
llm=llm,
tools=tools,
middleware=[limit_middleware]
)
3. ConversationSummaryMiddleware(对话摘要)
from langchain.agents.middleware import ConversationSummaryMiddleware
# 当对话超过 10 轮时自动摘要
summary_middleware = ConversationSummaryMiddleware(
max_turns=10,
summary_model=llm # 用于生成摘要的模型
)
agent = create_agent(
llm=llm,
tools=tools,
middleware=[summary_middleware]
)
4. PIISanitizerMiddleware(PII 脱敏)
from langchain.agents.middleware import PIISanitizerMiddleware
import re
class CustomPIISanitizer(PIISanitizerMiddleware):
"""
自定义 PII 脱敏中间件
"""
def sanitize(self, text: str) -> str:
# 脱敏手机号
text = re.sub(r'1[3-9]\d{9}', '***PHONE***', text)
# 脱敏身份证号
text = re.sub(r'\d{17}[\dXx]', '***ID***', text)
# 脱敏邮箱
text = re.sub(r'[\w.-]+@[\w.-]+\.\w+', '***EMAIL***', text)
return text
pii_middleware = CustomPIISanitizer()
3.5 LangChain 中间件实战案例
案例 1:日志记录中间件
import logging
from typing import Any, Dict, List
from langchain.agents.middleware import AgentMiddleware
# 配置日志
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
)
logger = logging.getLogger(__name__)
class LoggingMiddleware(AgentMiddleware):
"""
全面的日志记录中间件
记录 Agent 执行的完整生命周期
"""
async def before_agent(
self,
query: str,
context: Dict[str, Any]
) -> Dict[str, Any]:
logger.info(f"🚀 Agent 开始执行 | Query: {query}")
context['start_time'] = time.time()
return {"query": query, "context": context}
async def before_model(
self,
messages: List[Dict[str, Any]],
context: Dict[str, Any]
) -> List[Dict[str, Any]]:
logger.info(f"🤖 调用模型 | Messages: {len(messages)}")
return messages
async def after_model(
self,
response: Any,
context: Dict[str, Any]
) -> Any:
logger.info(f"✅ 模型响应 | Content: {response.content[:100]}...")
return response
async def wrap_tool_call(
self,
tool_name: str,
tool_input: Dict[str, Any],
context: Dict[str, Any]
) -> Any:
logger.info(f"🔧 调用工具 | {tool_name} | Input: {tool_input}")
try:
result = await self.call_tool(tool_name, tool_input)
logger.info(f"✅ 工具执行成功 | {tool_name}")
return result
except Exception as e:
logger.error(f"❌ 工具执行失败 | {tool_name} | Error: {e}")
raise
async def after_agent(
self,
result: Any,
context: Dict[str, Any]
) -> Any:
duration = time.time() - context.get('start_time', 0)
logger.info(f"🏁 Agent 执行完成 | 耗时: {duration:.2f}s")
return result
案例 2:重试机制中间件
import asyncio
from typing import Any, Dict
from langchain.agents.middleware import AgentMiddleware
class RetryMiddleware(AgentMiddleware):
"""
工具调用重试中间件
当工具调用失败时自动重试
"""
def __init__(self, max_retries: int = 3, delay: float = 1.0):
self.max_retries = max_retries
self.delay = delay
async def wrap_tool_call(
self,
tool_name: str,
tool_input: Dict[str, Any],
context: Dict[str, Any]
) -> Any:
last_exception = None
for attempt in range(self.max_retries):
try:
result = await self.call_tool(tool_name, tool_input)
if attempt > 0:
logger.info(f"✅ 第 {attempt + 1} 次重试成功")
return result
except Exception as e:
last_exception = e
logger.warning(
f"⚠️ 工具调用失败 | {tool_name} | "
f"第 {attempt + 1} 次尝试 | Error: {e}"
)
if attempt < self.max_retries - 1:
wait_time = self.delay * (2 ** attempt) # 指数退避
logger.info(f"⏳ 等待 {wait_time}s 后重试...")
await asyncio.sleep(wait_time)
# 所有重试都失败
logger.error(f"❌ 工具调用最终失败 | {tool_name}")
raise last_exception
案例 3:权限控制中间件
from typing import Any, Dict, List
from langchain.agents.middleware import AgentMiddleware
class PermissionMiddleware(AgentMiddleware):
"""
工具权限控制中间件
根据用户角色限制可使用的工具
"""
def __init__(self, user_role: str):
self.user_role = user_role
# 定义角色权限映射
self.role_permissions = {
"admin": ["*"], # 管理员可以使用所有工具
"user": ["search", "calculator", "weather"], # 普通用户
"guest": ["calculator"] # 访客
}
def _can_use_tool(self, tool_name: str) -> bool:
"""检查用户是否有权限使用工具"""
allowed_tools = self.role_permissions.get(self.user_role, [])
return "*" in allowed_tools or tool_name in allowed_tools
async def wrap_tool_call(
self,
tool_name: str,
tool_input: Dict[str, Any],
context: Dict[str, Any]
) -> Any:
if not self._can_use_tool(tool_name):
raise PermissionError(
f"用户角色 '{self.user_role}' 无权使用工具 '{tool_name}'"
)
return await self.call_tool(tool_name, tool_input)
四、FastAPI 与 LangChain 中间件对比
4.1 设计理念对比
| 维度 | FastAPI 中间件 | LangChain 中间件 |
|---|---|---|
| 设计模式 | 洋葱模型(Onion Model) | 钩子函数(Hooks) |
| 核心思想 | 请求/响应拦截 | 生命周期干预 |
| 数据流向 | 单向:Request → Response | 多阶段:多轮对话循环 |
| 状态管理 | 无状态(每次请求独立) | 有状态(维护对话上下文) |
| 组合方式 | 链式嵌套 | 列表顺序执行 |
4.2 实现机制对比
# FastAPI: 洋葱模型,嵌套调用
async def middleware1(request, call_next):
# 前置处理
response = await call_next(request) # 进入下一层
# 后置处理
return response
# LangChain: 钩子函数,阶段干预
class MyMiddleware(AgentMiddleware):
async def before_agent(self, query, context):
# Agent 执行前
pass
async def before_model(self, messages, context):
# 模型调用前
pass
async def after_model(self, response, context):
# 模型调用后
pass
4.3 应用场景对比
| 场景 | FastAPI | LangChain |
|---|---|---|
| 日志记录 | ✅ HTTP 请求/响应日志 | ✅ Agent 执行流程日志 |
| 认证授权 | ✅ API 访问控制 | ✅ 工具使用权限控制 |
| 限流控制 | ✅ 接口频率限制 | ✅ 模型调用次数限制 |
| 错误处理 | ✅ HTTP 异常统一处理 | ✅ 工具调用失败重试 |
| 数据转换 | ✅ 请求/响应格式化 | ✅ 消息内容脱敏/摘要 |
| 人工介入 | ❌ 不适用 | ✅ 人工审核确认 |
五、实战:构建完整的 Agent API 服务
5.1 项目架构
agent_api/
├── main.py # FastAPI 应用入口
├── middleware/
│ ├── __init__.py
│ ├── auth.py # JWT 认证中间件
│ ├── rate_limit.py # 限流中间件
│ └── logging.py # 日志中间件
├── agent/
│ ├── __init__.py
│ ├── agent.py # LangChain Agent 配置
│ └── middleware.py # Agent 中间件
├── tools/
│ └── __init__.py # 工具定义
└── requirements.txt
5.2 完整代码实现
1. LangChain Agent 中间件 (agent/middleware.py)
import time
import logging
from typing import Any, Dict, List
from langchain.agents.middleware import AgentMiddleware
logger = logging.getLogger(__name__)
class AgentLoggingMiddleware(AgentMiddleware):
"""Agent 日志中间件"""
async def before_agent(self, query: str, context: Dict[str, Any]):
context['start_time'] = time.time()
logger.info(f"[Agent] 开始处理: {query[:50]}...")
return {"query": query, "context": context}
async def wrap_tool_call(self, tool_name: str, tool_input: Dict, context: Dict):
logger.info(f"[Agent] 调用工具: {tool_name}")
start = time.time()
result = await self.call_tool(tool_name, tool_input)
logger.info(f"[Agent] 工具 {tool_name} 执行完成, 耗时: {time.time()-start:.2f}s")
return result
async def after_agent(self, result: Any, context: Dict):
duration = time.time() - context.get('start_time', 0)
logger.info(f"[Agent] 执行完成, 总耗时: {duration:.2f}s")
return result
class AgentRateLimitMiddleware(AgentMiddleware):
"""Agent 调用限制中间件"""
def __init__(self, max_tool_calls: int = 10):
self.max_tool_calls = max_tool_calls
self.call_count = 0
async def wrap_tool_call(self, tool_name: str, tool_input: Dict, context: Dict):
self.call_count += 1
if self.call_count > self.max_tool_calls:
raise Exception(f"工具调用次数超过限制: {self.max_tool_calls}")
return await self.call_tool(tool_name, tool_input)
2. FastAPI 中间件 (middleware/)
# middleware/logging.py
import time
import logging
from fastapi import Request
from starlette.middleware.base import BaseHTTPMiddleware
logger = logging.getLogger(__name__)
class APILoggingMiddleware(BaseHTTPMiddleware):
"""API 请求日志中间件"""
async def dispatch(self, request: Request, call_next):
start = time.time()
# 记录请求
logger.info(f"[API] {request.method} {request.url.path}")
# 处理请求
response = await call_next(request)
# 记录响应
duration = time.time() - start
logger.info(
f"[API] {request.method} {request.url.path} "
f"- {response.status_code} - {duration:.3f}s"
)
return response
# middleware/rate_limit.py
import time
from collections import defaultdict
from fastapi import Request
from starlette.middleware.base import BaseHTTPMiddleware
from fastapi.responses import JSONResponse
class APIRateLimitMiddleware(BaseHTTPMiddleware):
"""API 限流中间件"""
def __init__(self, app, max_requests: int = 100, window: int = 60):
super().__init__(app)
self.max_requests = max_requests
self.window = window
self.requests = defaultdict(list)
async def dispatch(self, request: Request, call_next):
client_ip = request.client.host
current_time = time.time()
# 清理过期记录
self.requests[client_ip] = [
t for t in self.requests[client_ip]
if current_time - t < self.window
]
# 检查限流
if len(self.requests[client_ip]) >= self.max_requests:
return JSONResponse(
status_code=429,
content={"error": "Rate limit exceeded"}
)
self.requests[client_ip].append(current_time)
return await call_next(request)
3. FastAPI 主应用 (main.py)
from fastapi import FastAPI, HTTPException
from fastapi.middleware.cors import CORSMiddleware
from pydantic import BaseModel
import logging
# 配置日志
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
)
# 导入中间件
from middleware.logging import APILoggingMiddleware
from middleware.rate_limit import APIRateLimitMiddleware
# 导入 Agent
from agent.agent import create_agent_with_middleware
app = FastAPI(title="Agent API Service")
# 添加 FastAPI 中间件
app.add_middleware(APILoggingMiddleware)
app.add_middleware(APIRateLimitMiddleware, max_requests=100, window=60)
app.add_middleware(
CORSMiddleware,
allow_origins=["*"],
allow_methods=["POST"],
allow_headers=["*"],
)
# 创建 Agent 实例
agent = create_agent_with_middleware()
class QueryRequest(BaseModel):
query: str
session_id: str = "default"
class QueryResponse(BaseModel):
result: str
tool_calls: list
duration: float
@app.post("/agent/query", response_model=QueryResponse)
async def agent_query(request: QueryRequest):
"""
调用 Agent 处理查询
"""
import time
start = time.time()
try:
# 调用 Agent
result = await agent.ainvoke({
"input": request.query
})
duration = time.time() - start
return QueryResponse(
result=result["output"],
tool_calls=result.get("intermediate_steps", []),
duration=duration
)
except Exception as e:
logging.error(f"Agent 执行失败: {e}")
raise HTTPException(status_code=500, detail=str(e))
@app.get("/health")
async def health_check():
"""健康检查"""
return {"status": "healthy"}
if __name__ == "__main__":
import uvicorn
uvicorn.run(app, host="0.0.0.0", port=8000)
4. Agent 配置 (agent/agent.py)
from langchain.agents import create_agent
from langchain_openai import ChatOpenAI
from langchain.tools import Tool
from agent.middleware import AgentLoggingMiddleware, AgentRateLimitMiddleware
def create_agent_with_middleware():
"""
创建带中间件的 Agent
"""
# 创建 LLM
llm = ChatOpenAI(
model="gpt-4",
temperature=0.7
)
# 定义工具
tools = [
Tool(
name="search",
func=lambda x: f"搜索结果: {x}",
description="搜索信息的工具"
),
Tool(
name="calculator",
func=lambda x: str(eval(x)),
description="计算数学表达式的工具"
),
]
# 创建中间件
middleware = [
AgentLoggingMiddleware(), # 日志记录
AgentRateLimitMiddleware(max_tool_calls=10), # 限流控制
]
# 创建 Agent
agent = create_agent(
llm=llm,
tools=tools,
middleware=middleware
)
return agent
5.3 运行和测试
# 安装依赖
pip install fastapi uvicorn langchain langchain-openai
# 启动服务
python main.py
# 测试 API
curl -X POST "http://localhost:8000/agent/query" \
-H "Content-Type: application/json" \
-d '{"query": "计算 123 * 456"}'
六、最佳实践与注意事项
6.1 FastAPI 中间件最佳实践
-
保持轻量:中间件应该快速执行,避免阻塞请求
-
异常处理:始终捕获异常,避免影响后续处理
-
状态管理:避免在中间件中存储请求相关的状态
-
顺序重要:中间件按照添加顺序嵌套执行
✅ 好的实践:快速执行,正确传递
@app.middleware("http")
async def good_middleware(request: Request, call_next):
# 快速前置处理
response = await call_next(request)
# 快速后置处理
return response❌ 避免:长时间阻塞
@app.middleware("http")
async def bad_middleware(request: Request, call_next):
time.sleep(5) # 不要这样做!
response = await call_next(request)
return response
6.2 LangChain 中间件最佳实践
-
幂等性:中间件应该可以安全地多次执行
-
上下文传递:使用 context 字典传递跨阶段数据
-
错误处理:提供有意义的错误信息
-
性能考虑:避免在关键路径上执行耗时操作
✅ 好的实践:使用 context 传递数据
async def before_agent(self, query: str, context: Dict):
context['start_time'] = time.time() # 记录开始时间
return {"query": query, "context": context}async def after_agent(self, result: Any, context: Dict):
duration = time.time() - context['start_time'] # 使用 context 数据
return result
6.3 常见陷阱
| 陷阱 | 说明 | 解决方案 |
|---|---|---|
| 中间件顺序 | 顺序错误导致逻辑异常 | 仔细规划中间件执行顺序 |
| 异常吞噬 | 中间件捕获异常但不处理 | 始终正确传递或处理异常 |
| 状态泄漏 | 请求间状态相互影响 | 使用上下文对象,避免全局状态 |
| 性能瓶颈 | 中间件执行过慢 | 异步处理,避免阻塞操作 |
| 循环依赖 | 中间件间相互依赖 | 保持中间件独立性 |
七、总结
核心要点回顾
- 中间件的本质:在核心逻辑前后插入横切关注点的机制
- FastAPI 中间件:基于洋葱模型,适合 HTTP 请求/响应处理
- LangChain 中间件:基于钩子函数,适合 Agent 生命周期干预
- 两者结合:可以构建完整的 AI API 服务体系
选择建议
| 场景 | 推荐方案 |
|---|---|
| 纯 Web API 服务 | FastAPI 中间件 |
| AI Agent 应用 | LangChain 中间件 |
| Agent + API 服务 | 两者结合使用 |
学习路径建议
- 入门:先掌握 FastAPI 中间件,理解洋葱模型
- 进阶:学习 LangChain 中间件,理解钩子机制
- 实战:结合两者构建完整的 AI 应用
- 深入:阅读源码,理解底层实现原理
📚 延伸阅读:
本文基于 FastAPI 0.100+ 和 LangChain v1.0+ 编写,建议读者使用最新版本进行实践。