引言
在前一篇文章中,我们深入探讨了 RedisVL 的SemanticCache—— 一个基于自建 Redis 索引的语义缓存解决方案。但有时候,你可能不想自己维护 Redis 集群和索引,而是希望使用一个托管的语义缓存服务,即开即用,免去运维烦恼。
这就是LangCache的用武之地。LangCache 是 Redis 官方提供的一项托管语义缓存服务,它通过 HTTP API 提供与SemanticCache类似的check/store接口。RedisVL 中的LangCacheSemanticCache类正是这个服务的轻量级封装,让你在使用习惯上与SemanticCache保持高度一致,但底层却由 LangCache 服务代为管理所有缓存存储和向量搜索逻辑。
本文将从零开始,带你了解如何配置和使用LangCacheSemanticCache,并指出它与自托管方案的异同。
前置条件
在开始之前,请确保:
- 安装了带有
langcache额外依赖的 RedisVL:pip install redisvl[langcache] - Python 版本 3.10+(与 RedisVL 要求一致)
- 拥有一个 LangCache 服务实例,并获得Cache ID和API Key。你可以通过 Redis Cloud 快速创建 LangCache 服务。
- (可选)如果你打算使用元数据/属性进行过滤,请先在 LangCache 控制台或 API 中配置好对应的属性(字段名和类型)。
能做的
完成本指南后,你将能够:
- 根据实际场景,在
SemanticCache和LangCacheSemanticCache之间做出合适的选择 - 使用凭证和默认 TTL 初始化
LangCacheSemanticCache - 实现“读穿透”缓存模式(先查缓存,未命中再调用 LLM,最后存储结果)
- 利用 LangCache 属性进行数据隔离和删除操作
- 掌握按条目覆盖 TTL、使用异步 API 以及执行删除操作的方法
- 了解当前版本与
SemanticCache相比存在的限制
选择SemanticCache还是LangCacheSemanticCache?
两者都提供语义缓存能力,但架构和适用场景有明显区别。下表帮你快速决策:
| 特性 | SemanticCache | LangCacheSemanticCache |
|---|---|---|
| 数据存储位置 | 你自己的 Redis 部署;RedisVL 在 Redis 中创建并查询搜索索引 | LangCache 托管服务(通过 HTTP API 访问) |
| 最佳使用场景 | 你掌控 Redis 基础设施,需要完整的 RedisVL 查询/过滤能力,或者希望缓存与应用程序数据放在一起 | 你希望使用托管的语义缓存,无需操心 Redis 运维和索引管理 |
| 按原始向量搜索 | 支持(check中可传入vector=) | 不支持—— 搜索只能基于 prompt 文本,通过 LangCache API 完成 |
| 过滤表达式 | 支持FilterExpression | 不支持—— 请使用 LangCache 属性(需在服务端预先配置) |
| 部分更新条目 | 在后端允许时支持 | update/aupdate会抛出异常,请改用“删除后重新存储”的方式 |
补充说明:SemanticCache的完整用法可参考前一篇指南。
安装 LangCache 额外依赖
你需要安装redisvl[langcache]以获取兼容的langcache客户端库:
pipinstallredisvl[langcache]如果在 Jupyter Notebook 中,也可使用:
%pip install redisvl[langcache]初始化LangCacheSemanticCache
创建LangCacheSemanticCache实例时,需要提供 LangCache 服务的凭证信息。默认的server_url指向 Redis 官方托管服务,如果你的服务提供商给出了不同的端点,请自行覆盖。
建议:将凭证存储在环境变量中,避免硬编码。
importosfromredisvl.extensions.cache.llmimportLangCacheSemanticCache# 从环境变量读取凭证(推荐做法)CACHE_ID=os.environ.get("LANGCACHE_CACHE_ID","YOUR_CACHE_ID")API_KEY=os.environ.get("LANGCACHE_API_KEY","YOUR_API_KEY")cache=LangCacheSemanticCache(name="my_app_cache",# 仅用于本地标识,不影响服务端server_url="https://aws-us-east-1.langcache.redis.io",# LangCache API 基础 URLcache_id=CACHE_ID,# 你的缓存实例 IDapi_key=API_KEY,# API 密钥ttl=3600,# 默认 TTL(秒),可选)关键参数说明
| 参数 | 作用 |
|---|---|
cache_id,api_key | 必需。标识你的 LangCache 缓存并验证身份。 |
server_url | LangCache API 基础地址。默认匹配官方托管服务,若使用其他提供商请修改。 |
ttl | 存储条目的默认生存时间(秒),可在每次store调用时单独覆盖。 |
use_exact_search/use_semantic_search | 启用精确匹配和/或语义匹配(至少一个必须为True)。 |
distance_threshold(在check中) | 配合distance_scale使用:可设为"normalized"(距离 0~1)或"redis"(余弦风格 0~2)。 |
属性和元数据
属性(Attributes)是 LangCache 为每个条目附加的键值对元数据。你可以在查询(check/acheck的attributes参数)和删除(delete_by_attributes/adelete_by_attributes)时使用它们来限定范围。
⚠️重要:你必须在 LangCache 控制台或通过管理 API预先定义这些属性的名称和类型(如字符串、数字等),否则 RedisVL 调用时会收到错误。如果传递了未配置的属性,LangCache API 会返回错误,RedisVL 会抛出清晰的RuntimeError,提示你配置属性或移除相关调用。
字符串值在传输时会进行编码/解码,以支持特殊字符。
读穿透缓存模式(Read-Through Caching)
这是最典型的使用流程:先调用check查询缓存,如果命中则直接返回;若未命中,则调用 LLM 生成答案,然后调用store存入缓存,供后续请求复用。
defcall_your_llm(prompt:str)->str:"""替换为实际的 LLM 客户端调用(OpenAI、Anthropic 等)"""returnf"对 '{prompt}' 的回答"defanswer(user_prompt:str)->str:# 1. 查缓存hits=cache.check(prompt=user_prompt,num_results=1)ifhits:returnhits[0]["response"]# 2. 未命中 → 调用 LLMresponse=call_your_llm(user_prompt)# 3. 存储到缓存cache.store(prompt=user_prompt,response=response)returnresponse如果需要按租户或模型等维度隔离缓存,可以传入attributes(前提是这些属性已在 LangCache 中配置):
hits=cache.check(prompt=user_prompt,attributes={"tenant_id":"acme","model":"gpt-4o"},num_results=1,)下面的流程图直观展示了这一过程:
TTL(生存时间)控制
- 默认 TTL:在初始化时通过
ttl=参数设置所有新条目的默认有效期(秒)。 - 按条目覆盖:在调用
store/astore时,可以传入ttl=参数,为当前存储的条目单独指定过期时间。
prompt="什么是 Redis?"response="Redis 是一种内存数据存储。"# 此条目将在 5 分钟后过期,覆盖构造函数的默认 TTLcache.store(prompt=prompt,response=response,ttl=300)异步 API
LangCacheSemanticCache也支持异步操作,方法名前缀为a,例如acheck、astore、adelete、adelete_by_id、adelete_by_attributes、aclear。这在构建高并发应用时非常有用。
importasyncioasyncdefcall_your_llm_async(prompt:str)->str:returnf"异步回答:{prompt}"asyncdefanswer_async(user_prompt:str)->str:hits=awaitcache.acheck(prompt=user_prompt,num_results=1)ifhits:returnhits[0]["response"]response=awaitcall_your_llm_async(user_prompt)awaitcache.astore(prompt=user_prompt,response=response)returnresponse# 运行异步函数result=asyncio.run(answer_async("你好,世界"))print(result)删除操作
| 方法 | 功能 |
|---|---|
delete()/adelete() | 清空整个缓存(所有条目)。别名:clear()/aclear() |
delete_by_id(entry_id)/adelete_by_id | 根据 LangCache 返回的entry_id删除单个条目。 |
delete_by_attributes(attributes)/adelete_by_attributes | 根据指定的属性键值对删除所有匹配的条目(属性字典不能为空)。 |
使用示例:
# 删除特定 ID 的条目cache.delete_by_id("abc123")# 删除所有 tenant_id 为 "acme" 的条目(需在 LangCache 中预定义该属性)cache.delete_by_attributes({"tenant_id":"acme"})当前限制
由于LangCacheSemanticCache是 LangCache HTTP API 的封装,其能力受限于服务端接口。与SemanticCache相比,存在以下限制:
不支持按原始向量搜索
如果在check/acheck中传入vector=参数,会记录警告并忽略该参数,搜索仍基于 prompt 文本。不支持
FilterExpression
无法使用 RedisVL 的复杂过滤器表达式,请改用 LangCache 属性(需预先配置)。不支持
update()/aupdate()
LangCache API 没有提供更新单个条目的接口,因此这些方法会抛出NotImplementedError。如需修改,请先删除旧条目,再存储新条目。store中的filters参数无效SemanticCache允许在store时传入filters,但 LangCache 不支持该字段,传入时会记录警告并被忽略。
总结
- LangCacheSemanticCache是一个托管缓存方案,让你无需维护 Redis 索引即可享受语义缓存的便利。
- 它与SemanticCache在使用上高度相似,但在过滤能力、更新操作等方面存在取舍。
- 我们掌握了初始化、读穿透缓存、TTL 控制、异步 API 和删除操作等核心功能。
- 清楚了当前版本的限制,帮助你避开常见的误用场景。
选择哪种缓存方案,取决于你是否愿意自己管理 Redis 基础设施,以及对高级查询(如向量搜索、复杂过滤)的需求。如果你希望轻装上阵,LangCache 无疑是绝佳的搭档;如果你需要深度定制,SemanticCache则提供了更大的灵活性。