学习 LiteLLM 的缓存系统

随着我们的 LLM 网关承载的流量越来越大、接入的模型越来越多,除了稳定性和可靠性,成本和性能 也成为了关键的考量因素。实际上,我们的应用每天都在处理很多相似的查询,比如客服系统中的常见问题、代码生成中的重复模式、文档摘要中的相似内容。如果每次都调用真实的 LLM API,不仅延迟高、成本昂贵,还可能触发供应商的速率限制。而且,对于相同或相似的查询,LLM 的回答往往是一致的或高度相似的,这就为缓存的应用提供了天然的基础。

LiteLLM 提供了一套完整的缓存解决方案,它不仅能够缓存完全相同的请求(精确缓存),还能够基于语义相似性缓存相似的请求(语义缓存),从而显著降低成本,并大幅提升性能,同时减轻对上游 API 的压力,提高了系统稳定性。

今天这篇文章,我们就来深入了解一下 LiteLLM 的缓存系统。

缓存类型一览

LiteLLM 支持 8 种不同的缓存后端,每种都针对特定的使用场景:

传统缓存

  • 内存缓存(In-Memory):最快的访问速度,适合单机部署和开发测试
  • 磁盘缓存(Disk):持久化存储,适合单机长期缓存
  • Redis 缓存:分布式缓存,适合多实例部署和生产环境

云存储缓存

  • S3 缓存:利用 AWS S3 进行大规模、低成本的缓存存储
  • GCS 缓存:基于 Google Cloud Storage 的缓存解决方案
  • Azure Blob 缓存:微软 Azure 的对象存储缓存

智能缓存

  • Redis 语义缓存:基于向量相似度的语义匹配缓存
  • Qdrant 语义缓存:专业向量数据库的语义缓存解决方案

基本使用

首先我们来体验下最简单的内存缓存,在 config.yaml 文件中添加如下配置:

yaml 复制代码
litellm_settings:
  cache: true
  cache_params:
    type: local

然后重启 LiteLLM Proxy 服务。发送第一次请求:

bash 复制代码
$ curl -X POST 'http://127.0.0.1:4000/chat/completions' \
  -H 'Authorization: Bearer sk-1234' \
  -H 'Content-Type: application/json' \
  -d '{
    "model": "gpt-4o",
    "messages": [{"role": "user", "content": "给我讲个笑话"}]
  }'

第一次请求会调用真实的 API,耗时可能在 1-3 秒。再发送完全相同的请求(加了 -v 选项用于打印响应头):

bash 复制代码
$ curl -v -X POST 'http://127.0.0.1:4000/chat/completions' \
  -H 'Authorization: Bearer sk-1234' \
  -H 'Content-Type: application/json' \
  -d '{
    "model": "gpt-4o",
    "messages": [{"role": "user", "content": "给我讲个笑话"}]
  }'

第二次请求从缓存返回,耗时通常在 50-200 毫秒,响应头中会包含缓存相关信息:

makefile 复制代码
x-litellm-cache-key: 6b86d8cb3ab300d7cbe071f93176e28cc25d9da7f2db95dd8ad078176faddc94

这个 Key 是根据请求内容生成的,相同的请求参数生成的 Key 也是相同的。

Redis 缓存

内存缓存适合单机部署和开发测试,生产环境一般使用 Redis 作为缓存后端。可以用 Docker 在本地快速启一个 Redis 示例:

bash 复制代码
$ docker run -d \
  --name redis-stack \
  -p 6379:6379 \
  -p 8001:8001 \
  redis/redis-stack:latest

注意我这里使用的是 Redis Stack 镜像,它是一个包含多个模块的集成软件套件,在 Redis 的基础上,扩展了 搜索(RedisSearch)JSON(RedisJSON)图数据库(RedisGraph)时间序列(RedisTimeSeries)布隆过滤器(RedisBloom) 等功能,提供了更丰富的数据处理能力,适用于构建复杂应用。相对于 Redis Server 只是核心的内存数据结构存储,更侧重于缓存和消息队列等场景。

最简单的 Redis 缓存配置如下:

yaml 复制代码
litellm_settings:
  cache: true
  cache_params:
    type: redis
    host: localhost          # Redis 服务器地址
    port: 6379               # Redis 端口
    password: your_password  # Redis 密码(可选)
    ttl: 600                 # 默认缓存时间(秒)

对于大规模部署,LiteLLM 支持配置 Redis 集群或哨兵。

验证方法和内存缓存一样,第二次请求从缓存返回,并且在 Redis 中应该能找到 x-litellm-cache-key 对应的缓存键:

Redis 语义缓存

Redis 不仅可以用来做普通缓存,还可以用来做向量数据库。我们可以将文本、图像等非结构化数据转换为向量,存储到 Redis 中,并利用 Redis 的内存优势实现毫秒级的向量相似性搜索。

