利用知识图谱提升RAG应用的准确性

文章目录

    • 一、关于 GraphRAG
    • 二、Neo4j环境配置
    • 三、数据提取
    • 四、RAG混合检索
      • 1、非结构化数据检索器
      • 2、图谱检索器
      • 3、最终的检索器
    • 五、定义RAG Chain


本文转载自:lucas大叔 : 利用知识图谱提升RAG应用的准确性
https://zhuanlan.zhihu.com/p/692595027
英文原文:Enhancing the Accuracy of RAG Applications With Knowledge Graphs
https://neo4j.com/developer-blog/enhance-rag-knowledge-graph/


一、关于 GraphRAG

图检索增强生成(GraphRAG)利用图数据库的结构化特性,以节点和关系的方式进行组织数据,增加了检索到信息的深度和关联上下文,是传统向量检索方法的有效补充

在这里插入图片描述

图擅长以结构化的方式表示和存储异构和互连的信息,可以轻松地捕捉不同数据类型之间的复杂关系和属性。
相比之下,向量数据库往往难以处理此类结构化信息,因为它们的优势在于通过高维向量处理非结构化数据。
在RAG应用中,可以将结构化图数据与非结构化文本的向量搜索相结合,以实现优势互补。


虽然知识图谱的概念已经比较普及,但构建知识图还是一项有挑战性的工作。
它涉及到数据的收集和结构化,需要对领域和图建模有深入的了解。
为了简化图谱构建过程,我们尝试利用LLM来构建。

LLM对语言和上下文有着深刻的理解,可以实现知识图创建过程重要部分的自动化。
通过分析文本数据,LLM可以识别实体,理解实体之间的关系,并建议如何在图结构中最好地表示它们。
作为实验的结果,我们在LangChain中添加了图构建模块的第一个版本,并将在这篇博客中进行演示。

相关代码:https://github.com/tomasonjo/blogs/blob/master/llm/enhancing_rag_with_graph.ipynb


二、Neo4j环境配置

首先创建一个Neo4j实例。
最简单的方法是在Neo4j Aura上启动一个免费实例,该实例提供Neo4j数据库的云实例。
或者,你也可以下载 Neo4j Desktop 应用并创建Neo4j数据库的本地实例。

os.environ["OPENAI_API_KEY"] = "sk-"
os.environ["NEO4J_URI"] = "bolt://localhost:7687"
os.environ["NEO4J_USERNAME"] = "neo4j"
os.environ["NEO4J_PASSWORD"] = "password"graph = Neo4jGraph()

三、数据提取

本演示使用 Elizabeth I 的维基百科页面,我们用LangChain loader无缝地从维基百科抓取和分割文档。

# Read the wikipedia article
raw_documents = WikipediaLoader(query="Elizabeth I").load()# Define chunking strategy
text_splitter = TokenTextSplitter(chunk_size=512, chunk_overlap=24)
documents = text_splitter.split_documents(raw_documents[:3])

现在用分割后的文档构建图谱。
为此,我们实现了LLMGraphTransformer模块,它大大简化了在图数据库中构建和存储知识图谱。

LLMGraphTransformer类利用LLM将文档转化为图谱文档,允许指定输出图谱中节点和关系类型的约束,不支持抽取节点或者关系的属性。
它的参数如下:

  • llm (BaseLanguageModel):支持结构化输出的语言模型实例
  • allowed_nodes (List[str], optional): 指定图谱中包含哪些节点类型,默认是空list,允许所有节点类型
  • allowed_relationships (List[str], optional): 指定图谱中包含哪些关系类型,默认是空list,允许所有关系类型
  • prompt (Optional[ChatPromptTemplate], optional): 传给LLM的带有其他指令的prompt
  • strict_mode (bool, optional): 确定转化是否应该使用筛选以严格遵守allowed_nodesallowed_relationships,默认为True

本例allowed_nodes和allowed_relationships都采取默认设置,即图谱中允许所有的节点和关系类型。

