【手摸手学 RAG】RAG性能再突破!当知识图谱遇上LLM,打造你的终极问答机器人
大家好,我是胡琦。
在之前的教程里,我们从0到1搭建了一个基于文本检索的RAG系统。它很棒,但很快我们就会发现它的“天花板”:当面对需要多步推理、关系查询的复杂问题时,它就显得力不从心了。
比如你问它:“查询一下,和发表了关于‘图神经网络’论文的作者在同一个机构的,还有哪些作者?”
传统的RAG大概率会卡住。因为它看到的是一堆孤立的文本片段,很难理解“作者”、“论文”、“机构”之间复杂的连接关系。
如何破局?答案就是——知识图谱(Knowledge Graph, KG)!
今天,我们就一起探索如何将知识图谱的结构化推理能力,与LLM的强大语言能力相结合,构建一个能够深度思考、精准回答复杂问题的Graph RAG系统。
第一步:蓝图升级 —— 为什么需要知识图谱?
传统的RAG,知识库就像一本“流水账日记”,信息都平铺直叙地记录着。而知识图谱,则像一张“人物关系网”,清晰地描绘了实体(节点)之间的关系(边)。
知识图谱的优势:
- 结构化知识:能清晰表达“谁-做了-什么”、“什么-属于-哪里”这类关系。
- 精准查询:通过图查询语言(如Cypher),可以进行多跳(Multi-hop)的复杂关系查询。
- 可解释性强:返回的结果和路径一目了然,方便溯源。
我们的新蓝图——Graph RAG架构如下:它是一个混合系统,同时拥有向量数据库和图数据库,并增加了一个“智能路由”来决定该走哪条路。
图片来源: all-in-rag
第二步:点石成金 —— 从非结构化文本中抽取“知识”
我们的核心任务,就是从现有的海量文档中,自动地构建出这张“关系网”。这个过程就像“点石成金”,而LLM就是我们的“魔法棒”。
1. 知识建模:
在抽取前,我们先要定义好想抽取哪些东西。比如,对于一篇学术论文,我们关心的可能是:论文 (Paper)
、作者 (Author)
、机构 (Institution)
、关键词 (Keyword)
这些节点,以及 发表了 (PUBLISHED)
、作者是 (AUTHOR_OF)
、隶属于 (AFFILIATED_WITH)
这些关系。
2. LLM魔法:抽取三元组
我们可以利用LLM强大的信息抽取能力,让它读取文本,并按照我们定义的模型,输出知识三元组(头实体,关系,尾实体)。
实战代码(用LangChain实现知识抽取):
import os
from langchain_openai import ChatOpenAI
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.pydantic_v1 import BaseModel, Field
from typing import List
# 确保你已设置 OPENAI_API_KEY
# os.environ["OPENAI_API_KEY"] = "sk-..."
# 1. 定义我们希望LLM输出的数据结构
class Triple(BaseModel):
"""一个知识图谱中的三元组,表示头实体、关系和尾实体"""
head: str = Field(..., description="头实体")
relation: str = Field(..., description="关系")
tail: str = Field(..., description="尾实体")
class Triples(BaseModel):
"""三元组的列表"""
triples: List[Triple]
# 2. 准备一段示例文本
text_chunk = """
在2023年的NeurIPS会议上,来自华为诺亚方舟实验室的张三发表了一篇名为《图神经网络新范式》的论文。
他的同事李四也参与了这项研究。
"""
# 3. 编写一个强大的Prompt,指导LLM进行抽取
extract_prompt = ChatPromptTemplate.from_template(
"""
从以下文本中提取知识三元组。请识别'作者'、'论文'、'机构'、'会议'等实体,
以及'发表了'、'参与了'、'来自'等关系。
文本: ```{text}```
"""
)
# 4. 初始化一个支持结构化输出的LLM
# 注意:并非所有模型都擅长结构化输出,GPT-4/Turbo, Claude 3, Qwen-max等较新模型效果更好
llm = ChatOpenAI(model="gpt-4-turbo", temperature=0)
extraction_chain = extract_prompt | llm.with_structured_output(schema=Triples)
# 5. 执行抽取!
extracted_triples = extraction_chain.invoke({"text": text_chunk})
print(extracted_triples)
输出可能为:
{
"triples": [
{"head": "张三", "relation": "来自", "tail": "华为诺亚方舟实验室"},
{"head": "张三", "relation": "发表了", "tail": "《图神经网络新范式》"},
{"head": "李四", "relation": "参与了", "tail": "《图神经网络新范式》"},
{"head": "《图神经网络新范式》", "relation": "发表于", "tail": "NeurIPS会议"}
]
}
看!我们成功地将一段杂乱的文本,转换成了结构清晰的知识!
第三步:构建大脑 —— 将知识注入图数据库
抽取出三元组后,我们需要一个地方来存储和管理它们。这就是图数据库(如 Neo4j)的用武之地。
实战代码(使用LangChain连接Neo4j并写入数据):
from langchain_community.graphs import Neo4jGraph
# 1. 连接到你的Neo4j数据库 (请先通过Docker等方式启动一个Neo4j实例)
graph = Neo4jGraph(
url="bolt://localhost:7687",
username="neo4j",
password="your_password"
)
# 2. 将我们上一步抽出的三元组写入图数据库
for triple in extracted_triples.triples:
# 使用Cypher语句的MERGE,如果节点/关系已存在则不会重复创建
graph.query(
"""
MERGE (h {name: $head})
MERGE (t {name: $tail})
MERGE (h)-[:`{relation}`]->(t)
""".format(relation=triple.relation),
params={"head": triple.head, "tail": triple.tail}
)
print("知识图谱构建完成!")
现在,打开Neo4j的浏览器界面,你就能看到一个可视化的关系网络,非常酷!
第四步:智能路由 —— 让问题找到“对”的路
现在我们的系统同时拥有了“流水账日记”(向量数据库)和“人物关系网”(图数据库)。当一个问题进来,我们该问谁呢?
答案还是LLM!我们再创建一个“路由”LLM,让它当“总调度官”。
实战代码(构建一个查询路由器):
# 1. 定义路由器的Prompt,让它做选择题
router_prompt_template = """
根据用户的提问,判断该问题更适合通过哪种方式回答:
1. 'graph': 当问题涉及多个实体及其复杂关系,需要多步推理时。例如:“谁和谁共同发表了什么论文?”
2. 'vector': 当问题是开放式的、描述性的,或是查询单个实体的具体信息时。例如:“介绍一下图神经网络。”
用户问题: `{question}`
你的选择是:
"""
router_prompt = ChatPromptTemplate.from_template(router_prompt_template)
router_chain = router_prompt | llm
# 2. 模拟用户提问并进行路由
question1 = "介绍一下华为诺亚方舟实验室。"
question2 = "张三和谁一起研究了《图神经网络新范式》这篇论文?"
route1 = router_chain.invoke({"question": question1})
route2 = router_chain.invoke({"question": question2})
print(f"问题1的路由选择: {route1.content}") # 应该会选择 'vector'
print(f"问题2的路由选择: {route2.content}") # 应该会选择 'graph'
后续流程:
- 如果路由结果是
'vector'
,我们就走传统RAG流程:问题 -> 向量检索 -> LLM生成。 - 如果路由结果是
'graph'
,我们就需要再让LLM把自然语言问题转换成Cypher查询语句,然后执行图查询,最后将查询结果交给LLM生成答案。
后记
太棒了!我们一起完成了一次从0到1的Graph RAG系统搭建之旅。回顾一下:
- 理解原理:我们明白了为何需要知识图谱来处理复杂的关系查询。
- 知识抽取:我们学会了如何利用LLM从文本中自动抽取结构化的三元组。
- 图谱构建:我们将抽取的知识存入了Neo4j图数据库,构建了一个可视化的“知识大脑”。
- 智能路由:我们设计了一个调度官,能智能地为用户问题选择最佳的查询路径。
通过Graph RAG,我们的问答机器人不再是一个只会“背书”的简单角色,而是进化成了一个具备初步“推理”能力的智能助手。
这趟旅程充满了挑战,但也乐趣无穷。技术的边界正由我们每一个人亲手拓展。快去用你自己的数据,构建一个更聪明的RAG吧!
本文基于[datawhalechina/all-in-rag]项目整理,仅代表个人学习心得,不代表任何官方观点。小伙伴们有疑问欢迎交流讨论,一起学习一起进步!
- 点赞
- 收藏
- 关注作者
评论(0)