LiteLLM 基于 Redis 向量数据库实现了语义缓存功能,语义缓存 不同于传统的精确匹配缓存,它能够理解查询的语义含义,并返回语义相似查询的缓存结果。即使这些查询的字面表达完全不同,语义缓存也能识别它们的相似性并返回相同的结果,大大提高了缓存命中率。

Redis 语义缓存基于 Redis Vector Library (RedisVL) 实现,首先需要安装依赖:

bash 复制代码
$ pip install redisvl==0.4.1

然后在 LiteLLM 的配置文件中开启 Redis 语义缓存:

yaml 复制代码
litellm_settings:
  cache: true
  cache_params:
    type: redis-semantic              # 语义缓存类型
    host: localhost
    port: 6379
    password: your_password
    ttl: 600
    similarity_threshold: 0.8                                     # 相似度阈值(0-1,1为完全匹配)
    redis_semantic_cache_embedding_model: text-embedding-3-large  # 嵌入模型名称

其中 similarity_threshold 为相似度阈值,范围 [0-1]redis_semantic_cache_embedding_model 为嵌入模型名称,必须是在 model_list 中定义。

注意,如果你的 Redis 没有密码,启动可能会报错 Missing required Redis configuration: REDIS_PASSWORD。设置一下环境变量 export REDIS_PASSWORD= 即可。

可以使用两个相似的请求来验证,第一次请求:

bash 复制代码
$ curl -X POST 'http://127.0.0.1:4000/chat/completions' \
  -H 'Authorization: Bearer sk-1234' \
  -H 'Content-Type: application/json' \
  -d '{
    "model": "gpt-4o",
    "messages": [{"role": "user", "content": "什么是人工智能?"}]
  }'

第二次请求:

bash 复制代码
$ curl -v -X POST 'http://127.0.0.1:4000/chat/completions' \
  -H 'Authorization: Bearer sk-1234' \
  -H 'Content-Type: application/json' \
  -d '{
    "model": "gpt-4o",
    "messages": [{"role": "user", "content": "AI 是什么?"}]
  }'

第二次请求的响应头中不仅包含了缓存 Key,还包含了相似度信息:

makefile 复制代码
x-litellm-cache-key: 9cdf6246deaee1f8d96aa8b093029bc129f7eca32f2f8f4a9fd0352138e6ea3e
x-litellm-semantic-similarity: 0.162912428379

我们打开 Redis 管理页面,可以发现多了一个 litellm_semantic_cache_index 开头的键:

它的内容包含了请求的 Prompt 和对应的响应:

RedisVL 库介绍

RedisVL (Redis Vector Library) 是 Redis 官方提供的 Python 客户端库,专为 AI 应用设计,提供了高级的向量搜索抽象,主要特性包括:

  • 简化的 API:高级抽象,简化向量搜索操作
  • 强大的查询能力:支持混合查询、过滤条件、聚合等
  • 语义缓存:专门为 LLM 缓存优化的 LLMCache 组件,LiteLLM 的 Redis 语义缓存就是基于它实现的

这一节我们通过一个简单示例学习它的基本使用。

首先通过 Schema 创建索引:

python 复制代码
from redisvl.index import SearchIndex
from redis import Redis

# 创建连接
client = Redis.from_url("redis://localhost:6379")

# 使用字典定义 Schema
schema = {
  "index": {
    "name": "product_index",
    "prefix": "product:",
  },
  "fields": [
    {"name": "title", "type": "text"},
    {"name": "brand", "type": "tag"},
    {"name": "price", "type": "numeric"},
    {"name": "category", "type": "tag"},
    {
      "name": "description_vector",
      "type": "vector",
      "attrs": {
        "dims": 3072,  # 向量维度
        "distance_metric": "cosine",  # 距离度量
        "algorithm": "flat",  # 向量索引算法
        "datatype": "float32"
      }
    }
  ]
}

# 创建索引
index = SearchIndex.from_dict(schema, redis_client=client)
index.create(overwrite=True)
print(f"索引创建成功!")

这是一个产品表,包含产品的名称、品牌、分类、价格、描述等信息,我们为描述加一个向量字段,用于语义搜索。

其中向量维度根据你使用的 Embedding 模型来定义,我这里使用的是 OpenAI 的 text-embedding-3-large 模型,维度是 3072 维。

接着定义一批示例数据,加载到 Redis 中:

python 复制代码
import numpy as np
from litellm import embedding

