基于Redis的LLM语义缓存

让我们面对现实——大型语言模型(LLMs)非常强大,但它们可能会非常缓慢。如果你曾经等待超过10秒来回答一个你几乎之前问过的问题,你就知道这种挫败感了。这就是语义缓存发挥作用的地方:你的模型可以快速回忆起对类似查询的回答,而不是从头开始生成答案。

我在实验带有LLM的聊天机器人时遇到了这个问题。用户会用稍微不同的方式问同一个问题,而机器人则会浪费时间(和计算资源)来生成新的回复。因此,我决定用Redis和Ollama来解决这个问题,这个组合证明是非常有效的(而且是免费的)。让我们来分解一下。

1、什么是语义缓存?

传统的缓存通过存储精确查询的确切响应来工作。如果你先问“Redis是什么?”然后问“告诉我关于Redis”,标准缓存将无法匹配。语义缓存通过存储向量嵌入——意义的数值表示——来解决这个问题,这样它就可以检索对类似查询的响应。

这种方法代替了每次重新生成文本:

  • 加快响应速度(毫秒 vs 秒)
  • 减少LLM的工作负载(降低成本,提高效率)
  • 感觉更智能(用户获得快速、一致的答案)

2、为什么选择Redis和Ollama?

我选择了这些工具,因为它们配合得非常好:

  • Redis:一个闪电般快速的内存数据存储。通过RedisVL扩展,它支持向量搜索,这正是我们需要的。
  • Ollama:一种简单的方法来本地运行LLM并生成嵌入。我使用Llama 3.2进行响应,使用nomic-embed-text进行嵌入。

一起,它们使LLM感觉更快,而不增加不必要的复杂性。

3、开始使用

所有代码文件都在我的GitHub仓库中,并且还提供了如何设置它们的方法。

你需要安装以下内容:

pip install langchain-ollama redis redisvl

确保Redis在本地运行——我使用的是redis://localhost:6379。你可以按照给定仓库中的README文件中的详细设置说明进行操作。

4、代码解析:逐步拆解

我把设置分为两个主要部分:一个用于生成嵌入,另一个用于管理缓存。

4.1 向量化器(cache_vectorizer.py)

RedisVL需要一个自定义向量化器来生成和检索嵌入。这是如何将Ollama的nomic-embed-text模型包装成Redis可以使用的东西:

from langchain_ollama import OllamaEmbeddings  
from redisvl.utils.vectorize import CustomTextVectorizer  
import asyncio  
from typing import List  
  
def create_vectorizer():  
    ollama_embedder = OllamaEmbeddings(model='nomic-embed-text')  
  
def sync_embed(text: str) -> List[float]:  
        return ollama_embedder.embed_query(text)  
  
def sync_embed_many(texts: List[str]) -> List[List[float]]:  
    return ollama_embedder.embed_documents(texts)  
  
async def async_embed(text: str) -> List[float]:  
    return await asyncio.to_thread(sync_embed, text)  
  
async def async_embed_many(texts: List[str]) -> List[List[float]]:  
    return await asyncio.to_thread(sync_embed_many, texts)  
  
return CustomTextVectorizer(  
    embed=sync_embed,  
    aembed=async_embed,  
    embed_many=sync_embed_many,  
    aembed_many=async_embed_many  
)  
vectorizer = create_vectorizer()

4.2 主事件(test.py)

此脚本处理缓存逻辑——首先检查Redis,如果需要再调用LLM。

import time  
from langchain_ollama import ChatOllama  
from redisvl.extensions.llmcache import SemanticCache  
from cache_vectorizer import vectorizer  
import os  
from dotenv import load_dotenv  
---  
  
load_dotenv()  
redis_url = os.getenv('REDIS_URL', 'redis://localhost:6379')  
llmcache = SemanticCache(  
    name="OllamaLLMCache",  
    redis_url=redis_url,  
    distance_threshold=0.1,  
    vectorizer=vectorizer,  
    connection_kwargs={'decode_responses': True, 'socket_timeout': 5, 'retry_on_timeout': True},  
    dimension=768  
)  
question = input("请输入您的问题:")  
client = ChatOllama(model="llama3.2", verbose=True)  
def ask_ollama(question: str) -> str:  
    response = client.invoke(input=question)  
    return response.content  
start_time = time.time()  
cached_response = llmcache.check(prompt=question)  
cache_time = time.time() - start_time  
if cached_response:  
    print("命中缓存!")  
    print("提示:", cached_response[0]['prompt'])  
    print("回复:", cached_response[0]['response'])  
    print(f"所用时间:{cache_time:.4f}秒")  
else:  
    start_time = time.time()  
    response = ask_ollama(question)  
    llm_time = time.time() - start_time  
    print("未命中缓存。LLM回复:")  
    print("提示:", question)  
    print("回复:", response)  
    print(f"所用时间:{llm_time:.4f}秒")  
    llmcache.store(prompt=question, response=response)  
    print("已存储在缓存中!")  

5、看看效果

启动test.py并尝试:

  1. 问“Ollama是什么?”→ LLM回复(几秒钟延迟)。
  2. 问“定义Ollama?”→ 命中缓存!即时回复。

这就是语义缓存的力量——不需要不必要的LLM调用,只有快速的回答。你可以调整距离阈值参数,以期望的即时回答为准。

使用这种设置,我看到了:

  • 重复问题的LLM查询减少了80-90%
  • 响应时间缩短到不到一秒,而不是等待10秒以上。
  • 更聪明的聊天机器人,不会卡在重复回答类似问题上。

想要调整吗?

  • 调整阈值:我设置distance_threshold=0.1,但将其提高到0.2可能会匹配更多查询(以轻微的不准确为代价)。
  • 尝试不同的嵌入模型:Ollama支持多种模型——实验看看哪种最适合。

6、结束语

使用Redis和Ollama的语义缓存是一个改变游戏规则的方案。它使LLMs更快,降低成本,并在实际应用中感觉更流畅。如果你正在处理AI聊天机器人、知识库或任何文本密集型应用,你必须尝试一下。

试一试,调整代码,看看它能多大程度提升你的设置。我很想知道你是如何使用它的!


原文链接:Redis Semantic Caching with Ollama

汇智网翻译整理,转载请标明出处