dot 快速的未来正在您所在的城市举办的活动中到来。

加入我们参加 Redis 发布会

使用 Redis 进行实时 RAG 超越了向量数据库

为什么 RAG 需要实时数据?

我们看到检索增强生成 (RAG) 正在成为需要访问私有数据的 GenAI 应用程序的事实上的标准架构。然而,有些人可能想知道为什么实时访问这些数据很重要。答案很简单:您不希望在将 AI 添加到您的堆栈后,您的应用程序停止快速运行。

那么,什么是快速应用程序?Paul Buchheit(Gmail 的创建者)创造了100 毫秒规则。它指出每次交互都应该快于 100 毫秒。为什么?100 毫秒是“交互感觉即时”的阈值。

让我们检查一下典型的基于 RAG 的架构是什么样的,以及每个组件目前具有的延迟边界以及预期的端到端延迟。

  1. 网络往返行程——假设您的应用程序的最终用户和数据中心都在美国,往返行程预计在 20-50 毫秒范围内。
  2. LLM——来自 ChatGPT:“根据我的最后一次更新,LLM 生成响应的处理时间通常在几十到几百毫秒之间,具体取决于上面提到的具体情况。该处理时间会受到模型架构、输入文本的长度和复杂性以及在生成响应时执行的任何附加任务(例如上下文分析或格式化)的影响。”
  3. GenAI 应用程序——我们可以预期本地操作需要几十毫秒,而调用第三方服务则需要几百毫秒。
  4. 向量数据库——存储您想要用于向 LLM 添加上下文以生成准确和相关响应的数据集或语料库。该数据集捕获有关文档的语义信息(即向量),并能够在检索阶段进行有效的基于相似性的检索。向量搜索查询通常是高计算复杂度操作查询。在我们进行并即将发布的全面基准测试中,向量搜索查询加 10 个文档导致多个数据集和负载的中位响应时间为 569 毫秒。
  5. 基于代理的架构——可能会驱动组件 2-4 的多个执行周期

根据分析,使用上述架构构建的 GenAI 应用程序应预计平均 1,513 毫秒(或 1.5 秒)的端到端响应时间。这意味着您可能会在几次交互后失去最终用户的兴趣。

要构建一个允许更接近 100 毫秒规则体验的实时 GenAI 应用程序,您需要重新考虑您的数据架构。

Redis 如何使 RAG 实时?

为了应对上述挑战,Redis 为 AI 提供了三种主要的数据存储功能,这些功能将使实时 RAG 成为可能。

实时向量数据库

即使在 GenAI 这个词被创造出来之前,Redis 就已经支持向量数据类型和向量搜索功能。Redis 向量搜索算法使用高效的内存数据结构和专用搜索引擎,从而实现高达 50 倍的搜索速度(我们将很快发布我们的全面基准测试结果)和文档检索速度快两个数量级。在这篇博文中稍后将展示实时向量搜索如何显著改善用户的端到端体验。

语义缓存

Redis(以及通常)中的传统缓存技术使用关键字匹配,这难以捕捉到类似查询与基于 LLM 的服务之间的语义相似性,导致命中率非常低。使用现有的缓存,我们无法检测到“给我推荐一部喜剧电影”和“推荐一部搞笑电影”之间的语义相似性,导致缓存未命中。语义缓存超越了精确匹配:它使用智能算法来理解查询的含义。即使措辞不同,缓存也可以识别它在语义上是否与之前的查询相似,并返回相应的响应(如果它有的话)。根据最近的一项研究,31% 的 LLM 查询可以被缓存(或者换句话说,31% 的查询在语义上是可重复的),这可以显著提高基于 RAG 架构运行的 GenAI 应用程序的响应时间,同时大幅降低 LLM 成本。

您可以将语义缓存视为 LLM 的新缓存。利用向量搜索,语义缓存可以带来显著的性能和部署成本效益,我们将在以下部分进行说明。

LLM 内存(或扩展的对话历史记录)

LLM 内存是 LLM 与特定用户之间所有先前交互的记录;将其视为 LLM 的会话存储,只是它还可以记录不同用户会话的信息。通过使用现有的 Redis 数据结构和向量搜索实现,LLM 内存对于以下几个原因可能非常有价值。

  • 改进的个性化
    • 了解用户偏好:通过分析过去的对话,LLM 可以识别用户的首选主题、沟通风格和术语。这使得 LLM 可以调整其响应,以更好地满足用户的需求和兴趣。
    • 建立融洽关系:引用过去的讨论并承认用户的历史有助于营造一种连续性和融洽关系,从而营造更自然、更引人入胜的用户体验。
  • 增强的上下文感知
    • 消除歧义查询:LLM 内存允许 LLM 理解当前查询的上下文。它可以将用户的当前问题与之前的讨论联系起来,从而得出更准确、更相关的响应。语义缓存可以利用 LLM 内存来回答诸如“接下来是什么?”之类的通用问题。
    • 建立在先前的知识基础上:LLM 可以利用过去的对话,在对用户兴趣或目标的现有知识基础上进行构建。这使得它能够提供更全面、更信息丰富的响应,这些响应超越了基本的信息检索。