# 定义产品数据
products = [
  {
    "id": "p001",
    "title": "iPhone 15 Pro Max",
    "brand": "Apple",
    "category": "smartphone",
    "price": 1199,
    "description": "最新的 iPhone,配备 A17 Pro 芯片,48MP 主摄像头,钛金属机身"
  },
  {
    "id": "p002",
    "title": "MacBook Pro 14英寸",
    "brand": "Apple",
    "category": "laptop",
    "price": 1999,
    "description": "搭载 M3 芯片的专业笔记本电脑,适合开发者和创意工作者"
  },
  {
    "id": "p003",
    "title": "AirPods Pro",
    "brand": "Apple",
    "category": "audio",
    "price": 249,
    "description": "主动降噪无线耳机,空间音频技术,透明模式"
  },
  {
    "id": "p004",
    "title": "Samsung Galaxy S24",
    "brand": "Samsung",
    "category": "smartphone",
    "price": 899,
    "description": "Android 旗舰手机,AI 摄影功能,120Hz 显示屏"
  },
  {
    "id": "p005",
    "title": "Dell XPS 13",
    "brand": "Dell",
    "category": "laptop",
    "price": 1299,
    "description": "轻薄笔记本电脑,13.3英寸 4K 触摸屏,Intel 处理器"
  }
]

# 生成向量嵌入
for product in products:
  response = embedding('text-embedding-3-large', product["description"])
  product["description_vector"] = np.array(response.data[0]['embedding'], dtype=np.float32).tobytes()

# 加载产品数据到 Redis
for product in products:
  index.load([product], id_field="id")

print(f"成功加载 {len(products)} 个产品到索引中")

我们为每个产品的描述生成对应的向量,并通过 index.load() 将产品数据加载到 Redis 中。接下来就可以执行搜索了:

python 复制代码
from redisvl.index import SearchIndex
from redisvl.query import VectorQuery

# 使用已有索引
index = SearchIndex.from_existing('product_index', client)

# 生成查询向量
response = embedding('text-embedding-3-large', "适合程序员的电脑")
query_vector = np.array(response.data[0]['embedding'], dtype=np.float32).tobytes()

# 创建向量查询
vector_query = VectorQuery(
  vector=query_vector,
  vector_field_name="description_vector",
  return_fields=["title", "brand", "category", "price", "description"],
  num_results=3,
)

# 执行搜索
results = index.query(vector_query)

print(f"\n查询: '适合程序员的电脑'")
for doc in results:
  score = 1 - float(doc['vector_distance'])  # 转换为相似度分数
  print(f"- {doc['title']} ({doc['brand']}) - 相似度: {score:.3f}")
  print(f"  价格: ${doc['price']} | 描述: {doc['description']}")

搜索结果如下:

bash 复制代码
查询: '适合程序员的电脑'
- MacBook Pro 14英寸 (Apple) - 相似度: 0.570
  价格: $1999 | 描述: 搭载 M3 芯片的专业笔记本电脑,适合开发者和创意工作者
- Dell XPS 13 (Dell) - 相似度: 0.435
  价格: $1299 | 描述: 轻薄笔记本电脑,13.3英寸 4K 触摸屏,Intel 处理器
- iPhone 15 Pro Max (Apple) - 相似度: 0.293
  价格: $1199 | 描述: 最新的 iPhone,配备 A17 Pro 芯片,48MP 主摄像头,钛金属机身

缓存控制参数

LiteLLM 提供了灵活的缓存控制机制,允许在请求级别动态控制缓存行为。这些控制参数借鉴了 HTTP 缓存控制的设计理念,提供了细粒度的缓存管理能力。

参数 类型 说明 使用场景
ttl int 指定缓存时间(秒) 短期缓存、时效性内容
s-maxage int 只接受指定时间内的缓存(秒) 强制刷新、数据一致性
no-cache bool 跳过缓存读取,强制调用 API 需要最新数据
no-store bool 不存储响应到缓存 敏感内容、一次性查询
namespace str 自定义缓存命名空间 多租户、环境隔离

动态 TTL 控制

参数 ttl 用于控制缓存时间,我们可以为不同类型的请求设置不同的缓存时间:

python 复制代码
# 长期缓存:百科类知识
response = client.chat.completions.create(
  model="gpt-4o",
  messages=[{"role": "user", "content": "什么是量子计算?"}],
  extra_body={
    "cache": {
      "ttl": 86400  # 缓存 24 小时
    }
  }
)

# 短期缓存:实时性要求较高的内容
response = client.chat.completions.create(
  model="gpt-4o",
  messages=[{"role": "user", "content": "今天的股市表现如何?"}],
  extra_body={
    "cache": {
      "ttl": 300  # 缓存 5 分钟
    }
  }
)

缓存年龄控制

参数 s-maxage 控制只接受指定年龄内的缓存:

