
本方案介绍如何使用 PolarDB、通义千问和 LangChain 搭建 GraphRAG 系统,结合知识图谱和向量检索提升问答质量。通过实例展示了单独使用向量检索和图检索的局限性,并通过图 + 向量联合搜索增强了问答准确性。PolarDB 支持 AGE 图引擎和 pgvector 插件,实现图数据和向量数据的统一存储与检索,提升了 RAG 系统的性能和效果。
业务场景
知识图谱
知识图谱 (KG) 是谷歌提出的一种知识表示形式,它通过互联的节点和实体捕捉知识,以结构化的形式表示关系和信息。
知识图谱具有以下优势:
- 结构化信息:知识图谱将信息以节点(实体)和边(关系)的形式组织,使得复杂信息结构化,便于存储和查询。
- 语义理解:通过明确的关系定义,知识图谱可以帮助系统更好地理解信息之间的语义关系,提升信息检索和自然语言处理的效果。
- 知识关联性:知识图谱能够将不同的知识点联系起来,形成更丰富的知识网络,帮助用户发现隐藏的关联。
- 支持推理:基于知识图谱,系统可以进行逻辑推理,从已知的信息推导出新的信息,提高智能应用的能力。
- 可视化:知识图谱通常可以通过图形化的方式展示,使得复杂关系一目了然,便于用户理解和分析。
知识图谱被广泛应用于金融风控,企业知识管理以及社交网络分析等多种业务场景中。

RAG
检索增强生成(Retrieval Augmented Generation, RAG)作为一种强大的技术,它结合了信息检索与生成模型的创新方法,用来解决大语言模型 (LLM) 的局限性问题。
RAG 通过利用从各种来源检索到的相关数据来增强 LLM 提示,来实现更为准确和上下文相关的文本生成。RAG 系统的准确性在很大程度上依赖于它们获取相关、可验证信息的能力。
RAG 中信息检索通常使用诸如关键词匹配或语义相似性等技术来实现的。在语义相似性中,例如,数据被表示为由 AI 嵌入模型生成的数值向量,这些向量试图捕捉其含义。前提是,相似的向量在向量空间中相互靠近,然后通过近似最近邻(ANN)搜索获取相似的信息;关键词匹配则更为简单,使用准确的关键词匹配来查找信息,通常使用诸如全文检索等算法。
但是,基于关键词或相似性搜索构建的简单 RAG 系统在需要推理的复杂查询中表现不佳。简单的相似度的检索无法对实体之间的关系进行推理和判断。
GraphRAG
GraphRAG 是一种新的 RAG 系统,它结合了知识图谱和大型语言模型(LLM)的优势。在 GraphRAG 中,知识图谱作为事实信息的结构化资源库,LLM 将自然语言转换为知识图谱的查询信息,从图谱中检索相关知识,并生成针对问题的回答。
GraphRAG 解决了上述许多局限性,因为它能够对数据进行推理。
GraphRAG 具有以下优势:
- 改善信息检索:通过理解实体之间的基本联系,GraphRAG 可以更准确地识别相关信息。
- 增强上下文理解:知识图谱为查询理解和响应生成提供了更丰富的上下文。
- 减少幻觉:通过将响应建立在事实知识上,GraphRAG 可以减轻生成错误信息的风险。
最佳实践
本文以一个开源的股票知识图谱为例,介绍如何使用 PolarDB+ 通义千问+LangChain 搭建一个 GraphRAG 系统。
技术实现
PolarDB
PolarDB PostgreSQL 版(下文简称为 PolarDB)是一款阿里云自主研发的云原生关系型数据库产品,100% 兼容 PostgreSQL,高度兼容 Oracle 语法;采用基于 Shared-Storage 的存储计算分离架构,具有极致弹性、毫秒级延迟、HTAP 的能力和高可靠、高可用、弹性扩展等企业级数据库特性。同时,PolarDB 具有大规模并行计算能力,可以应对 OLTP 与 OLAP 混合负载。
PolarDB 具备高度兼容 apache AGE 的图引擎,支持对知识图谱的存储和查询检索。同时 PolarDB 具备 pgvector 增强插件,支持对向量数据的存储与检索。
通义千问
通义千问是由阿里云自主研发的大语言模型,用于理解和分析用户输入的自然语言,在不同领域和任务为用户提供服务和帮助。
在 RAG 场景中,通义千问基于用户的查询或上下文,结合用户兴趣或历史交互相关的信息,生成更加个性化的回复。
LangChain
LangChain 是一个开源的工具包和框架,旨在简化和加速基于语言模型的应用程序开发。LangChain 的核心在于将强大的语言模型(如 OpenAI 的 GPT 系列、阿里云的通义千问等)与实际应用结合起来,帮助开发者构建诸如聊天机器人、文本生成、知识管理、代码辅助等多种自然语言处理(NLP)相关应用。
LangChain 中实现了 apache age 图插件和 pgvector 插件的支持,可以支持对于知识图谱和向量两种检索方式。

