传统 RAG 的局限性
经典的 RAG 架构以向量数据库(VectorDB)为核心来检索语义相似性上下文,让大语言模型(LLM)不需要重新训练就可以或许获取最新的知识,其工作流如下图所示:
这一架构现在广泛应用于各类 AI 业务场景中,比方问答机器人、智能客服、私域知识库检索等等。虽然 RAG 通过知识加强肯定程度上缓解了 LLM 幻觉题目,但是依然面临准确度不高的题目,它受限于信息检索流程固有的限制(比如信息检索相干性、搜刮算法服从、Embedding模型)以及大量对 LLM 能力依靠,使得在生成效果的过程中有了更多的不确定性。
下图来自 RAG 范畴一篇闻名的论文:《Seven Failure Points When Engineering a Retrieval Augmented Generation System》 ,它总结了在利用 RAG 架构时经常会碰到的一些题目(红框部分,原作者称之为7个失败点)。
它们包罗:
- 内容缺失(Missing Content),用户想要的答案并不在知识库中,LLM会给出无意义的回答。
- 缺失排名靠前的文档(Missed Top Ranked),受限于文本切分方式、大小、嵌入模型等影响,从向量数据库中检索出来的 top-k 不愿定是最精确的答案。
- 不在上下文中(Not in Context),和上一条雷同,经过各种处理后终极取出来的上下文质量较低。
- 格式错误(Wrong Format),检索出来的效果没有特定格式,而 LLM 没有很好地对其识别分析。
- 未提取(Not Extracted),Prompt 包含大量无关信息影响了 LLM 判定,即上下文包含很多噪音。
- 答案不完备(Incomplete),生成的答案不够完备。
- 不正确的差异(Incorrect Speciticity),答案在响应中返回,但不够具体或过于具体,无法满足用户的需求。
以上题目现在都有一些优化方法来提升回答准确度,但依然离我们的预期有差距。
因此除了基于语义匹配文本块之外,业内也探索新的数据检索形式,比如在语义之上关注数据之间的关联性。这种关联性区别于关系模型中的逻辑性强依靠,而是带有肯定的语义关联。比如人和手机是两个独立的实体,在向量数据库中的相似性肯定非常差,但是结合实际生活场景,人和手机的关系黑白常密切的,如果我搜刮一个人的信息,我大大概性也关心他的周边,比方用什么型号的手机、喜好拍照还是玩游戏等等。
基于如许的关系模型,从知识库检索出来的上下文在某些场景中大概更有用,比方“公司内里喜好利用手机拍照的人有哪些”。
用知识图谱来出现数据关系
为了有用地形貌知识库中的抽象数据关系,引入了知识图谱的概念,它不再利用二维表格来组织数据,而是用图结构来形貌关系,这内里的关系没有固定的范式约束,雷同下面这种人物关系图:
![](https://img2024.cnblogs.com/blog/614524/202410/614524-20241010193535967-5879819.png) 上图中有最紧张的三个元素:对象(人物)、属性(身份)、关系,对于存储如许的数据有专门的数据库产品来支持,即图数据库。
图数据库(GraphDB)
图数据库是属于 NoSQL 的一种,它有着较为机动的 schema 定义,可以简单高效表达真实世界中任意的关联关系。图数据库中的重要概念有:
- 实体(Entity),也称之为顶点或节点,图结构中的对象
- 边(Edge),毗连两个实体的一条路径,即实体之间的关系
- 属性(Property),用来具体形貌实体或边的特征
以上概念可对应到前面的人物关系图。
具体到 schema 定义和数据操作层面,以流行的图数据库 NebulaGraph 为例:
- # 创建图空间
- CREATE SPACE IF NOT EXISTS test(vid_type=FIXED_STRING(256), partition_num=1, replica_factor=1);
- USE test;
- # 创建节点和边
- CREATE TAG IF NOT EXISTS entity(name string);
- CREATE EDGE IF NOT EXISTS relationship(relationship string);
- CREATE TAG INDEX IF NOT EXISTS entity_index ON entity(name(256));
- # 写入数据
- INSERT VERTEX entity (name) VALUES "1":("神州数码");
- INSERT VERTEX entity (name) VALUES "2":("云基地");
- INSERT EDGE relationship (relationship) VALUES "1"->"2":("建立了");
- ...
复制代码 图数据库的查询语法遵照 Cypher 标准:
- MATCH p=(n)-[*1..2]-() RETURN p LIMIT 100;
复制代码 以上语句从 NebulaGraph 中找到最多100个从某个节点出发,通过1到2条边毗连的路径,并返回这些路径。
如果用可视化图结构展示的话,它是这个样子:
GraphRAG
如果借用 VectorRAG 的思想,通过图数据库来实现检索加强的话就演变出了 GraphRAG 架构,团体流程和 VectorRAG 并无差异,只是新知识的存储和检索都用知识图谱来实现,解决 VectorRAG 对抽象关系明白较弱的题目。
借助一些AI脚手架工具可以很容易地实现 GraphRAG,以下是用 LlamaIndex 和图数据库 NebulaGraph 实现的一个简易 GraphRAG 应用:
- import os
- from llama_index.core import KnowledgeGraphIndex, SimpleDirectoryReader
- from llama_index.core import StorageContext
- from llama_index.graph_stores.nebula import NebulaGraphStore
- from llama_index.llms.openai import OpenAI
- from pyvis.network import Network
- from llama_index.core import (
- Settings,
- SimpleDirectoryReader,
- KnowledgeGraphIndex,
- )
- # 参数准备
- os.environ['OPENAI_API_KEY'] = "sk-proj-xxx"
- os.environ["NEBULA_USER"] = "root"
- os.environ["NEBULA_PASSWORD"] = "nebula"
- os.environ["NEBULA_ADDRESS"] = "10.3.xx.xx:9669"
- space_name = "heao"
- edge_types, rel_prop_names = ["relationship"], ["relationship"]
- tags = ["entity"]
- llm = OpenAI(temperature=0, model="gpt-3.5-turbo")
- Settings.llm = llm
- Settings.chunk_size = 512
- # 连接Nebula数据库实例
- graph_store = NebulaGraphStore(
- space_name=space_name,
- edge_types=edge_types,
- rel_prop_names=rel_prop_names,
- tags=tags,
- )
- storage_context = StorageContext.from_defaults(graph_store=graph_store)
- hosts=graph_store.query("SHOW HOSTS")
- print(hosts)
- # 抽取知识图谱写入数据库
- documents = SimpleDirectoryReader(
- "data"
- ).load_data()
- kg_index = KnowledgeGraphIndex.from_documents(
- documents,
- storage_context=storage_context,
- max_triplets_per_chunk=2,
- space_name=space_name,
- edge_types=edge_types,
- rel_prop_names=rel_prop_names,
- tags=tags,
- max_knowledge_sequence=15,
- )
- # 信息检索生成答案
- query_engine = kg_index.as_query_engine()
- response = query_engine.query("神州数码云基地在哪里?")
- print(response)
复制代码 VectorRAG 擅优点理具有肯定事实性的题目,对复杂关系明白较弱,而 GraphRAG 刚好弥补了这一点,如果把这两者举行结合,理论上能得到更优的效果。
HybridRAG — 新一代 RAG 架构
我们可以把数据在 VectorDB 和 GraphDB 各放一份,分别通过向量化检索和图检索得到与题目相干效果后,我们将这些效果毗连起来,形成同一的上下文,最后将组合的上下文传给大语言模型生成响应,这就形成了 HybridRAG 架构。
由英伟达团队Benika Hall等人在前段时间发表的论文《HybridRAG: Integrating Knowledge Graphs and Vector Retrieval Augmented Generation for Efficient Information Extraction》提出了这一假想,并利用金融服务行业中的数据集成、风险管理、猜测分析等场景举行对比验证。在需要从相干文本文档中获取上下文以生成有意义且连贯的响应时 VectorRAG 体现出色,而 GraphRAG 可以或许从金融文件中提取的结构化信息生成更准确的且具有上下文感知的响应,但 GraphRAG 在抽象问答任务或题目中没有明确提及实体时通常体现不佳。
该论文最后给出了 VectorRAG、GraphRAG和 HybridRAG 的测试效果:
表格中F代表忠实度,用来权衡生成的答案在多大程度上可以从提供的上下文中推断出来。AR代表答案相干性,用来评估生成的答案在多大程度上解决了原始题目。CP代表上下文精度,CR代表上下文召回率。
实现简单的 HybridRAG
以下示例用 Llama_index 作为应用框架,用 TiDB Vector 作为向量数据库,用 NebulaGraph 作为图数据库实现了简单的 HybridRAG 流程:
- import os
- from llama_index.core import KnowledgeGraphIndex, VectorStoreIndex,StorageContext,Settings
- from llama_index.embeddings.openai import OpenAIEmbedding
- from llama_index.graph_stores.nebula import NebulaGraphStore
- from llama_index.llms.openai import OpenAI
- from llama_index.vector_stores.tidbvector import TiDBVectorStore
- TIDB_CONN_STR="mysql+pymysql://xx.root:xx@gateway01.eu-central-1.prod.aws.tidbcloud.com:4000/test?ssl_ca=isrgrootx1.pem&ssl_verify_cert=true&ssl_verify_identity=true"
- os.environ["OPENAI_API_KEY"] = "sk-proj-xxx"
- os.environ["NEBULA_USER"] = "root"
- os.environ["NEBULA_PASSWORD"] = "nebula"
- os.environ["NEBULA_ADDRESS"] = "10.3.xx.xx:9669"
- llm = OpenAI(temperature=0, model="gpt-3.5-turbo")
- Settings.llm = llm
- Settings.chunk_size = 512
- class HybridRAG:
- def __init__(self):
- # 初始化向量数据库
- tidbvec = TiDBVectorStore(
- connection_string=TIDB_CONN_STR,
- table_name="semantic_embeddings",
- distance_strategy="cosine",
- vector_dimension=1536, # The dimension is decided by the model
- drop_existing_table=False,
- )
- tidb_vec_index = VectorStoreIndex.from_vector_store(tidbvec)
- self.vector_db = tidb_vec_index
-
- # 初始化知识图谱
- graph_store = NebulaGraphStore(
- space_name="heao",
- edge_types=["relationship"],
- rel_prop_names=["relationship"],
- tags=["entity"],
- )
- storage_context = StorageContext.from_defaults(graph_store=graph_store)
- kg_index = KnowledgeGraphIndex.from_documents(
- [],
- storage_context=storage_context,
- max_triplets_per_chunk=2,
- max_knowledge_sequence=15,
- )
- self.kg = kg_index
-
- # 初始化语言模型
- self.llm = llm
-
- # 初始化嵌入模型
- self.embeddings = OpenAIEmbedding()
-
- def vector_search(self, query):
- # 在向量数据库中搜索相关文本
- results = self.vector_db.as_retriever().retrieve(query)
- print(results)
- return [result.node for result in results]
-
- def graph_search(self, query):
- # 在知识图谱中搜索相关信息
- results = self.kg.as_retriever().retrieve(query)
- print(results)
- return [result.node for result in results]
-
- def generate_answer(self, query):
- vector_context = self.vector_search(query)
- graph_context = self.graph_search(query)
- combined_context = "\n".join(vector_context) + "\n" + graph_context
-
- prompt = f"Question: {query}\nContext: {combined_context}\nAnswer:"
- return self.llm.generate(prompt)
- # 使用示例
- hybrid_rag = HybridRAG()
- question = "TiDB从哪个版本开始支持资源管控特性?"
- answer = hybrid_rag.generate_answer(question)
- print(answer)
复制代码 以上实现中只是简单的拼装了从向量数据库和图数据库中的检索效果,实际应用中可引入相干reranker模型进一步做精排提高上下文精准度。不管终极利用什么样的 RAG 架构,我们要达到的目的始终是保障传递给大语言模型的上下文是更完备更有用的,从而让大语言模型给出更准确的回答。
总结
RAG 架构已经是提升大语言模型能力的有用方案,鉴于在差别的业务场景中正确率无法稳固保持,因此也衍生出了一系列变化后的 RAG 架构,如 Advanced RAG、GraphRAG、HybridRAG、RAG2.0 等等,它们之间有的是互补关系,有的是优化加强,也有重新努力别辟门户的门路,可以看出在 LLM 应用优化上依然处于快速变化当中。
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。 |