示例:计划旅行

没有 LLM 内存
用户:“我正在计划去意大利旅行。有哪些有趣的地方可以参观?”
LLM:“意大利有很多美丽的城市!以下是一些热门的旅游目的地:罗马、佛罗伦萨、威尼斯……”

有 LLM 内存
用户:“我正在计划去意大利旅行。我对艺术和历史感兴趣,不太喜欢人多的地方。”(假设这是对话的第一轮)
LLM:“由于您对艺术和历史感兴趣,不妨去佛罗伦萨看看?它以文艺复兴时期的艺术和建筑而闻名。”(LLM 使用对话历史来识别用户偏好并推荐相关地点)
用户:“听起来不错!有哪些博物馆是不容错过的?”
LLM(参考对话历史记录):“乌菲兹美术馆和学院美术馆是佛罗伦萨艺术爱好者必看的博物馆。”(LLM 利用对话历史来理解用户在旅行背景下的具体兴趣)

在这个例子中,LLM 内存(或对话历史记录)使 LLM 能够根据用户的初始陈述个性化其响应。它避免了泛泛的推荐,而是将建议量身定制以满足用户的表达的兴趣,从而带来更有效、更引人入胜的用户体验。

使用 Redis 的实时 RAG 如何运作?

为了解释 Redis 的 AI 功能如何实现实时 RAG,没有什么比图解和简短的说明更好了。

  1. 在收到用户的提示后,GenAI 应用程序调用嵌入服务(例如 OpenAI Ada2)对其进行向量化。
  2. GenA AppI 启动语义缓存操作,查找类似的响应(在本例中,>= 97% 的相似性,但也可以使用其他参数)。如果应用程序命中缓存(超过 30% 的时间),它只需将缓存响应发送回用户。
  3. 在缓存未命中时,GenAI 应用程序将根据向量化提示从 Redis 的 LLM 内存中检索历史上下文。
  4. 为了获得与向量化提示匹配的前 K 个文档,在 Redis 中运行相似性搜索(在本例中,Redis 用作实时向量数据库)。
  5. GenAI 应用程序根据对话历史记录和来自向量数据库的文档生成一个接地提示,并将其发送到 LLM。
  6. 响应经过处理,然后发送给用户,同时更新语义缓存。

使用 Redis 的实时 RAG 有多快?

我们应该查看两种情况

  1. 语义缓存命中——其中所有 LLM 调用都已保存,因为相关响应位于缓存中(如前所述,这约占 GenAI 查询的 30%)。
  2. 语义缓存未命中(70% 的情况)——GenAI 将触发与非实时 RAG 架构类似的过程,使用 Redis 的 LLM 内存、实时向量搜索和实时文档检索。

为了了解实时 RAG 应用程序的端到端性能,让我们分析每个选项。

语义缓存命中分析

如上图所示,只有两个组件实际参与了这种情况

  1. 在收到向量化提示后,GenAI 应用程序执行向量搜索和检索调用,以检索缓存响应并将其发送回用户。我们假设此过程比缓存未命中过程短 33%,为 20-60 毫秒。
  2. Redis – 仅执行语义缓存,对应于单个向量搜索加单个缓存响应检索。此操作的中位延迟为 40 毫秒,详细计算可以在此处找到。
  3. 网络访问保持不变,延迟为 20-50 毫秒
  4. 缓存命中时的平均端到端延迟 – 100 毫秒

语义缓存未命中分析

  1. 网络 – 保持不变,延迟约为 20-50 毫秒
  2. GenAI 应用 – 在缓存未命中情况下执行 此处 描述的所有步骤,仍然在 20-100 毫秒内
  3. Redis – 执行 Redis 的所有可用 AI 功能:(1) 语义缓存,(2) LLM 内存,以及 (3) 向量数据库。我们对各种数据集和负载场景进行了基准测试,发现所有组合操作(包括往返时间)的中位数为 79 毫秒。
  4. LLM – 根据历史上下文,我们预计 LLM 处理将提高高达 25%,因为提示更短(更少的标记),并且更相关、更准确,即 40-400 毫秒
  5. 基于代理的架构 – 保持不变,可能会驱动组件 2-4 的多个执行周期
  6. 平均端到端延迟 – 513 毫秒

实时 RAG 响应时间分析总览

基于 Redis 的 RAG 架构的平均端到端响应时间为 389 毫秒,比非实时 RAG 架构快约 3.2 倍,并且更接近 Paul Buchheit 的 100 毫秒规则。这使得现有和新的应用程序能够在堆栈中运行 LLM 组件,而不会产生或几乎不会产生性能影响。