llm=ChatOpenAI(temperature=0, model_name="gpt-4-0125-preview")
llm_transformer = LLMGraphTransformer(llm=llm)# Extract graph data
graph_documents = llm_transformer.convert_to_graph_documents(documents)
# Store to neo4j
graph.add_graph_documents(graph_documents, baseEntityLabel=True, include_source=True
)

你可以定义知识图谱生成链要使用的LLM。目前,只支持OpenAI和Mistral的function-calling模型。在本例中,我们使用最新的GPT-4。
值得注意的是,生成图谱的质量在很大程度上取决于所使用的模型。
LLM graph transformer 返回图谱文档,通过add_graph_documents方法导入到Neo4j。
baseEntityLabel参数为每个节点分配一个额外的__Entity__标签,从而提高索引和查询性能。
include_source参数将节点链接到其原始文档,便于数据跟踪和上下文理解。

在Neo4j浏览器中可以检查生成的图谱。

在这里插入图片描述

可以看到,每种类型的节点除了自身的节点类型之外,多了一个__Entity__ 标签。
在这里插入图片描述

在这里插入图片描述


同时,节点通过MENTIONS关系与源文档连接。
在这里插入图片描述


四、RAG混合检索

在生成图谱之后,我们将向量索引关键字索引的混合检索与图谱检索结合起来用于RAG。

在这里插入图片描述

上图展示了从用户提出问题开始的检索过程,问题首先输入到RAG检索器,该检索器采用关键词和向量搜索非结构化文本数据,并与从知识图谱中收集的信息结合。
由于neo4j同时具有关键词和向量索引,因此可以使用单个数据库实现全部三种检索方式。
从这些数据源收集的数据输入给LLM生成最终答案。


1、非结构化数据检索器

可以用 Neo4jVector.from_existing_graph 方法为文档添加关键字和向量检索。
此方法为混合搜索方法配置关键字和向量搜索索引,目标节点类型为Document。
另外,如果文本embedding值缺失,它还会计算创建向量索引。

vector_index = Neo4jVector.from_existing_graph(OpenAIEmbeddings(),search_type="hybrid",node_label="Document",text_node_properties=["text"],embedding_node_property="embedding"
)

可以看到,Document节点原来没有embedding属性,创建非结构化数据检索器后,基于Document节点的text属性新创建了embedding。

在这里插入图片描述


然后使用similarity_search方法就可以调用向量索引。


2、图谱检索器

另一方面,配置图谱检索更为复杂,但提供了更多的自由度。
本示例将使用全文索引 识别相关节点并返回其一阶邻居。

在这里插入图片描述


图谱检索器首先识别输入中的相关实体。
为简单起见,我们让LLM识别人、组织和地点等通用实体,用LCEL和新添加的with_structured_output 方法来提取。

# Extract entities from text
class Entities(BaseModel):"""Identifying information about entities."""names: List[str] = Field(...,description="All the person, organization, or business entities that ""appear in the text",)prompt = ChatPromptTemplate.from_messages([("system","You are extracting organization and person entities from the text.",),("human","Use the given format to extract information from the following ""input: {question}",),]
)entity_chain = prompt | llm.with_structured_output(Entities)

让我们测试一下:

entity_chain.invoke({"question": "Where was Amelia Earhart born?"}).names
# ['Amelia Earhart']

现在实现了从问题中检测出实体,接下来用全文索引将它们映射到知识图谱。
首先,我们需要定义全文索引和一个函数,该函数将生成允许有些拼写错误的全文查询。

graph.query("CREATE FULLTEXT INDEX entity IF NOT EXISTS FOR (e:__Entity__) ON EACH [e.id]")def generate_full_text_query(input: str) -> str:"""Generate a full-text search query for a given input string.This function constructs a query string suitable for a full-text search.It processes the input string by splitting it into words and appending asimilarity threshold (~2 changed characters) to each word, then combines them using the AND operator. Useful for mapping entities from user questionsto database values, and allows for some misspelings."""full_text_query = ""words = [el for el in remove_lucene_chars(input).split() if el]for word in words[:-1]:full_text_query += f" {word}~2 AND"full_text_query += f" {words[-1]}~2"return full_text_query.strip()

把上面的功能组装在一起实现图谱检索的结构化检索器。

