纲要
- 速率限制的产生背景与影响
- LangChain 内置速率限制器
InMemoryRateLimiter核心参数
- 缓存机制的必要性与原理
- 短期缓存(内存)与长期缓存(持久化)
- 实战:速率限制与缓存结合
- 项目结构
- 速率限制器配置
- 缓存方案对比(内存与 SQLite)
- 代码示例
- 关键要点总结
速率限制:为什么需要关注
当前主流大模型提供商(OpenAI、DeepSeek、Anthropic 等)都会对 API 实施速率限制。例如 OpenAI 对免费用户限制每分钟请求数(RPM)和每分钟 Token 数(TPM),付费用户则有更高的配额。一旦请求超过限制,API 将返回错误(如 429),导致应用不可用,严重影响用户体验。
在 AI Agent 开发中,如果不做任何防护,高频调用很容易触发限流。因此,必须在客户端侧主动实施流量整形,确保请求速率始终在安全范围内。
LangChain 中的速率限制器
LangChain 提供了 InMemoryRateLimiter 作为内置速率控制组件。它位于 langchain_core.rate_limiters 模块中,通过令牌桶算法限制单位时间内的请求数。
核心参数如下:
| 参数 | 说明 | 示例 |
|---|---|---|
requests_per_second |
每秒允许的最大请求数 | 1.0(每秒 1 次)或 0.1(每 10 秒 1 次) |
check_every_n_seconds |
每隔多少秒检查一次是否允许新请求 | 0.1(每 100 毫秒检查) |
max_burst_size |
允许的最大突发请求数 | 10 |
使用时,只需在实例化模型组件时传入 rate_limiter 参数即可。
缓存机制:减少不必要的 API 调用
除了限制速率,缓存也是降低 API 调用频率的有效手段。其流程如下:
#mermaid-svg-JFcJEwiw8ok3AgMw{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-JFcJEwiw8ok3AgMw .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-JFcJEwiw8ok3AgMw .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-JFcJEwiw8ok3AgMw .error-icon{fill:#552222;}#mermaid-svg-JFcJEwiw8ok3AgMw .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-JFcJEwiw8ok3AgMw .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-JFcJEwiw8ok3AgMw .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-JFcJEwiw8ok3AgMw .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-JFcJEwiw8ok3AgMw .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-JFcJEwiw8ok3AgMw .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-JFcJEwiw8ok3AgMw .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-JFcJEwiw8ok3AgMw .marker{fill:#333333;stroke:#333333;}#mermaid-svg-JFcJEwiw8ok3AgMw .marker.cross{stroke:#333333;}#mermaid-svg-JFcJEwiw8ok3AgMw svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-JFcJEwiw8ok3AgMw p{margin:0;}#mermaid-svg-JFcJEwiw8ok3AgMw .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-JFcJEwiw8ok3AgMw .cluster-label text{fill:#333;}#mermaid-svg-JFcJEwiw8ok3AgMw .cluster-label span{color:#333;}#mermaid-svg-JFcJEwiw8ok3AgMw .cluster-label span p{background-color:transparent;}#mermaid-svg-JFcJEwiw8ok3AgMw .label text,#mermaid-svg-JFcJEwiw8ok3AgMw span{fill:#333;color:#333;}#mermaid-svg-JFcJEwiw8ok3AgMw .node rect,#mermaid-svg-JFcJEwiw8ok3AgMw .node circle,#mermaid-svg-JFcJEwiw8ok3AgMw .node ellipse,#mermaid-svg-JFcJEwiw8ok3AgMw .node polygon,#mermaid-svg-JFcJEwiw8ok3AgMw .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-JFcJEwiw8ok3AgMw .rough-node .label text,#mermaid-svg-JFcJEwiw8ok3AgMw .node .label text,#mermaid-svg-JFcJEwiw8ok3AgMw .image-shape .label,#mermaid-svg-JFcJEwiw8ok3AgMw .icon-shape .label{text-anchor:middle;}#mermaid-svg-JFcJEwiw8ok3AgMw .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-JFcJEwiw8ok3AgMw .rough-node .label,#mermaid-svg-JFcJEwiw8ok3AgMw .node .label,#mermaid-svg-JFcJEwiw8ok3AgMw .image-shape .label,#mermaid-svg-JFcJEwiw8ok3AgMw .icon-shape .label{text-align:center;}#mermaid-svg-JFcJEwiw8ok3AgMw .node.clickable{cursor:pointer;}#mermaid-svg-JFcJEwiw8ok3AgMw .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-JFcJEwiw8ok3AgMw .arrowheadPath{fill:#333333;}#mermaid-svg-JFcJEwiw8ok3AgMw .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-JFcJEwiw8ok3AgMw .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-JFcJEwiw8ok3AgMw .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-JFcJEwiw8ok3AgMw .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-JFcJEwiw8ok3AgMw .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-JFcJEwiw8ok3AgMw .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-JFcJEwiw8ok3AgMw .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-JFcJEwiw8ok3AgMw .cluster text{fill:#333;}#mermaid-svg-JFcJEwiw8ok3AgMw .cluster span{color:#333;}#mermaid-svg-JFcJEwiw8ok3AgMw div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-JFcJEwiw8ok3AgMw .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-JFcJEwiw8ok3AgMw rect.text{fill:none;stroke-width:0;}#mermaid-svg-JFcJEwiw8ok3AgMw .icon-shape,#mermaid-svg-JFcJEwiw8ok3AgMw .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-JFcJEwiw8ok3AgMw .icon-shape p,#mermaid-svg-JFcJEwiw8ok3AgMw .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-JFcJEwiw8ok3AgMw .icon-shape .label rect,#mermaid-svg-JFcJEwiw8ok3AgMw .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-JFcJEwiw8ok3AgMw .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-JFcJEwiw8ok3AgMw .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-JFcJEwiw8ok3AgMw :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 是
否
用户请求
缓存命中?
返回缓存结果
调用大模型 API
获取响应
写入缓存
LangChain 的缓存分为两种:
- 内存缓存 (
InMemoryCache):存储在进程内存中,重启即失,适合短期重用。 - 持久化缓存:如基于 SQLite 的实现,可长期保存,适合跨会话复用。
结合速率限制与缓存,可以大幅提升应用稳定性和成本效率。
项目结构
本示例将实现:
- 使用
InMemoryRateLimiter控制请求频率。 - 对比内存缓存和 SQLite 缓存的实际效果。
- 演示如何通过
usage_metadata追踪 Token 消耗,验证缓存是否真正减少了 API 调用。
目录结构如下:
dir
├── main.py
└── requirements.txt
依赖清单(requirements.txt):
bash
langchain-openai>=0.3.0
langchain-core>=0.3.0
langchain-community>=0.3.0
代码示例
python
import os, time
from langchain_openai import ChatOpenAI
from langchain_core.rate_limiters import InMemoryRateLimiter
from langchain_core.caches import InMemoryCache
from langchain_community.caches import SQLiteCache
import langchain
# ---------- 1. 配置速率限制器 ----------
rate_limiter = InMemoryRateLimiter(
requests_per_second=0.5, # 每 2 秒允许 1 次请求
check_every_n_seconds=0.1, # 每 0.1 秒检查一次
max_burst_size=1 # 最大突发请求为 1
)
# ---------- 2. 初始化模型(使用 GPT-3.5 以降低成本) ----------
llm = ChatOpenAI(
model="gpt-3.5-turbo",
temperature=0,
max_tokens=100,
api_key=os.getenv("OPENAI_API_KEY"),
rate_limiter=rate_limiter # 注入速率限制器
)
# ---------- 3. 辅助函数:打印 Token 用量 ----------
def print_usage(response):
usage = response.usage_metadata
if usage:
print(f" 输入: {usage.get('input_tokens')}, 输出: {usage.get('output_tokens')}, 总计: {usage.get('total_tokens')}")
else:
print(" 未获取到 Token 用量")
# ---------- 4. 演示速率限制效果 ----------
print("=== 速率限制演示:连续发送 5 个相同请求 ===")
start_time = time.time()
prompt = "用一句话介绍深度学习。"
for i in range(5):
t0 = time.time()
response = llm.invoke(prompt)
t1 = time.time()
print(f"请求 {i+1}: 耗时 {t1-t0:.2f}s")
print_usage(response)
print(f"总耗时: {time.time() - start_time:.2f}s\n")
# ---------- 5. 缓存机制对比 ----------
# 先移除速率限制器以便专注于缓存测试
llm_no_limit = ChatOpenAI(
model="gpt-3.5-turbo",
temperature=0,
max_tokens=100,
api_key=os.getenv("OPENAI_API_KEY"),
)
# 5.1 无缓存
print("=== 无缓存:重复请求相同问题 ===")
langchain.llm_cache = None # 清除全局缓存
for i in range(3):
t0 = time.time()
res = llm_no_limit.invoke("什么是 Transformer?")
t1 = time.time()
print(f"第 {i+1} 次: 耗时 {t1-t0:.2f}s, 返回前 20 字: {res.content[:20]}...")
# 5.2 内存缓存
print("\n=== 启用内存缓存 ===")
langchain.llm_cache = InMemoryCache()
for i in range(3):
t0 = time.time()
res = llm_no_limit.invoke("什么是 Transformer?")
t1 = time.time()
print(f"第 {i+1} 次: 耗时 {t1-t0:.2f}s, 返回前 20 字: {res.content[:20]}...")
# 第一次会调用 API,后续直接命中缓存
# 5.3 SQLite 持久化缓存
print("\n=== 启用 SQLite 缓存 ===")
langchain.llm_cache = SQLiteCache(database_path=".langchain_cache.db")
for i in range(3):
t0 = time.time()
res = llm_no_limit.invoke("什么是 Transformer?")
t1 = time.time()
print(f"第 {i+1} 次: 耗时 {t1-t0:.2f}s, 返回前 20 字: {res.content[:20]}...")
# 如果之前已缓存,第一次也可能快速返回
运行说明:
- 代码依赖
langchain-openai、langchain-core、langchain-community。 - 需要设置环境变量
OPENAI_API_KEY为有效的 OpenAI API 密钥。 - 速率限制演示部分会因
requests_per_second=0.5而明显降低请求速度。 - 缓存演示中,第一次调用会真正请求 API,后续调用几乎瞬间完成,表明缓存生效。
- SQLite 缓存文件
.langchain_cache.db会在当前目录生成,可跨脚本持久化。
关键要点总结
- 几乎所有的商用大模型 API 都有速率限制,忽略它会导致应用频繁报错。
- LangChain 提供
InMemoryRateLimiter用于客户端流量控制,只需传入rate_limiter参数即可。 - 缓存是减少 API 调用、降低成本的有效手段,LangChain 支持内存缓存和 SQLite 持久化缓存,切换非常方便。
- 在实际 AI Agent 开发中,建议将速率限制与缓存结合使用,以保障服务的稳定性和经济性。