其他优势

除了确保您的快速应用程序保持快速运行之外,基于 Redis 的实时 RAG 架构还提供以下其他优势

  • 成本 – 通过语义缓存,您可以将对 LLM 的 API 调用减少高达 30%。这可以节省大量费用!
  • 更准确的响应 – LLM 内存提供了有关历史对话的上下文,并帮助 LLM 了解用户偏好,建立融洽关系,并根据先前的知识改进响应。

总结和下一步

本文分析了基于 RAG 的架构的响应时间,并解释了 Redis 如何在复杂、快速变化的 LLM 环境中提供实时最终用户体验。如果您想尝试本文中讨论的所有内容,我们建议您使用 Redis Vector Library (RedisVL),这是一个用于 AI 应用程序的基于 Python 的客户端,它使用 Redis 功能实现实时 RAG(语义缓存、LLM 内存和向量数据库)。RedisVL 与您的 Redis 云 实例或您自部署的 Redis 堆栈 兼容。

附录:详细的 E2E 延迟分析

在本附录中,您将找到有关如何计算 RAG(实时和非实时)的端到端响应时间的详细信息。它基于我们进行的综合基准测试,我们将在不久的将来发布,我们在四种类型的向量数据集上运行了该测试

  1. glove-100-angular (召回率>=0.95)
  2. gist-960-euclidean (召回率>=0.98)
  3. deep-image-96-angular (召回率>=0.99)
  4. dbpedia-openai-1M-angular (召回率>=0.99))

基准测试发布后,将提供有关这些数据集的更多信息。

非实时 RAG

对于非实时 RAG,我们对所有基于磁盘的数据库(专用数据库和通用数据库)的结果进行了平均。由于大数据偏差,我们对来自四种不同测试集和所有测试供应商的归一化中位数进行了取值。

组件延迟
网络往返时间(20+50)/2 = 35 毫秒
LLM(50+500)/2 = 275 毫秒
GenAI 应用
*假设在没有调用其他服务的情况下为 20 毫秒,否则为 100 毫秒
(20+100)/2=60 毫秒
向量数据库
*假设一个向量/混合搜索 + 10 个文档
一个向量搜索查询(我们对低负载和高负载的中位数进行了取值) – 63 毫秒10 个文档检索 – 10 x 50 毫秒 = 500 毫秒总计 – 563 毫秒
基于代理的架构假设对 LLM 的 1/3 调用会触发代理处理,从而触发来自 LLM 的应用程序调用以及数据检索和 LLM 调用的另一次迭代。33% x (LLM + 应用 + 向量数据库)
总计35 + {275+60+563}⅔ + {275+60+563}2*⅓ = 1232

实时 RAG

对于实时 RAG,我们查看了两种场景:缓存命中(使用语义缓存)和缓存未命中。根据 这项研究,我们计算了一个加权平均值,假设 30% 的查询将命中缓存(70% 将未命中)。我们对基准测试中所有测试数据集的 Redis 中位数延迟值进行了取值。

语义缓存命中

组件延迟
最佳情况
网络往返时间(20+50)/2 = 35 毫秒
GenAI 应用
*假设缓存命中会导致应用程序处理时间减少 33%
40 毫秒
Redis 语义缓存
一个向量搜索查询(我们对低负载和高负载的中位数进行了取值) – 24.6 毫秒1 个文档检索 – 1 x 0.5 毫秒总计 – 25 毫秒
总计35+40+25 = 100 毫秒

语义缓存未命中

组件延迟
最佳情况
网络往返时间35 毫秒
LLM

*根据历史上下文,我们假设 LLM 处理将提高 25%,因为提示更短(更少的标记),并且更准确、更相关
(40+400)/2 =220 毫秒
GenAI 应用
*假设在没有调用其他服务的情况下为 20 毫秒,否则为 100 毫秒
(20+100)/2=60 毫秒
Redis语义缓存未命中 – 24.6 毫秒LLM 内存搜索 (24.6 毫秒) + 5 个上下文检索
(5 x 0.5 毫秒) = 27.1 毫秒向量搜索 (24.6 毫秒) + 5 个上下文检索 (5 x 0.5 毫秒) = 27.1 毫秒总计 – 24.6+27.1+27.1 = 79 毫秒
基于代理的架构假设对 LLM 的 1/3 调用会触发代理处理,从而触发来自 LLM 的应用程序调用以及数据检索和 LLM 调用的另一次迭代。33% x (LLM + 应用 + Redis)
总计35 + {220+60+79}⅔ + {220+60+79}2*⅓ = 513 毫秒

端到端应用程序延迟

缓存命中和未命中的加权平均值计算如下:30% * 100 毫秒 + 70% * 513 毫秒 = 389 毫秒