# Fulltext index query
def structured_retriever(question: str) -> str:"""Collects the neighborhood of entities mentionedin the question"""result = ""entities = entity_chain.invoke({"question": question})for entity in entities.names:response = graph.query("""CALL db.index.fulltext.queryNodes('entity', $query, {limit:2})YIELD node,scoreCALL {MATCH (node)-[r:!MENTIONS]->(neighbor)RETURN node.id + ' - ' + type(r) + ' -> ' + neighbor.id AS outputUNIONMATCH (node)<-[r:!MENTIONS]-(neighbor)RETURN neighbor.id + ' - ' + type(r) + ' -> ' +  node.id AS output}RETURN output LIMIT 50""",{"query": generate_full_text_query(entity)},)result += "\n".join([el['output'] for el in response])return result

structured_retriever 函数从检测用户问题中的实体开始,迭代检测到的实体,使用Cypher模板来检索相关节点的一阶邻居。

print(structured_retriever("Who is Elizabeth I?"))
# Elizabeth I - BORN_ON -> 7 September 1533
# Elizabeth I - DIED_ON -> 24 March 1603
# Elizabeth I - TITLE_HELD_FROM -> Queen Of England And Ireland
# Elizabeth I - TITLE_HELD_UNTIL -> 17 November 1558
# Elizabeth I - MEMBER_OF -> House Of Tudor
# Elizabeth I - CHILD_OF -> Henry Viii
# and more...

3、最终的检索器

如开头所述,我们将结合非结构化和图谱检索器来创建传递给LLM的最终上下文。

def retriever(question: str):print(f"Search query: {question}")structured_data = structured_retriever(question)unstructured_data = [el.page_content for el in vector_index.similarity_search(question)]final_data = f"""Structured data:
{structured_data}
Unstructured data:
{"#Document ". join(unstructured_data)}"""return final_data

正如处理Python一样,可以简单地使用f-string拼接输出。


五、定义RAG Chain

我们已经成功地实现了RAG的检索组件。
首先引入查询重写功能,允许根据对话历史对当前问题进行改写。

# Condense a chat history and follow-up question into a standalone question
_template = """Given the following conversation and a follow up question, rephrase the follow up question to be a standalone question,
in its original language.
Chat History:
{chat_history}
Follow Up Input: {question}
Standalone question:"""  # noqa: E501
CONDENSE_QUESTION_PROMPT = PromptTemplate.from_template(_template)def _format_chat_history(chat_history: List[Tuple[str, str]]) -> List:buffer = []for human, ai in chat_history:buffer.append(HumanMessage(content=human))buffer.append(AIMessage(content=ai))return buffer_search_query = RunnableBranch(# If input includes chat_history, we condense it with the follow-up question(RunnableLambda(lambda x: bool(x.get("chat_history"))).with_config(run_name="HasChatHistoryCheck"),  # Condense follow-up question and chat into a standalone_questionRunnablePassthrough.assign(chat_history=lambda x: _format_chat_history(x["chat_history"]))| CONDENSE_QUESTION_PROMPT| ChatOpenAI(temperature=0)| StrOutputParser(),),# Else, we have no chat history, so just pass through the questionRunnableLambda(lambda x : x["question"]),
)

接下来,引入prompt利用集成混合检索器提供的上下文生成响应,完成RAG链的实现。

template = """Answer the question based only on the following context:
{context}Question: {question}
"""
prompt = ChatPromptTemplate.from_template(template)chain = (RunnableParallel({"context": _search_query | retriever,"question": RunnablePassthrough(),})| prompt| llm| StrOutputParser()
)

最后,继续测试混合RAG实现。

chain.invoke({"question": "Which house did Elizabeth I belong to?"})
# Search query: Which house did Elizabeth I belong to?
# 'Elizabeth I belonged to the House of Tudor.'

前面实现了查询重写功能,使RAG链能够适配允许后续问题的对话设置。
考虑到我们使用向量和关键字搜索,必须重写后续问题以优化搜索过程。

chain.invoke({"question": "When was she born?","chat_history": [("Which house did Elizabeth I belong to?", "House Of Tudor")],}
)
# Search query: When was Elizabeth I born?
# 'Elizabeth I was born on 7 September 1533.'