查询流程
主要的查询流程如下图所示:

- 用户提出相关问题
- RAG 系统在知识图谱中检索相关的答案
- RAG 系统把知识图谱的检索结果作为向量检索的相关信息,用向量相似性检索的方式检索相关的文档
- RAG 系统将多种结果输入给大语言模型
- 大语言模型根据输入的信息组织问答结果
- RAG 系统将问答结果返回给用户
建议配置
为了得到良好的体验,建议使用以下配置:
| 项目 | 推荐配置 |
|---|
| PolarDB 版本 | 标准版 兼容 PostgreSQL 14 |
| CPU | >16 Core |
| 内存 | >64 GB |
| 磁盘 | >100GB (AUTOPL) |
| 版本 | >2.0.14.23.1 |
实战步骤
准备工作
环境准备
申请阿里云灵积模型 api key(如已有 key 可略过此步骤)
进入阿里云官网,注册或登录;
搜索灵积模型服务,开通服务;
进入产品控制台,创建 api key;
该 api key 是访问灵积模型服务的凭证,需要妥善保管。
- 环境中安装相关的 python 包
pip install langchain
pip install langchain-community
pip install python-dotenv
pip install dashscope
pip install psycopg
如遇到一些安装失败的问题,需要升级 python 版本 > 3.7。
- 修改 age_graph.py 文件
age_graph.py 位于 /site-packages/langchain_community/graphs/age_graph.py 下,需要修改两处地方:
LOAD 'age'; sql 语句修改为 select * from ag_catalog.get_cypher_keywords() limit 0; 确保 PolarDB 可以正确加载对应的扩展
MATCH ()-[e]-() RETURN collect(distinct label(e)) as labels 修改 MATCH ()-[e]->() RETURN collect(distinct label(e)) as labels 加速对图的 schema 的获取
数据库准备
创建 AGE 插件
AGE 是一个为 PostgreSQL 系列数据库打造的扩展,旨在增强其处理图数据的能力。AGE 旨在结合关系型数据库与图数据库的优势,提供一个高性能、灵活且易于扩展的解决方案。
create extension age;
创建 vector 插件
Vector 是一个 PostgreSQL 的扩展插件,用于高效地处理和查询高维向量数据。vector 插件提供了高维向量的存储以及基于向量的近似最近邻(Approximate Nearest Neighbor, ANN)搜索功能。
create extension vector;
数据入库
将数据进行下载。数据分为两部分,一部分为图数据 (data/import 目录),包含了人员 (Executive), 产业 (Industry), 股票 (Stock), 概念(Concept)等点以及人员 - 股票,股票 - 概念,股票 - 产业之间的关联关系,这部分数据以图的方式存储到 PolarDB 中。

