我们看到检索增强生成 (RAG) 正在成为需要访问私有数据的 GenAI 应用程序的事实上的标准架构。然而,有些人可能想知道为什么实时访问这些数据很重要。答案很简单:您不希望在将 AI 添加到您的堆栈后,您的应用程序停止快速运行。
那么,什么是快速应用程序?Paul Buchheit(Gmail 的创建者)创造了100 毫秒规则。它指出每次交互都应该快于 100 毫秒。为什么?100 毫秒是“交互感觉即时”的阈值。
让我们检查一下典型的基于 RAG 的架构是什么样的,以及每个组件目前具有的延迟边界以及预期的端到端延迟。
根据此分析,使用上述架构构建的 GenAI 应用程序应预计平均 1,513 毫秒(或 1.5 秒)的端到端响应时间。这意味着您可能会在几次交互后失去最终用户的兴趣。
要构建一个允许更接近 100 毫秒规则体验的实时 GenAI 应用程序,您需要重新考虑您的数据架构。
为了应对上述挑战,Redis 为 AI 提供了三种主要的数据存储功能,这些功能将使实时 RAG 成为可能。
即使在 GenAI 这个词被创造出来之前,Redis 就已经支持向量数据类型和向量搜索功能。Redis 向量搜索算法使用高效的内存数据结构和专用搜索引擎,从而实现高达 50 倍的搜索速度(我们将很快发布我们的全面基准测试结果)和文档检索速度快两个数量级。在这篇博文中稍后将展示实时向量搜索如何显著改善用户的端到端体验。
Redis(以及通常)中的传统缓存技术使用关键字匹配,这难以捕捉到类似查询与基于 LLM 的服务之间的语义相似性,导致命中率非常低。使用现有的缓存,我们无法检测到“给我推荐一部喜剧电影”和“推荐一部搞笑电影”之间的语义相似性,导致缓存未命中。语义缓存超越了精确匹配:它使用智能算法来理解查询的含义。即使措辞不同,缓存也可以识别它在语义上是否与之前的查询相似,并返回相应的响应(如果它有的话)。根据最近的一项研究,31% 的 LLM 查询可以被缓存(或者换句话说,31% 的查询在语义上是可重复的),这可以显著提高基于 RAG 架构运行的 GenAI 应用程序的响应时间,同时大幅降低 LLM 成本。
您可以将语义缓存视为 LLM 的新缓存。利用向量搜索,语义缓存可以带来显著的性能和部署成本效益,我们将在以下部分进行说明。
LLM 内存是 LLM 与特定用户之间所有先前交互的记录;将其视为 LLM 的会话存储,只是它还可以记录不同用户会话的信息。通过使用现有的 Redis 数据结构和向量搜索实现,LLM 内存对于以下几个原因可能非常有价值。
没有 LLM 内存
用户:“我正在计划去意大利旅行。有哪些有趣的地方可以参观?”
LLM:“意大利有很多美丽的城市!以下是一些热门的旅游目的地:罗马、佛罗伦萨、威尼斯……”
有 LLM 内存
用户:“我正在计划去意大利旅行。我对艺术和历史感兴趣,不太喜欢人多的地方。”(假设这是对话的第一轮)
LLM:“由于您对艺术和历史感兴趣,不妨去佛罗伦萨看看?它以文艺复兴时期的艺术和建筑而闻名。”(LLM 使用对话历史来识别用户偏好并推荐相关地点)
用户:“听起来不错!有哪些博物馆是不容错过的?”
LLM(参考对话历史记录):“乌菲兹美术馆和学院美术馆是佛罗伦萨艺术爱好者必看的博物馆。”(LLM 利用对话历史来理解用户在旅行背景下的具体兴趣)
在这个例子中,LLM 内存(或对话历史记录)使 LLM 能够根据用户的初始陈述个性化其响应。它避免了泛泛的推荐,而是将建议量身定制以满足用户的表达的兴趣,从而带来更有效、更引人入胜的用户体验。
为了解释 Redis 的 AI 功能如何实现实时 RAG,没有什么比图解和简短的说明更好了。
我们应该查看两种情况
为了了解实时 RAG 应用程序的端到端性能,让我们分析每个选项。
如上图所示,只有两个组件实际参与了这种情况
基于 Redis 的 RAG 架构的平均端到端响应时间为 389 毫秒,比非实时 RAG 架构快约 3.2 倍,并且更接近 Paul Buchheit 的 100 毫秒规则。这使得现有和新的应用程序能够在堆栈中运行 LLM 组件,而不会产生或几乎不会产生性能影响。
除了确保您的快速应用程序保持快速运行之外,基于 Redis 的实时 RAG 架构还提供以下其他优势
本文分析了基于 RAG 的架构的响应时间,并解释了 Redis 如何在复杂、快速变化的 LLM 环境中提供实时最终用户体验。如果您想尝试本文中讨论的所有内容,我们建议您使用 Redis Vector Library (RedisVL),这是一个用于 AI 应用程序的基于 Python 的客户端,它使用 Redis 功能实现实时 RAG(语义缓存、LLM 内存和向量数据库)。RedisVL 与您的 Redis 云 实例或您自部署的 Redis 堆栈 兼容。
在本附录中,您将找到有关如何计算 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,我们查看了两种场景:缓存命中(使用语义缓存)和缓存未命中。根据 这项研究,我们计算了一个加权平均值,假设 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 毫秒