可以看到 When was she born? 首先被改写为“When was Elizabeth I born?”,然后使用重写后的查询来检索相关上下文并回答问题。


2024-05-12(日)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/326204.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

网页版五子棋的自动化测试

目录 前言 一、主要技术 二、测试环境的准备部署 三、测试用例 四、执行测试 4.1、公共类设计 创建浏览器驱动对象 测试套件 释放驱动类 4.2、功能测试 登录页面 注册页面 游戏大厅页面 游戏房间页面 测试套件结果 4.3、界面测试 登录页面 注册页面 游戏大…

密码学《图解密码技术》 记录学习 第十五章

目录 十五章 15.1本章学习的内容 15.2 密码技术小结 15.2.1 密码学家的工具箱 15.2.2 密码与认证 15.2.3 密码技术的框架化 15.2.4 密码技术与压缩技术 15.3 虚拟货币——比特币 15.3.1 什么是比特币 15.3.2 P2P 网络 15.3.3地址 15.3.4 钱包 15.3.5 区块链 15.3.…

web安全之登录框渗透骚姿势,新思路

不管漏洞挖掘还是挖SRC&#xff0c;登录框都是重点关注对象&#xff0c;什么漏洞都有可能出现&#xff0c; 本篇文章做个总结&#xff0c;后面发现新思路后会继续更新 万能密码 or 弱口令 SQL注入 水平越权 垂直越权 逻辑漏洞 短信轰炸 邮箱轰炸 信息泄露 验证码DOS XSS万能密…

React:Router-2. createBrowserRouter函数式

参考文档&#xff1a;ReactRouter官网 前边的文章 BrowserRouter组件式路由 提供了组件式路由的方式&#xff0c;在react-router6.4.0及以上版本&#xff0c;提供了 createBrowserRouter 函数式路由创建方式。 一、创建路由 1. 新建router.js文件&#xff0c;使用createBrow…

rac asm新增磁盘报0RA-15333或ORA-15075

虚拟化做的rac&#xff0c;发现原来加盘直接把sdb、sdc、sdd、sde加到asm里了&#xff0c;后面通过udev绑定的盘&#xff0c;增加到asm里就报错&#xff1a; [DBT-30007]Addition of disks to disk group DATA failed ORA-15032:not all alterations performed 0RA-15333: d…

迷宫中离入口最近的出口

题目链接 迷宫中离入口最近的出口 题目描述 注意点 maze[i][j] 要么是 ‘.’ &#xff0c;要么是 ‘’entrance.length 2entrance 一定是空格子出口的含义是 maze 边界上的空格子entrance格子不算出口 解答思路 广度优先遍历找到走i步时所能到达的所有节点位置&#xff0…

idea使用git不提示账号密码登录,而是输入token问题解决

idea 或者 webstream 等全家桶软件 使用git 推送代码时&#xff0c;不提示账号密码登录&#xff0c;而是输入token问题解决 你的代码仓库是gitlab 然后打开修改代码后推送时&#xff0c;会默认使用gitlab插件&#xff0c;所以提示数据token 解决方式就是把gitlab插件取消使用这…

ASP.NET MVC 如何使用 Form Authentication?

前言 .NET 的 Form Authentication 是一种基于表单的简单且灵活的身份验证机制&#xff0c;用户通过输入用户名和密码来登录应用程序&#xff0c;并且通过配置来控制用户访问权限。 在使用 Form Authentication 时&#xff0c;我们需要在 web.config 文件中配置身份验证和授权…

工业级路由器的穿透力是不是更强(原创科普)

今天我想和大家聊聊工业级路由器的一个重要特性——穿透力。作为一名从事工业网络通信的工程师,我发现很多用户在选择工业级路由器时,都会问到一个问题:"工业级路由器的穿透力是不是更强?"下面就让我来为大家解答这个疑问。当然如果有通信产品需要也可以关注星创易联…

导航app为什么知道还有几秒变绿灯?