另一部分为每个股票的介绍信息(data/stockpage),原始数据为股票的网页信息,切分后以向量的方式存储到数据库中以提供进一步的信息。
图数据
创建一个名为 stock_graph 的图用于存储该知识图谱:
SELECT create_graph('stock_graph');
附录 1 中脚本可以将数据转换为 Cypher SQL 脚本,配合客户端工具,如 psql 等可完成数据导入。转换后的 sql 脚本示意如下:
SELECT create_vlabel('stock_graph','person');
SELECT * FROM cypher('stock_graph', $$ CREATE (:person {person_id:'dddbd3ad0f2e3fca80da88296298bb51',name:'杜玉岱',gender:'男',age:'58'}) $$ ) as (n agtype);
SELECT * FROM cypher('stock_graph', $$ CREATE (:person {person_id:'2f867939e123f10437a15a127799248e',name:'延万华',gender:'男',age:'45'}) $$ ) as (n agtype);
SELECT * FROM cypher('stock_graph', $$ CREATE (:person {person_id:'e68b3ae7a003c60cd9d50e371cdb3529',name:'宋军',gender:'男',age:'48'}) $$ ) as (n agtype);
...
SELECT * FROM cypher('stock_graph', $$ MATCH (a:person), (b:stock) WHERE a.person_id = 'dddbd3ad0f2e3fca80da88296298bb51' AND b.stock_id = '601058' CREATE (a)-[e:employ_of {jobs:'董事长/董事'} ]->(b) RETURN e$$) as (e agtype);
cypher(, $$ (a:person), (b:stock) a.person_id b.stock_id (a)[e:employ_of {jobs:} ](b) e$$) (e agtype);
cypher(, $$ (a:person), (b:stock) a.person_id b.stock_id (a)[e:employ_of {jobs:} ](b) e$$) (e agtype);
...
向量数据
文本信息按照一定规则切割后,以向量的形式存储到数据库中。切分采用 langchain 的 RecursiveCharacterTextSplitter 切分器,实际使用中可根据需要进行切分,并使用通义大模型转换为向量。
附录 2 中脚本可实现向量数据入库过程。
安装使用到的 python 包:
pip install BeautifulSoup
pip install bs4
数据库中创建了对应的数据表 docs 用于记录文本以及对应的文本向量。
CREATE TABLE IF NOT EXISTS docs (
id bigserial primary key,
title text,
content text,
tokens integer,
embedding vector(1536)
);
在向量上创建索引,此处使用 hnsw 索引,并使用欧式距离 (l2 距离) 进行查询。(索引类型和距离计算方式可根据实际需要进行选择)
CREATE INDEX ON docs USING hnsw (embedding vector_l2_ops);
查询
以问题'李士祎关连的股票信息?'为例进行查询,并对于单独使用向量检索、图检索以及图加向量检索的结果进行对比。
单独使用向量检索
定义大语言模型为通义千问
DASHSCOPE_API_KEY 从环境变量中读取:
import os
DASHSCOPE_API_KEY=os.environ["DASHSCOPE_API_KEY"]
from langchain.embeddings import DashScopeEmbeddings
embeddings = DashScopeEmbeddings(model="text-embedding-v1", dashscope_api_key=DASHSCOPE_API_KEY)
from langchain_community.llms import Tongyi
llm_tongyi=Tongyi(temperature=1)
RetrievalQA 是 LangChain 中封装的一个 chain,可以实现基于本地知识库的问答。
问答大语言模型过程中需要输入相关文档,来进行答案的生成。这里定义了一个 Retriever,通过向量检索的方式来获取相似度最大的 5 篇文档:
from langchain.schema import Document
from langchain_core.retrievers import BaseRetriever
class CustomRetriever(BaseRetriever):
def __init__(self):
super().__init__()
def _get_relevant_documents(self, query: str) -> list[Document]:
relevant_docs = []
cur = conn.cursor()
cur.execute("SELECT content FROM docs ORDER BY embedding <=> '{}' LIMIT 5".format(embeddings.embed_query(query)))
top3_docs = cur.fetchall()
cur.close()
for doc in top3_docs:
relevant_docs.append(Document(page_content=doc[0]))
return relevant_docs
custom_retriever = CustomRetriever()
进行问答:
from langchain.chains import RetrievalQA
qa_chain = RetrievalQA.from_chain_type(llm=llm_tongyi, retriever=custom_retriever, verbose=True)
response = qa_chain.invoke("李士祎关连的股票信息?")
print(response)
问答结果如下:
根据提供的信息,没有找到与李士祎相关的股票信息。请提供更多的信息以便我能更准确地回答您的问题。如果无法提供更多细节,那么我将无法给出具体的答案。
可见查询结果无法获取正确的答案。
从执行过程中可以看到,直接执行向量相似度的执行结果如下:
('科士达 (002518) 公司资料_F10_同花顺金融服务网...',)
('佳士科技 (300193) 公司资料_F10_同花顺金融服务网...',)
('日签署的股权转让协议,沪士控股及碧景控股...',)
('5 年 8 月 1 日,香港碧景将其拥有的本公司全部股权转让...',)
('其中合并报表的有:7 家。序号关联公司名称参控关系...',)
这是因为涉及到的网页材料中与人名关联度较低,主要提供的是上市公司的相关信息,两者关联性不大,因此向量检索模式无法获取答案。
单独使用图搜索
LangChain 中图引擎实现了对于 AGE 的支持,使用时需要先定义一个图对象,并获取图的模式。这里图的模式是指点和边的类型信息,用于生产图的查询语言。
from langchain.chains import GraphCypherQAChain
from langchain_community.graphs.age_graph import AGEGraph
conf = { "database": "xxx", "user":"xxx", "host":"pc-xxxx.rwlb.rds.aliyuncs.com", "port":"1921", "password":"xxx"}
graph = AGEGraph(graph_name="stock_graph", conf=conf)
graph.refresh_schema()
GraphCypherQAChain 是 LangChain 中封装的一个 chain,可以将自然语言转为图查询,实现本地图谱的问答。
graphchain = GraphCypherQAChain.from_llm(llm_tongyi, graph=graph, verbose=True, top_k=5)
response = graphchain.invoke("李士祎关连的股票信息?")
print(response)
问答结果如下:
根据提供的信息,这里有关于股票的信息:股票名称为酒鬼酒,代码为 799。但是,没有直接提到李士祎相关的具体股票信息。所以,基于给出的数据,我们不知道李士祎具体的关联股票详情。
可见基于图的查询虽然查到了李士祎关联的股票为酒鬼酒,且股票代码为 799,但是由于图中并没有存储酒鬼酒的信息,因此也无法给出具体的信息。
从执行的过程中可以看到,langchain 生成了 Cypher 语句并在知识图谱中进行了执行,查询到了酒鬼酒的名称以及股票代码。
Generated Cypher:
MATCH (p:person)-[r:employ_of]->(s:stock) WHERE p.name = "李士祎" RETURN s.name, s.code
Full Context:
[{'name': '酒鬼酒', 'code': '799'}]
注意:如果 langchain 中生成的 Cypher 不正确,还可以使用 prompt 的方式给 langchain 提供示例 Cypher 以帮助生成。当然,也可以自定义一个 Retriever,通过连接数据库的方式执行自定义的 Cypher 以获得更加准确的回答。
图 + 向量联合搜索增强
以上两种查询的结果都不尽人意,因为向量检索和知识图谱都只存储了独立的内容,无法独自生成问答结果。将以上两种查询方式进行结合,把知识图谱的查询结果作为向量查询的输入信息,通过向量查询获取相关文档后,由 LLM 最终生成更加完整的问答结果。
此处定义了一个 prompt 的模版,把知识图谱的查询结果作为向量检索的输入,来获得更为准确的问答结果。
from langchain_core.prompts.prompt import PromptTemplate
from langchain.chains import LLMChain
graphchain = GraphCypherQAChain.from_llm(llm_tongyi, graph=graph, verbose=True, top_k=5, return_direct=True,
template = """Task:Generate more detailed information about the raw answer.
The question is:`{question}`the raw answer from knowledge graph in format:`{answer}`please give more detailed answer about raw answer"""
prompt = PromptTemplate(input_variables=["question", "answer"], template=template)
llm_chain = LLMChain(llm=llm_tongyi, prompt=prompt)
def dynamic_query(question):
answer = graphchain.invoke(question)
if answer['result'] == '':
return "没有找到答案"
formatted_prompt = prompt.format(question=question, answer=answer['result'][0])
answer = qa_chain.invoke(formatted_prompt)
return answer
response = dynamic_query("李士祎关连的股票信息?")
print(response['result'])
问答结果如下:
根据提供的信息,与李士祎相关的股票'酒鬼酒'的详细信息如下:
- 公司名称:酒鬼酒股份有限公司
- 股票代码:000799
- 所属地域:湖南省
- 英文名称:Jiugui Liquor Co., Ltd.
- 公司网址:www.jiuguijiu000799.com
- 主营业务:从事生产、销售曲酒系列产品。
- 主要产品:
- 控股股东及实际控制人:中皇有限公司(持有酒鬼酒股份有限公司股份比例:31.00%)
请注意,以上信息是基于所提供的上下文得出的结论,如果需要更详细的财务数据或其他具体信息,请参考官方公告或相关财经网站。
该问答结果不但准确地回答出相关的股票为'酒鬼酒',并且给出了酒鬼酒的相关信息。相比只使用向量查询和图查询,该结果更加符合预期,满足实际应用需求。
从执行过程中可以看到,当知识图谱中检索了相应的结果后,问题转换为:
Task:Generate more detailed information about the raw answer.
The question is:李士祎关连的股票信息?
the raw answer from knowledge graph in format:{'name_0': '酒鬼酒', 'code': '799'}
please give more detailed answer about raw answer
在向量检索中包含了酒鬼酒的相关信息,使得向量检索的结果更加准确:
('酒鬼酒 (000799) 公司资料_F10_同花顺金融服务网...',)
('名:*ST 酒鬼公司网址:www.jiuguijiu000799.com...',)
('其中合并报表的有:0 家。序号关联公司名称参控关系...',)
('华谊 B 股 (900909) 公司资料_F10_同花顺金融服务网...',)
('华电 B 股 (900937) 公司资料_F10_同花顺金融服务网...',)
总结
如何有效利用私有数据一直是 RAG 系统中面临的重大挑战,其中知识图谱是企业私有数据的重要组成部分。得益于 PolarDB 的可扩展性,PolarDB 数据库中可同时存储向量数据和图谱数据,进行统一的存储和检索。同时相较于传统的向量检索,PolarDB 中可将图检索与向量检索相结合,提供更高质量的 RAG 问答结果,满足应用需求。
