跳到主要内容 基于 Milvus 向量数据库实现 GraphRAG 技术详解 | 极客日志
Python AI 算法
基于 Milvus 向量数据库实现 GraphRAG 技术详解 GraphRAG 技术结合知识图谱增强 RAG 应用检索能力。本文介绍了基于 Milvus 向量数据库实现 GraphRAG 的完整流程,涵盖环境配置、数据准备、索引构建、实体与关系加载、搜索引擎搭建及查询测试。通过 Milvus 存储实体向量嵌入,结合 LLM 实现精准问答与问题生成。此外,还补充了生产环境下的性能优化与部署建议,助力开发者高效落地复杂信息检索系统。
女王 发布于 2025/2/7 更新于 2026/4/20 1 浏览GraphRAG 技术借助知识图谱,给 RAG 应用注入了新的动力,使其能够在海量数据中精确检索所需信息。本文将带你了解 GraphRAG 的实现方法,包括如何创建索引以及如何利用 Milvus 向量数据库进行查询,助你在信息检索的道路上事半功倍。
1. 先决条件 pip install --upgrade pymilvus
pip install git+https://github.com/zc277584121/graphrag.git
注意:通过一个分支仓库来安装 GraphRAG,这是因为 Milvus 的存储功能在本文编写时还未被官方正式合并。
2. 数据准备 为了进行 GraphRAG 索引,我们需要准备一个小型文本文件。我们将从 Gutenberg 项目下载一个大约一千行的文本文件,这个文件包含了关于达芬奇的故事。
利用这个数据集,构建一个涉及达芬奇所有关系的知识图谱索引,并使用 Milvus 向量数据库来检索相关知识,以便回答相关问题。
以下是 Python 代码,用于下载文本文件并进行初步处理:
import nest_asyncio
nest_asyncio.apply()
import os
import urllib.request
index_root = os.path.join(os.getcwd(), 'graphrag_index' )
os.makedirs(os.path.join(index_root, 'input' ), exist_ok=True )
url = "https://www.gutenberg.org/cache/epub/7785/pg7785.txt"
file_path = os.path.join(index_root, 'input' , 'davinci.txt' )
urllib.request.urlretrieve(url, file_path)
with open (file_path, 'r+' , encoding='utf-8' ) as file:
lines = file.readlines()
file.seek(0 )
file.writelines(lines[:934 ])
file.truncate()
3. 初始化工作空间 现在,使用 GraphRAG 对文本文件进行索引。首先运行 graphrag.index --init 命令初始化工作空间。
python -m graphrag.index --init --root ./graphrag_index
4. 配置环境变量文件 在索引的根目录下,能找到一个名为 .env 的文件。要启用这个文件,请将你的 OpenAI API 密钥添加进去。
本例将使用 OpenAI 模型作为一部分,请准备好你的 API 密钥。
GraphRAG 索引的成本相对较高,因为它需要用 LLM 处理整个文本语料库。运行这个演示可能会花费一些资金。为了节省成本,你可以考虑将文本文件缩减尺寸。
5. 执行索引流程 运行索引需要一些时间,请耐心等待。执行完毕后,你可以在 ./graphrag_index/output// 路径下找到一个新创建的文件夹,里面包含了多个 parquet 格式的文件。
python -m graphrag.index --root ./graphrag_index
6. 使用 Milvus 向量数据库进行查询 在查询阶段,我们使用 Milvus 来存储 GraphRAG 本地搜索中实体描述的向量嵌入。
这种方法将知识图谱的结构化数据与输入文档的非结构化数据相结合,为 LLM 提供了额外的相关实体信息,从而能够得出更准确的答案。
import os
import pandas as pd
import tiktoken
from graphrag.query.context_builder.entity_extraction import EntityVectorStoreKey
from graphrag.query.indexer_adapters import (
read_indexer_entities,
read_indexer_relationships,
read_indexer_reports,
read_indexer_text_units,
)
from graphrag.query.input .loaders.dfs import store_entity_semantic_embeddings
from graphrag.query.llm.oai.chat_openai import ChatOpenAI
from graphrag.query.llm.oai.embedding import OpenAIEmbedding
from graphrag.query.llm.oai.typing import OpenaiApiType
from graphrag.query.question_gen.local_gen import LocalQuestionGen
from graphrag.query.structured_search.local_search.mixed_context import LocalSearchMixedContext
from graphrag.query.structured_search.local_search.search import LocalSearch
from graphrag.vector_stores import MilvusVectorStore
output_dir = os.path.join(index_root, "output" )
subdirs = [os.path.join(output_dir, d) for d in os.listdir(output_dir)]
latest_subdir = max (subdirs, key=os.path.getmtime)
INPUT_DIR = os.path.join(latest_subdir, "artifacts" )
COMMUNITY_REPORT_TABLE = "create_final_community_reports"
ENTITY_TABLE = "create_final_nodes"
ENTITY_EMBEDDING_TABLE = "create_final_entities"
RELATIONSHIP_TABLE = "create_final_relationships"
COVARIATE_TABLE = "create_final_covariates"
TEXT_UNIT_TABLE = "create_final_text_units"
COMMUNITY_LEVEL = 2
7. 从索引过程中加载数据 在索引过程中,会生成几个 parquet 文件。我们将其加载到内存中,并将实体描述信息存储在 Milvus 向量数据库中。
entity_df = pd.read_parquet(f"{INPUT_DIR} /{ENTITY_TABLE} .parquet" )
entity_embedding_df = pd.read_parquet(f"{INPUT_DIR} /{ENTITY_EMBEDDING_TABLE} .parquet" )
entities = read_indexer_entities(entity_df, entity_embedding_df, COMMUNITY_LEVEL)
description_embedding_store = MilvusVectorStore(collection_name="entity_description_embeddings" )
description_embedding_store.connect(uri="./milvus.db" )
entity_description_embeddings = store_entity_semantic_embeddings(entities=entities, vectorstore=description_embedding_store)
print (f"实体数量:{len (entity_df)} " )
entity_df.head()
relationship_df = pd.read_parquet(f"{INPUT_DIR} /{RELATIONSHIP_TABLE} .parquet" )
relationships = read_indexer_relationships(relationship_df)
print (f"关系数量:{len (relationship_df)} " )
relationship_df.head()
report_df = pd.read_parquet(f"{INPUT_DIR} /{COMMUNITY_REPORT_TABLE} .parquet" )
reports = read_indexer_reports(report_df, entity_df, COMMUNITY_LEVEL)
print (f"报告记录:{len (report_df)} " )
report_df.head()
text_unit_df = pd.read_parquet(f"{INPUT_DIR} /{TEXT_UNIT_TABLE} .parquet" )
text_units = read_indexer_text_units(text_unit_df)
print (f"文本单元记录:{len (text_unit_df)} " )
text_unit_df.head()
8. 构建本地搜索引擎 一切准备就绪,现在已经有了本地搜索引擎所需的所有数据。接下来,我们可以利用这些数据,配合一个大型语言模型(LLM)和一个嵌入模型,来构建一个 LocalSearch 实例,为搜索任务提供强大的支持。
api_key = os.environ["OPENAI_API_KEY" ]
llm_model = "gpt-4o"
embedding_model = "text-embedding-3-small"
llm = ChatOpenAI(
api_key=api_key,
model=llm_model,
api_type=OpenaiApiType.OpenAI,
max_retries=20 ,
)
token_encoder = tiktoken.get_encoding("cl100k_base" )
text_embedder = OpenAIEmbedding(
api_key=api_key,
api_base=None ,
api_type=OpenaiApiType.OpenAI,
model=embedding_model,
deployment_name=embedding_model,
max_retries=20 ,
)
context_builder = LocalSearchMixedContext(
community_reports=reports,
text_units=text_units,
entities=entities,
relationships=relationships,
covariates=None ,
entity_text_embeddings=description_embedding_store,
embedding_vectorstore_key=EntityVectorStoreKey.ID,
text_embedder=text_embedder,
token_encoder=token_encoder,
)
local_context_params = {
"text_unit_prop" : 0.5 ,
"community_prop" : 0.1 ,
"conversation_history_max_turns" : 5 ,
"conversation_history_user_turns_only" : True ,
"top_k_mapped_entities" : 10 ,
"top_k_relationships" : 10 ,
"include_entity_rank" : True ,
"include_relationship_weight" : True ,
"include_community_rank" : False ,
"return_candidate_context" : False ,
"embedding_vectorstore_key" : EntityVectorStoreKey.ID,
"max_tokens" : 12_000 ,
}
llm_params = {
"max_tokens" : 2_000 ,
"temperature" : 0.0 ,
}
search_engine = LocalSearch(
llm=llm,
context_builder=context_builder,
token_encoder=token_encoder,
llm_params=llm_params,
context_builder_params=local_context_params,
response_type="multiple paragraphs" ,
)
9. 进行查询 result = await search_engine.asearch("Tell me about Leonardo Da Vinci" )
print (result.response)
莱昂纳多·达·芬奇,1452 年出生于佛罗伦萨附近的文奇镇,被广泛誉为意大利文艺复兴时期最多才多艺的天才之一。他的全名是莱昂纳多·迪·塞尔·皮耶罗·达·安东尼奥·迪·塞尔·皮耶罗·迪·塞尔·圭多·达·芬奇,他是塞尔·皮耶罗的非婚生和长子,塞尔·皮耶罗是一位乡村公证人。莱昂纳多的贡献涵盖了艺术、科学、工程和哲学等多个领域,他被誉为基督教时代最万能的天才。
早期生活和训练 莱昂纳多早期的才华得到了他父亲的认可,他父亲将他的一些画作带给了安德烈亚·德尔·维罗基奥,一位著名的艺术家和雕塑家。维罗基奥对莱昂纳多的才华印象深刻,于 1469-1470 年左右接受了他进入自己的工作室。在这里,莱昂纳多遇到了包括博蒂切利和洛伦佐·迪·克雷迪在内的其他著名艺术家。到 1472 年,莱昂纳多被佛罗伦萨画家行会录取,标志着他职业生涯的开始。
艺术杰作 莱昂纳多或许以他的标志性画作最为人所知,如《蒙娜丽莎》和《最后的晚餐》。《蒙娜丽莎》以其微妙的表情和详细的背景而闻名,现藏于卢浮宫,仍然是世界上最著名的艺术品之一。《最后的晚餐》是一幅壁画,描绘了耶稣宣布他的一个门徒将背叛他的那一刻,位于米兰圣玛利亚·格拉齐教堂的餐厅。其他重要作品包括《岩间圣母》和他大约在 1489-1490 年开始的《绘画论》。
科学和工程贡献 莱昂纳多的天才超越了艺术,延伸到各种科学和工程事业。他在解剖学、光学和水力学方面做出了重要观察,他的笔记本里充满了预示许多现代发明的草图和想法。例如,他预示了哥白尼关于地球运动的理论和拉马克对动物的分类。他对光影法则和明暗对比的掌握对艺术和科学都产生了深远影响。
赞助和职业关系 莱昂纳多的职业生涯受到他的赞助人的重大影响。米兰公爵卢多维科·斯福尔扎雇佣莱昂纳多作为宫廷画家和普通工匠,委托了各种作品,甚至在 1499 年赠送给他一个葡萄园。在他的晚年,莱昂纳多在法国国王弗朗西斯一世的赞助下搬到了法国,国王为他提供了丰厚的收入,并对他评价很高。莱昂纳多在法国安博瓦兹附近的克洛克斯庄园度过了他最后几年,国王经常拜访他,他得到了他的密友和助手弗朗切斯科·梅尔齐的支持。
遗产和影响 莱昂纳多·达·芬奇的影响远远超出了他的一生。他在米兰创立了一所绘画学校,他的技术和教导被他的学生和追随者,如乔瓦尼·安布罗焦·达·普雷迪斯和弗朗切斯科·梅尔齐传承下去。他的作品继续受到庆祝和研究,巩固了他作为文艺复兴时期最伟大的大师之一的遗产。莱昂纳多将艺术和科学融合的能力在两个领域都留下了不可磨灭的印记,激励着无数的艺术家和科学家。
总之,莱昂纳多·达·芬奇对艺术、科学和工程的无与伦比的贡献,加上他的创新思维和对同时代人及后代的深远影响,使他成为人类成就史上的一位杰出人物。他的遗产继续激发着钦佩和研究,强调了他的天才的永恒相关性。
GraphRAG 的结果具体,明确标出了引用的数据源。
10. 问题生成 GraphRAG 还可以根据历史查询生成问题,这对于在聊天机器人对话中创建推荐问题非常有用。这种方法结合了知识图谱的结构化数据和输入文档的非结构化数据,产生与特定实体相关的候选问题。
question_generator = LocalQuestionGen(
llm=llm,
context_builder=context_builder,
token_encoder=token_encoder,
llm_params=llm_params,
context_builder_params=local_context_params,
)
question_history = [
"Tell me about Leonardo Da Vinci" ,
"Leonardo's early works" ,
]
candidate_questions = await question_generator.agenerate(
question_history=question_history, context_data=None , question_count=5
)
candidate_questions.response
["- 莱昂纳多·达·芬奇的早期作品有哪些,它们存放在哪里?",
"莱昂纳多·达·芬奇与安德烈亚·德尔·维罗基奥的关系如何影响了他的早期作品?",
"莱昂纳多·达·芬奇在米兰期间承担了哪些重要项目?",
"莱昂纳多·达·芬奇的工程技能如何促成他的项目?",
"莱昂纳多·达·芬奇与法国弗朗西斯一世的关系有何重要性?"]
11. 结语 本文带领大家深入了解了 GraphRAG 技术,这是一种融合知识图谱来强化 RAG 应用的创新手段。GraphRAG 特别擅长处理那些需要跨信息片段进行多步骤推理和全面回答问题的复杂任务。
结合 Milvus 向量数据库后,GraphRAG 能够高效地在庞大的数据集中探索复杂的语义联系,从而得出更精准、更深刻的分析结果。这种强强联合的解决方案,使 GraphRAG 成为众多实际通用人工智能(GenAI)应用中的得力助手,为理解和处理复杂信息提供了强有力的支持。
12. 性能优化与部署建议 在实际生产环境中部署 GraphRAG 结合 Milvus 时,还需考虑以下优化策略以提升系统性能和稳定性:
Milvus 连接管理 :在生产环境中建议使用持久化连接池,避免频繁建立 TCP 连接带来的开销。可以通过配置 Milvus 客户端的 pool_size 参数来优化并发处理能力。
索引类型选择 :Milvus 支持多种向量索引类型(如 IVF_FLAT, HNSW, SCANN)。对于大规模实体描述向量,推荐使用 HNSW 索引以获得更高的查询精度和速度平衡。
分片与副本 :当数据量增长时,应合理配置 Milvus 的分片数(num_shards)和副本数(replica_number),以实现水平扩展和高可用性。
缓存机制 :对于高频查询的实体或社区报告,可以在应用层引入 Redis 等缓存机制,减少数据库读取压力。
异步处理 :GraphRAG 的索引和查询过程均支持异步操作(async/await),建议在 Web 服务中使用异步框架(如 FastAPI)来充分利用 I/O 等待时间,提高吞吐量。
通过上述优化措施,可以显著提升 GraphRAG 系统在真实业务场景下的响应速度和资源利用率。
相关免费在线工具 加密/解密文本 使用加密算法(如AES、TripleDES、Rabbit或RC4)加密和解密文本明文。 在线工具,加密/解密文本在线工具,online
RSA密钥对生成器 生成新的随机RSA私钥和公钥pem证书。 在线工具,RSA密钥对生成器在线工具,online
Mermaid 预览与可视化编辑 基于 Mermaid.js 实时预览流程图、时序图等图表,支持源码编辑与即时渲染。 在线工具,Mermaid 预览与可视化编辑在线工具,online
curl 转代码 解析常见 curl 参数并生成 fetch、axios、PHP curl 或 Python requests 示例代码。 在线工具,curl 转代码在线工具,online
Base64 字符串编码/解码 将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online
Base64 文件转换器 将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online