在使用地图导航app行驶至信号灯的交叉路口时&#xff0c;这些应用程序会贴心地告知用户距信号灯变化还有多少秒&#xff0c;无论是即将转为绿灯还是红灯。这一智能化提示不仅使得驾驶员能适时做好起步或刹车的准备&#xff0c;有效缓解了因等待时间不确定而产生的焦虑情绪&…

如何写好网评文章?写好了怎么去投稿呢,教程来了

如何写好网评文章&#xff0c;可谓仁者见仁、智者见智。俗话说&#xff1a;“冰冻三尺非一日之寒。”写好网评文章决不是一朝一夕能够练成的&#xff0c;是一个漫长的修炼的过程&#xff0c;需要我们耐得住寂寞、静得下心神。从事网评写作六年多&#xff0c;我有一些心得体会和…

LLAMA3中文语料 fine tune 测试与比对

概述&#xff1a; Meta开发并发布了Meta-Lama 3大语言模型家族&#xff08;LLM&#xff09;&#xff0c;Llama 3指令调优模型针对对话用例进行了优化&#xff0c;在常见的行业基准上优于许多可用的开源聊天模型。本文尝试对LLAMA3 在中文语料中尝试进行fine tune 为后续对 通义…

C++ vs Rust vs Go 性能比较

本文对C、Rust和Go三种编程语言编写的gunzip程序进行了性能比较&#xff0c;通过基准测试试图尽可能公平的比较它们的性能。原文: Performance — C vs Rust vs Go 本文将通过一些基准测试&#xff0c;比较 C 和 Rust 以及 Go 编写的相同程序的性能。我们将尽最大努力将语言差异…

Ubuntu20.04右键打不开终端

今天用virtualbox安装了ubuntu20.04 问题&#xff1a;右键打开终端&#xff0c;怎么也打开不了&#xff01; 点了也没反应&#xff0c;或者鼠标转小圈圈&#xff0c;然后也没有反应… 解决方法&#xff1a; 1、Ctrl Alt F6 先切换到终端访问界面 mac电脑 Ctrl Alt F6 …

计算机网络【应用层】邮件和DNS

文章目录 电子邮件DNSDNS提供的服务&#xff1a;域名分级域名解析流程DNS资源记录DNS服务器类型 电子邮件 使用SMTP协议发送邮件之前&#xff0c;需要将二进制多媒体数据编码为ASCII码SMTP一般不使用中间邮件服务器发送邮件&#xff0c;如果收件服务器没开机&#xff0c;那么会…

鸿蒙内核源码分析(消息封装篇) | 剖析LiteIpc(上)进程通讯内容

基本概念 LiteIPC是OpenHarmony LiteOS-A内核提供的一种新型IPC&#xff08;Inter-Process Communication&#xff0c;即进程间通信&#xff09;机制&#xff0c;为轻量级进程间通信组件&#xff0c;为面向服务的系统服务框架提供进程间通信能力&#xff0c;分为内核实现和用户…

测试人的福音:开源流量回放工具快速上手实践

笔者前段时间在参加测开大会时了解到了一款开源的自动化回归测试工具 AREX。主要是通过复制线上真实流量到测试环境进行回归测试&#xff0c;同时还做到了接口返回值的比对和写接口的验证&#xff0c;回放不会产生真实的数据或者调用&#xff0c;都是基于 Mock 数据的&#xff…

5分钟学设计模式:简单工厂与美食街的不期而遇

大家好&#xff0c;我是知微。 写代码好几年&#xff0c;是不是还纠结于这些问题&#xff1a; 面对一堆if-else&#xff0c;代码越写越长&#xff0c;维护起来比攀登珠穆朗玛峰还难每次只敢小心翼翼改个小功能&#xff0c;生怕程序突然“嘭”一声&#xff0c;全炸了想学习大佬…

18.Docker学习

1.Docker应用场景 Docker借鉴了标准集装箱的概念。标准集装箱将货物运往世界各地&#xff0c;Docker&#xff08;模板&#xff09;将软件运往各个环境&#xff08;测试环境和生产环境拉取镜像&#xff08;实例&#xff09;&#xff09;&#xff0c;相当于是一个模子刻出来的 …