AI Agent白手起家23: 大模型速率限制应对与缓存机制实践

纲要

  • 速率限制的产生背景与影响
  • 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 的实现,可长期保存,适合跨会话复用。

结合速率限制与缓存,可以大幅提升应用稳定性和成本效率。

项目结构

本示例将实现:

  1. 使用 InMemoryRateLimiter 控制请求频率。
  2. 对比内存缓存和 SQLite 缓存的实际效果。
  3. 演示如何通过 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-openailangchain-corelangchain-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 开发中,建议将速率限制与缓存结合使用,以保障服务的稳定性和经济性。
相关推荐
计算机魔术师1 小时前
Midjourney V8.2 发布:美学与个性化全面升级,低质图率显著下降
人工智能·ai编程
移动云开发者联盟1 小时前
移动云携手MiniMax首发MiniMax-H3, AI视频一键成片!
人工智能·音视频
Highcharts.js1 小时前
五大痛点拖慢数据分析平台决策效率:Highchart可视化与AI分析解决方案解析
人工智能·信息可视化·数据分析·数据可视化·highcharts·ai可视化分析
义嘉泰2 小时前
手腕上的小手电:智能手表怎么把“绿光”变成心率
人工智能·智能手表
Canace2 小时前
AI 生成到 90% 突然断了:你的解决方案是?
前端·人工智能
湘美书院--湘美谈教育2 小时前
AI时代的奥德赛:算法星空,寻找精神归航
大数据·人工智能·深度学习·机器学习·生活
瓦学妹2 小时前
2026亚马逊本土店怎么注册?入驻流程、资料准备与常见问题
大数据·运维·人工智能
YucongCai2 小时前
Opengovernment(智慧城市) v0.0.3 攻防一体的智能体主动网络安全平台:ActiveCybersecurity v0.2
人工智能·web安全·智慧城市
腾讯云大数据2 小时前
从多模态数据处理到模型训练:腾讯云EMR-Ray打通Data+AI全流程
人工智能·云计算·腾讯云·mapreduce·腾讯云大数据