python 复制代码
response = client.chat.completions.create(
  model="gpt-4o",
  messages=[{"role": "user", "content": "最新的股票价格"}],
  extra_body={
    "cache": {
      "s-maxage": 60  # 只接受 1 分钟内的缓存
    }
  }
)

强制刷新缓存

当需要获取最新结果时,可以使用 no-cache 强制绕过缓存:

python 复制代码
response = client.chat.completions.create(
  model="gpt-4o",
  messages=[{"role": "user", "content": "最新的股票价格"}],
  extra_body={
    "cache": {
      "no-cache": True  # 跳过缓存检查
    }
  }
)

禁用缓存存储

对于包含敏感信息的查询,可以使用 no-store 不缓存当前响应:

python 复制代码
response = client.chat.completions.create(
  model="gpt-4o",
  messages=[{"role": "user", "content": "敏感信息查询"}],
  extra_body={
    "cache": {
      "no-store": True  # 不存储此响应
    }
  }
)

命名空间隔离

在多租户或多环境场景中,LiteLLM 支持命名空间来实现缓存隔离:

yaml 复制代码
litellm_settings:
  cache: true
  cache_params:
    type: redis
    namespace: "litellm.production"  # 命名空间前缀

这样存储在 Redis 中的键将变成 {namespace}:<hash> 格式,我们也可以在请求参数中动态控制,为不同的用户或应用使用独立的缓存空间:

python 复制代码
response = client.chat.completions.create(
  model="gpt-4o",
  messages=[{"role": "user", "content": "Hello"}],
  extra_body={
    "cache": {
      "namespace": "user:12345"  # 用户专用命名空间
    }
  }
)

默认关闭模式

对于需要严格控制的场景,可以将缓存设置为默认关闭,只在需要时显式启用:

yaml 复制代码
litellm_settings:
  cache: true
  cache_params:
    mode: default_off  # 缓存默认关闭

客户端需要显式启用缓存:

python 复制代码
response = client.chat.completions.create(
  model="gpt-4o",
  messages=[{"role": "user", "content": "Hello"}],
  extra_body={
    "cache": {"use-cache": True}  # 显式启用缓存
  }
)

小结

我们今天简单学习了 LiteLLM 的缓存系统。LiteLLM 提供了从简单的内存缓存到复杂的语义缓存等多种选择,每种缓存都有其特定的适用场景,可以根据业务需求灵活选择最合适的缓存策略。此外,LiteLLM 的缓存控制参数提供了细粒度的缓存管理能力,使开发者能够动态调整缓存行为,以适应不同场景的需求。

通过合理配置和使用这些缓存功能,应用可以更好地处理重复性和相似性的查询,有效降低对上游 LLM API 的压力,提升系统的稳定性与响应速度。

欢迎关注

如果这篇文章对您有所帮助,欢迎关注我的同名公众号:日习一技,每天学一点新技术

我会每天花一个小时,记录下我学习的点点滴滴。内容包括但不限于:

  • 某个产品的使用小窍门
  • 开源项目的实践和心得
  • 技术点的简单解读

目标是让大家用5分钟读完就能有所收获,不需要太费劲,但却可以轻松获取一些干货。不管你是技术新手还是老鸟,欢迎给我提建议,如果有想学习的技术,也欢迎交流!

相关推荐
坚果派·白晓明15 分钟前
AI驱动的命令行工具集x-cmd鸿蒙化适配后通过DevBox安装使用
人工智能·华为·harmonyos
GISer_Jing28 分钟前
前端营销技术实战:数据+AI实战指南
前端·javascript·人工智能
Dekesas969541 分钟前
【深度学习】基于Faster R-CNN的黄瓜幼苗智能识别与定位系统,农业AI新突破
人工智能·深度学习·r语言
大佐不会说日语~1 小时前
Spring AI Alibaba 的 ChatClient 工具注册与 Function Calling 实践
人工智能·spring boot·python·spring·封装·spring ai
CeshirenTester1 小时前
Playwright元素定位详解:8种定位策略实战指南
人工智能·功能测试·程序人生·单元测试·自动化
世岩清上2 小时前
AI驱动的智能运维:从自动化到自主化的技术演进与架构革新
运维·人工智能·自动化
K2_BPM2 小时前
告别“单点智能”:AI Agent如何重构企业生产力与流程?
人工智能
TMT星球2 小时前
深业云从人工智能产业投资基金设立,聚焦AI和具身智能相关产业
人工智能
鼾声鼾语2 小时前
matlab的ros2发布的消息,局域网内其他设备收不到情况吗?但是matlab可以订阅其他局域网的ros2发布的消息(问题总结)
开发语言·人工智能·深度学习·算法·matlab·isaaclab
Dingdangcat862 小时前
中药材图像识别与分类 RetinaNet-R101-FPN模型详解
人工智能·数据挖掘