导读
随着 ChatGPT 的流行,LLMs(大语言模型)再次进入人们的视野。然而,在处理特定领域查询时,大模型生成的内容往往存在信息滞后和准确性不足的问题。如何让 RAG 和向量搜索技术在实际应用中更好地满足企业需求?如何在向量之上构建全链路 RAG 服务,提升开发者效率,降低成本?本文整理自 TiDB 生态系统架构师及高级开发者 Advocate 王琦智在墨天轮数据库沙龙的分享:《下一代RAG,tidb.ai 使用知识图谱增强 RAG 能力》,以下为演讲实录。
Graph RAG(Graph Retrieval-Augmented Generation)是一种结合知识图谱和信息检索技术的方法。为了更好地理解它,我们可以用一个形象的例子来说明:在商场门口有一个抽奖箱,你把手伸进去抽出一张小纸条,上面写着一二三等奖的奖项及相应的绕口令。你需要念出绕口令才能拿到奖品。这个过程很像 Graph RAG 的工作原理:你在一个“黑盒子”里接收到一段信息,然后使用这些信息进行相应的操作。
但我们发现,在某些情况下,抽到的奖项越高反而可能带来一些问题。例如,若口才不佳,抽到高奖项可能会让人难以应对,从而无法获得任何实际的奖励。这种现象在某些游戏场景中尤为明显。然而,在 RAG 场景中,我们希望能获得更好的回答。那么,如何解决这一问题呢?一种解决方案是,当抽取奖项时,同时抽取与之关联的其他奖项。例如,当抽到二等奖时,同时抽出相应的一等奖和三等奖。这种方法可以借助知识图谱来增强 RAG 能力。
今天,我们就来探讨如何使用知识图谱来提高 RAG 表现。
tidb.ai是什么
首先,让我们介绍一下 tidb.ai 它是什么。其实很简单,就是一个了解 TiDB 知识的 AI 问答机器人。在使用 tidb.ai 的过程中,可能会有一些常见的问题。例如,TiDB 中的 TiKV 和 TiFlash 有什么区别?实际上,这是指两种存储引擎,TiKV 是行存储,而 TiFlash 是列存储。但是,对于一个 TiDB 新手来说,可能会不知道这些区别,因此会产生疑问。这正是我们使用知识图谱生成的一些档案所解决的问题。
在我们使用 tidb.ai 之前,存在一些问题。首先,我们缺乏技术支持的人力。以前我们有一个轮班的小组来回答社区成员提出的问题,但人力一直不足,所以导致技术支持的间隔时间较长。如果问题提得不够清楚,没有一次性把所有问题提完,就需要几轮沟通,从而延长了技术回答的时间。其次,我们的文档丰富,但会导致用户不知道从哪里开始。 通过 tidb.ai,我们可以帮助用户查阅文档、编写代码、回答问题,从而解放技术支持工程师的人力,实现零延迟的回答,并且不需要等待多轮对话。
简单RAG的实现方案
首先,简单介绍一下我们在一年之前是如何实现的简单的 RAG。最初的实验方案是基于 Plain RAG(云原生 RAG)。下面是具体的实现步骤,如图所示,分别为:
文档切分与向量化:
◦ 从左边开始,我们首先有一些文档,将其切分成文本块(text chunk)。
◦ 这些文本块会被输入到 OpenAI 或其他模型中进行向量化处理,生成相应的向量。
◦ 这些向量与对应的文本块会被存储在 TiDB 中。
建立向量索引:
◦ 向量与文本块存储在同一行中,这样我们可以为这些向量建立索引(vector index),使服务能够使用这些向量进行快速检索。
用户问题向量化与检索:
◦ 当用户提出问题(例如“什么是 TiDB?”),我们使用相同的模型将问题向量化。
◦ 然后,我们通过一个 SQL 查询在向量索引中检索,使用一个称为 X 的函数来比较两个向量之间的余弦距离。
◦ 通过这个函数,我们可以找到与用户问题最接近的三个向量(Top Three Nearest Neighbors)。
生成回答:
◦ 找到最接近的向量后,我们可以获取相应的文本块。
◦ 最后,使用 OpenAI 模型生成答案,并将其返回给用户。
RAG(Retrieval-Augmented Generation)是一种开创性的技术 ,解决了多个问题。首先,它降低了大模型的幻觉现象 。大模型有时会生成不准确或无根据的回答,而 RAG 通过检索相关文档,提供额外的支持,减少了这种现象。其次, RAG 能够给予大模型额外的知识支持 ,通过检索相关信息,增强模型的回答质量和准确性。最后, RAG 突破了上下文窗口限制 。大模型的上下文窗口通常有限,例如 1024 个 token,虽然现在扩展到 2048、4096,甚至 8K、16K,但传递所有上下文依然成本高昂且不经济。
为什么需要Rerank?
在有了 RAG 之后,我们为什么还需要 Rerank 或其他增强操作呢?Rerank 是对检索到的文档进行重新排序的过程,其重要性在于提高检索的准确性。余弦相似性在比较文本时,往往忽略了语言间的关系。例如,问题“你吃饭了吗?”最相似的文本可能是“你吃饭了吗?”本身,而不是“我吃了”或“我没吃”。这种文本之间的相似性无法捕捉到实际回答的意图。
Rerank 通过重新排序,帮助我们找到最相关的回答。就像推荐系统中,在召回之后进行重新排序一样,Rerank 能进一步提升检索结果的相关性和准确性。通过这种方式,我们可以更好地理解文档与问题之间的交互关系,提高回答的准确性和用户体验。Rerank 加 RAG 就能解决所有问题吗?其实不然,它也无法一次性解决所有问题。
举个例子,比如在索引阶段(indexing),即构建 Vector index 的过程中,会遇到上下文窗口的限制。文字可能会在不该被截断的地方被截断,造成问题。举个例子,有一句话是“王叔叔夸我作业做得好,于是抱起了我妈妈,然后 token 没了”,结果 GPU 运行超载,大模型 GPU 直接开始冒烟,显然需要处理这些问题。这个例子仅仅展示了 indexing 阶段上下文窗口限制所导致的问题 ,除此之外,Rerank+RAG 还会造成 Chunks 之间没有关联,忽略文档结构关系等问题 。
知识图谱助力RAG
基于以上的问题,我们提出了一个使用知识图谱来增强 RAG(Retrieval-Augmented Generation)模型的解决方案。这个方案能够有效解决 RAG 的问题,提升模型的回答质量和准确性。
1、理论基础
如图是微软发布的一篇关于知识图谱的论文。大家可以参考这篇论文了解更多细节。我们在初始阶段基于这篇论文的实现思路进行开发,后续进行了改良和优化。
2、简易架构
用户提出问题后,我们首先找到几个最近邻的节点。不论使用哪种方法,找到一个或多个 Top n 节点后,对这些节点进行扩展。图中,我们扩展了一个度,蓝色箭头表示扩展路径,绿色节点是扩展后触达的节点。最后,我们将这些节点及其关系全部提取出来,生成最终的回答。这个架构简单但有效,通过这种方式,我们能够显著提高模型的回答准确性和相关性。
在前面我们提到知识图谱,大家可能对它有了一个初步的认识。实际上,建立好的知识图谱看起来就像一个星云,有些人觉得它更像是我们的神经元。我们的知识被存储在一个个小球和它们之间的连线上,看起来非常壮观。刚刚展示的图也显示了知识图谱中包含的大量节点及其复杂的关联。
3、构建方式
有人可能会问,这么多的节点是否是手工构建的?显然不是。我们是一家小型数据库公司,没有那么多人力去完成这个任务。接下来就分别与大家讨论下构建的四个步骤
• 丰富的文档及社区问答
社区中英文 Markdown 文档为 1276 篇,中文 Markdown 文档为 1098 篇,而且这都不是 AI 翻译的,我们这个文档是有一个专门的团队去进行维护,每一个文档都跟随着版本发布而作出对应的调整与更新。
• 使用LLM 进行知识图谱构建
我们提出了一个利用 LLM 抽取知识图谱的可行方案,解决了我们人力不足的问题。具体来说,我们使用了 DSPy 库进行知识图谱的自动化构建。如果你对代码实现感兴趣,可以查看我们提供的 demo,扫描二维码即可访问。
在具体实现上,我们采用了 DSPy 库来定义和抽取知识图谱中的节点和边。我们生成的图谱是一种汇总图谱(Summary graph),而不是实例图谱(instance graph)。
这两种图谱有以下区别:
• 实例图谱(Instance Graph):这种图谱更干净,准确率更高,泛用性更好,但其维护成本也更高。
• 汇总图谱(Summary Graph):这种图谱维护成本低,不需要频繁的人工维护。由于我们的人力有限,只能实现汇总图谱的程度。
•存入 TiDB Serverless 集群
尽管汇总图谱的精度和泛用性不如实例图谱,但其维护成本更低,并且在性能上明显优于不使用知识图谱的 RAG 版本。因此,我们选择了汇总图谱。由于知识图谱的数据量非常大,我们需要一个合适的数据库进行存储。这个数据库不仅需要支持大规模数据存储,还要支持向量操作和高效检索。TiDB Serverless 不仅支持大规模数据存储和向量检索,还能应对高并发的查询需求 ,基于此我们选择了 TiDB Serverless 来解决这些问题。
•检索时使用 Vector Search 和知识图谱
检索时首先会使用 Vector Search 在集群中搜索最近邻的 Top N 节点,随后使用这些节点扩散 K 度,取回其相关节点及边,最后使用这些节点及边生成回答。
Vector type within TiDB>TiDB+Vector Database
为什么在数据服务架构中,选择将向量嵌入到 TiDB 中,而不是“外挂”一个向量数据库。首先,“外挂”向量数据库的方案需要应用程序进行双重操作,即写两次和查两次数据,这会带来数据同步问题。不仅需要确保数据的事务级别同步,即使是最终一致性有时也很难保证。有工程经验的开发者都知道,最终一致性有时很难实现。
将向量嵌入到 TiDB 中,可以简化操作,只需对 TiDB 进行一次读写操作,避免了外挂数据库带来的复杂性。我们选择在 TiDB 中嵌入向量,主要基于以下几个原因。首先,这样的方案可以简化架构,应用程序只需进行一次读写操作,不需要管理两个不同的数据库,从而简化了应用架构。其次,我们希望创建一个兼容 MySQL 生态的向量数据库插件,TiDB Serverless 率先实现了这一功能。尽管 MySQL 9.0 也实现了类似功能,但我们在此之前已经实现了这一点。
通过将向量嵌入 TiDB,我们不仅简化了架构,减少了操作复杂性,还能利用 TiDB 的分布式架构和高可用性,满足大规模数据存储和高并发查询的需求。
All in one 数据库帮助开发者减负
我接下来要讲的是全能型数据库如何帮助开发者减轻负担。在架构方面,我们先回顾一下过去 20 年的技术架构演变。每当关系型数据库(RDB)无法解决问题时,我们总是倾向于创造新的解决方案。最开始,我们有两种数据结构:文档型数据库和关系型数据库。如果需要同时使用两种数据库,就需要将数据从关系型数据库同步到文档型数据库。随后,我们发现 OLAP(联机分析处理)也很有用,因为列存储能加快数据分析速度,所以我们又增加了一份数据同步。然后,我们发现倒排索引适合全文检索,于是再同步一次。现在,我们还想增加一个向量索引。这些变化使得业务开发者感到困扰,因为数据架构的复杂性往往超过了业务本身的复杂性。
许多复杂的数据架构并不是由应用本身引起的,而是由于数据库架构的复杂性。对于一个服务,如果需要集成不同种类的查询,就需要加入不同种类的数据库。例如,一个服务可能需要 MySQL、MongoDB、ClickHouse 和 Redis,开发者必须仔细维护它们之间的关系。这种复杂度不应该由业务开发者承担,而是应该由数据存储来解决。换句话说,现在的数据存储对于业务架构的侵入性越来越强。存储本应用于解耦,但现在反而增加了耦合度,导致本末倒置。即使不增加额外的数据库,有些架构由于数据量的原因,也需要进行分库分表,这同样增加了数据架构的复杂性。
TiDB 作为一个全能型数据库,开发者使用其可以避免上述复杂性,因为 TiDB 能够集成多种数据库功能 ,不需要进行数据同步或维护多个数据库。这不仅简化了数据架构,还提升了开发效率,降低了维护成本,使得开发者可以更加专注于业务本身 。在预算方面,这也能帮助降低成本。
Take away
接下来,我想给大家一些 takeaways。首先,没有任何一种技术是万能的,大家需要根据自身情况选择最适合的技术栈。
对于自部署的 TiDB 方案,它的数据量没有限制,可用性高,加上 TiFlash 后还具备一定的分析能力。然而,自部署版本没有 vector 能力,运维也较为复杂,即使 TiDB 已经是分布式数据库中相对简单的运维方案。
而 TiDB Serverless 的优点在于数据量没有限制,可用性高,因为它是基于云平台的分布式数据库,具备分析和向量计算能力,价格也相对便宜。不过,如果你的服务是持续高负载的,Serverless 的成本可能会比自部署方案稍微贵一些。
最后,我想给大家传递一些愿景。现在,大家的目光都集中在 AI 上,AI 是我们未来可以仰望的天空。然而,我们希望数据库是我们脚下的坚实土壤,只有站得更稳,我们才能看得更远。因此,我想把这句话送给大家:AI is the sky, database is the earth。
这就是我今天的分享内容,感谢大家的聆听,谢谢!