
使用GraphRAG读小说《凡人修仙传》
文档简要介绍了如何利用GraphRAG,实现对长篇小说等大规模文本的高效理解与问答。内容涵盖GraphRAG的基本原理、核心优势及其在实际应用中的操作流程,帮助初学者快速上手并掌握其用法。
1. 什么是知识图谱(knowledge graph )
2. 传统RAG与GraphRAG差异
3. GraphRAG知识模型的核心定义
4. GraphRAG工作的核心阶段
5. 安装GraphRAG
6. 使用方法
6.1 CLI 命令行
6.2 Python API
7. 初始化
8. 修改配置
9. 准备数据
10. 创建索引
10.1 索引阶段的Token使用情况
11. 查询
利用检索增强生成(RAG)从外部知识源中获取相关信息,使大型语言模型(LLMs)能够回答涉及私有文档或未见过文档集合的问题。
基础 RAG 系统仅依靠向量数据库中的语义搜索来检索和排序独立的文本片段。
尽管这种方法能提供一些相关信息,但它无法捕捉这些片段之间的上下文联系,基础 RAG 系统难以回答复杂的多跳问题。
或者当问题指向整个语料库时,RAG 往往也会失效,比如问题:“这个数据集的主题是什么?”,这类问题本质上属于面向查询的摘要任务,而不是单纯的检索任务。
GraphRAG,是利用知识图谱(knowledge graph )来表示和连接信息,不仅捕获更多数据点,还捕获它们之间的关系。因此,基于图的检索器能够通过揭示那些不明显但至关重要的关联信息,提供更准确和相关的结果。
他既能适应用户问题的泛化程度,也能应对庞大的文本规模。
GraphRAG利用 LLM 分两个阶段构建图索引:先从源文档中抽取实体知识图,再为每一组紧密相关的实体预生成群体摘要。面对问题时,系统会先根据这些群体摘要生成局部回答,再将所有局部回答整合成最终答复。
对于百万级别 token 数据集上的全局理解类问题, GraphRAG 相比传统 RAG 在答案的全面性和多样性上都有显著提升。
1. 什么是知识图谱(knowledge graph )
知识图谱(knowledge graph )是一种结构化的方式来表示现实世界中的人、事、物及其相互关系。
知识图谱里的实体可以代表各种事物,比如具体对象、发生的事件、特定情境或抽象概念。实体之间的联系则体现了它们相互关联的背景和含义。
之前传统 RAG 方式实际上效果不佳,各个信息比较碎片化,所以我们希望将这些概念之间的复杂关系展现出来。在查询时,不再是大海捞针去找「可能相关」的信息碎片,而是根据图谱中已经掌握的关联,提取一整串相连的信息,让大语言模型来一并处理。
GraphRAG结合了知识图谱结构和 RAG 方法,解决传统 RAG 方法的局限性。
2. 传统RAG与GraphRAG差异
维度 | 传统RAG | GraphRAG |
数据表示 | 向量数据库中的孤立文本块 | 结构化的知识图谱(实体、关系、社区) |
检索机制 | 基于向量相似性的语义搜索 | 图谱遍历、社区摘要和混合检索 |
擅长问题类型 | 简单的事实性检索 | 复杂的、跨文档的、需要全局理解和推断的问题 |
上下文理解 | 仅基于局部、碎片化的文本 | 能够理解实体间的关系和全局主题 |
可解释性 | 检索结果是一系列文本片段,可解释性较低 | 能够展示答案来源和推理路径,可解释性高 |
3. GraphRAG知识模型的核心定义
GraphRAG所构建的知识模型由多个相互关联的核心定义构成,它们共同描绘了数据的深层结构 :
- 实体(Entities):这是知识图谱中的基本节点,代表着文本中被识别出的关键对象,例如人、地点、组织、事件或概念。LLM会从原始文本中提取这些实体,并赋予其一个标题、类型和描述 。
- 关系(Relationships):这是连接实体之间的边,描述了实体之间的联系。它将实体连接起来。关系也包含一个描述,详细说明了连接的性质 。
- 文本单位(TextUnits):它是原始输入文档被切分后得到的逻辑文本块。这些文本单位是图谱(如实体和关系)的来源,并在查询阶段作为证据来源被引用 。
- 社区(Communities):这是GraphRAG实现高层次理解的关键。在图谱构建完成后,系统会利用聚类算法(如Hierarchical Leiden算法)识别出由紧密相连的实体组成的群组,这些群组被称为社区 。这种层次化的社区结构能够帮助系统从不同的粒度审视数据。
- 社区报告(Community Reports):这是LLM为每个社区生成的摘要。这些报告提供了对社区内关键实体、关系和主旨的高层次概述,并在全局搜索等查询模式中发挥核心作用 。
4. GraphRAG工作的核心阶段
GraphRAG的整个工作流程可以清晰地分为两大核心阶段:
索引阶段(Indexing Phase): 在这个阶段,系统将原始的非结构化文本数据作为输入,通过一系列复杂的LLM调用和数据转换步骤,自动构建出结构化的知识图谱及其相关的知识模型基本单元(如社区和摘要)。这个过程是一个自下而上的知识提炼过程 。
查询阶段(Querying Phase): 在知识图谱构建完成后,系统进入查询阶段。此时,用户可以提出自然语言问题,查询引擎会利用已构建的知识图谱来为LLM提供更丰富、更具关联性的上下文信息,从而生成准确、有洞察力的答案 。
GraphRAG所构建的知识图谱不仅仅是一个简单的信息存储库。它本质上是一个更丰富的、具有内在语义和拓扑结构的“记忆结构”。与传统RAG将信息视作孤立的、无关联的文本片段不同,GraphRAG通过LLM自动识别并编织这些片段之间的关系网络。这意味着在查询时,LLM不再是简单地阅读孤立的文本片段,而是可以基于这个结构化的“大脑”进行复杂的“推理”和“联想”,沿着图谱中的关系路径进行知识遍历,从而提供更具深度和广度的答案。
5. 安装GraphRAG
为了调试方便,我直接拉源码下来
git clone https://github.com/microsoft/graphrag.git
当前的版本是v2.5.0
新建虚拟环境(这里使用uv作为包管理工具),使用Python3.11.9
uv venv -p 3.11.9
切换到新建的虚拟环境:
.venv\Scripts\activate
安装依赖:
uv sync
6. 使用方法
GraphRAG可以选择通过 CLI 或 Python API 来运行
6.1 CLI 命令行
这是我们体验的主要操作方式,如:graphrag index --root ./ragtest
6.2 Python API
查看索引 API 的 Python 文件(https://github.com/microsoft/graphrag/blob/main/graphrag/api/index.py),了解从 Python 代码中直接调用的推荐方法。
7. 初始化
运行 graphrag init
命令
graphrag init --root ./ragtest
这将在 ./ragtest
目录中创建两个文件: .env
和 settings.yaml
和一个目录 prompts
。
看一下结构:
> tree ragtest /f
G:\WORKSPACE\IDEA\PY\GITHUB\GRAPHRAG\RAGTEST
│ .env
│ settings.yaml
│
└─prompts
basic_search_system_prompt.txt
community_report_graph.txt
community_report_text.txt
drift_reduce_prompt.txt
drift_search_system_prompt.txt
extract_claims.txt
extract_graph.txt
global_search_knowledge_system_prompt.txt
global_search_map_system_prompt.txt
global_search_reduce_system_prompt.txt
local_search_system_prompt.txt
question_gen_system_prompt.txt
summarize_descriptions.txt
8. 修改配置
.env
.env
包含运行 GraphRAG 流程所需的环境变量。其中定义了一个环境变量, GRAPHRAG_API_KEY=<API_KEY>
。将 <API_KEY>
替换为您个人的模型密钥。(我这里用的阿里百炼)
settings.yaml
settings.yaml
通过修改此文件来更改所有的配置。
GraphRAG主要用到两个模型:一个语言模型,一个嵌入模型,以下是我使用的配置
### This config file contains required core defaults that must be set, along with a handful of common optional settings.
### For a full list of available settings, see https://microsoft.github.io/graphrag/config/yaml/
### LLM settings ###
## There are a number of settings to tune the threading and token limits for LLM calls - check the docs.
models:
default_chat_model: # 定义语言模型
type:openai_chat# or azure_openai_chat
api_base:https://dashscope.aliyuncs.com/compatible-mode/v1 # 改为你的模型调用地址
# api_version: 2024-05-01-preview
auth_type:api_key# or azure_managed_identity
api_key:${GRAPHRAG_API_KEY}# set this in the generated .env file # 从.env读取key
# audience: "https://cognitiveservices.azure.com/.default"
# organization: <organization_id>
model:qwen-flash # 你使用的语言模型
# deployment_name: <azure_model_deployment_name>
encoding_model:cl100k_base# automatically set by tiktoken if left undefined # 确定分词编码方式
model_supports_json:true# recommended if this is available for your model.
concurrent_requests:25# max number of simultaneous LLM requests allowed
async_mode:threaded# or asyncio
retry_strategy:native
max_retries:10
tokens_per_minute:1000000 # set to null to disable rate limiting # 确定TPM,可选
requests_per_minute:1000 # set to null to disable rate limiting # 确定RPM,可选
default_embedding_model:
type:openai_embedding# or azure_openai_embedding # 嵌入模型
api_base:https://dashscope.aliyuncs.com/compatible-mode/v1 # 改为你的模型调用地址
# api_version: 2024-05-01-preview
auth_type:api_key# or azure_managed_identity
api_key:${GRAPHRAG_API_KEY} # 从.env读取key
# audience: "https://cognitiveservices.azure.com/.default"
# organization: <organization_id>
model:text-embedding-v4 # 你使用的嵌入模型
# deployment_name: <azure_model_deployment_name>
encoding_model:cl100k_base# automatically set by tiktoken if left undefined # 确定分词编码方式
model_supports_json:true# recommended if this is available for your model.
concurrent_requests:25# max number of simultaneous LLM requests allowed
async_mode:threaded# or asyncio
retry_strategy:native
max_retries:10
tokens_per_minute:1000000 # set to null to disable rate limiting or auto for dynamic 确定TPM,可选
requests_per_minute:1500 # set to null to disable rate limiting or auto for dynamic 确定RPM,可选
### Input settings ###
input:
storage:
type:file# or blob
base_dir:"input"
file_type:text# [csv, text, json]
chunks:
size:1200
overlap:100
group_by_columns:[id]
### Output/storage settings ###
## If blob storage is specified in the following four sections,
## connection_string and container_name must be provided
output:
type:file# [file, blob, cosmosdb]
base_dir:"output"
cache:
type:file# [file, blob, cosmosdb]
base_dir:"cache"
reporting:
type:file# [file, blob]
base_dir:"logs"
vector_store:
default_vector_store:
type:lancedb
db_uri:output\lancedb
container_name:default
overwrite:True
### Workflow settings ###
embed_text:
model_id:default_embedding_model
vector_store_id:default_vector_store
batch_size:10 # 嵌入的批量大小,要符合API限制
batch_max_tokens:8191
extract_graph:
model_id:default_chat_model
prompt:"prompts/extract_graph.txt"
entity_types:[organization,person,geo,event]
max_gleanings:1
summarize_descriptions:
model_id:default_chat_model
prompt:"prompts/summarize_descriptions.txt"
max_length:500
extract_graph_nlp:
text_analyzer:
extractor_type:regex_english# [regex_english, syntactic_parser, cfg]
cluster_graph:
max_cluster_size:10
extract_claims:
enabled:false
model_id:default_chat_model
prompt:"prompts/extract_claims.txt"
description:"Any claims or facts that could be relevant to information discovery."
max_gleanings:1
community_reports:
model_id:default_chat_model
graph_prompt:"prompts/community_report_graph.txt"
text_prompt:"prompts/community_report_text.txt"
max_length:2000
max_input_length:8000
embed_graph:
enabled:true# if true, will generate node2vec embeddings for nodes
umap:
enabled:true# if true, will generate UMAP embeddings for nodes (embed_graph must also be enabled)
snapshots:
graphml:true
embeddings:false
### Query settings ###
## The prompt locations are required here, but each search method has a number of optional knobs that can be tuned.
## See the config docs: https://microsoft.github.io/graphrag/config/yaml/#query
local_search:
chat_model_id:default_chat_model
embedding_model_id:default_embedding_model
prompt:"prompts/local_search_system_prompt.txt"
global_search:
chat_model_id:default_chat_model
map_prompt:"prompts/global_search_map_system_prompt.txt"
reduce_prompt:"prompts/global_search_reduce_system_prompt.txt"
knowledge_prompt:"prompts/global_search_knowledge_system_prompt.txt"
drift_search:
chat_model_id:default_chat_model
embedding_model_id:default_embedding_model
prompt:"prompts/drift_search_system_prompt.txt"
reduce_prompt:"prompts/drift_search_reduce_prompt.txt"
basic_search:
chat_model_id:default_chat_model
embedding_model_id:default_embedding_model
prompt:"prompts/basic_search_system_prompt.txt"
prompts
prompts
目录中生成了将要使用到的所有提示词。
我们需要把extract_claims.txt
和extract_graph.txt
中的“语言”修改为中文,避免生成英文内容。
extract_claims.txt
3. Return output in 中文 as a single list of all the claims identified in steps 1 and 2. Use **{record_delimiter}** as the list delimiter.
extract_graph.txt
3. Return output in 中文 as a single list of all the entities and relationships identified in steps 1 and 2. Use **{record_delimiter}** as the list delimiter.
9. 准备数据
新建数据目录:
mkdir -p ./ragtest/input
我准备了《凡人修仙传》前125章,一共25万字左右,放到input目录中
10. 创建索引
在命令行执行:
graphrag index --root ./ragtest
这个时间会比较长,我这里用了20分钟
GraphRAG的索引是其强大能力的来源,但同时也带来了显著的问题。
该过程被人们描述为“是一个昂贵的操作”,因为它涉及多次LLM调用,真的是“又贵又慢”。
这种高成本是其设计本身的直接结果。
系统不满足于简单的文本嵌入,而是通过LLM进行多轮次的文本解析、实体关系提取和社区摘要生成,每一步都是一次潜在的、昂贵的API调用。
因此,GraphRAG的“高成本”是其在复杂问题上实现“高精度”和提供“高洞察力”的直接代价。
对于希望在实践中部署GraphRAG的人而言,这是必须要考虑的一个问题.
10.1 索引阶段的Token使用情况
测试小说全文25万字,
语言模型 qwen-flash 的使用情况:
调用总次数1690次,输入Tokens总量3,890.1千Tokens,输出Tokens总量1,319.6千Tokens
嵌入模型 text-embedding-v4 的使用情况:
调用总次数301次,全部Tokens总量780.5千Tokens
11. 查询
这里的查询方式有两种:
局部查询:
通过将 AI 提取到知识图谱中的相关数据与原始文档的文本块相结合来生成答案,此方法适用于需要了解文档中提到的特定实体的问题。
全局查询:
全局查询方法通过以 map-reduce 方式搜索所有 AI 生成的社区报告来生成答案。这是一种资源密集型方法,需要LLM支持的context window足够大,但通常可以很好地回答需要了解整个数据集的问题。
那么我们现在就来用《凡人修仙传》问一些问题吧。
使用全局搜索来提出一个概括性问题的例子:
graphrag query --root ./ragtest --method global --query "韩立和墨大夫是什么关系?"
(graphrag) PS G:\workspace\idea\py\github\graphrag> graphrag query --root ./ragtest --method global --query "韩立和墨大夫是什么关系?"
韩立与墨大夫的关系极为复杂,呈现出多重身份交织的深层张力,既包含师徒传承、权力交接与医术继承的正面纽带,又暗藏敌对、控制与夺舍的致命冲突。这一关系并非单一维度,而是贯穿于修仙世界中的权谋、生存与自我觉醒的核心叙事主线。
从传承与身份的角度看,墨大夫是韩立的亲传师尊。他主持了韩立的入门考核,将其纳入七玄门正式弟子序列,传授《长春功》第一层法决与“引魂钟”这一关键法器,标志着韩立正式进入修真体系 [Data: Reports (19, 82, 158, 119, 167, 260, +more)]。墨大夫还通过“纹龙戒”这一信物认证韩立的身份,其与严氏戒指的契合进一步确认了韩立作为真传弟子的合法性,使他得以进入墨府、参与核心事务 [Data: Reports (80, 146, 157, 158, 146)]。此外,韩立在墨府期间承担了誊录《 医道心得》的重任,这不仅是对医术传承的履行,也标志着其从外部观察者向内部体系参与者的身份转变 [Data: Reports (216, 19, 146)]。墨大夫去世后,韩立 接任其职位,成为七玄门新的首席医师,完整继承了其医术权威与药园资源,并利用神秘瓶子大规模催生药材,展现出超越前任的掌控能力,完成了从弟子到领袖的权力交接 [Data: Reports (57, 65, 66, 3, 112, 185, 30, 36)]。
然而,这一表面的师徒关系之下,隐藏着深刻的敌对与控制本质。墨大夫对韩立的“教导”实为精心设计的夺舍计划。他通过传授《长春功》这一功法,实则植入了夺舍机制,意图借韩立的肉身实现自身元神的转移与长生 [Data: Reports (207, 115, 59, 60, 153)]。墨大夫更以“尸虫丸”与“缠香丝”等毒药进行双重控制,使韩立长期处于生死依赖状态——需服药以避免骨骼异变与瘫痪,从而确保其绝对服从 [Data: Reports (153, 82, 0, 55, 69, 257, 140, 1278, 597, 681, 625)]。此外,墨大夫通过“定”字咒语与黄纸符咒构建的法术体系,实施意识入侵与灵魂操控,试图在梦境中夺取韩立的神识 [Data: Reports (151, 28, 152, 45, 156)]。在“套
中套”情节中,墨大夫更主导了“七鬼噬魂大法”的夺舍仪式,其行为具有高度预谋性与攻击性,直接威胁韩立的自我意识完整性 [Data: Reports (31, 21, 45, 22, 152, 2452, +more)]。
使用局部搜索来询问关于某个特定角色的更具体问题的例子:
graphrag query --root ./ragtest --method local --query "墨大夫是个什么样的人"
(graphrag) PS G:\workspace\idea\py\github\graphrag> graphrag query --root ./ragtest --method local --query "墨大夫是个什么样的人"
墨大夫是一位复杂而深藏不露的角色,其形象贯穿于整个故事的多个层面,既是医术高超的象征,也是阴谋与权谋交织的核心人物。他是七玄门前任首席医师,被称
为“神医墨大夫”,以医术高明著称,能够救治内外伤及疑难杂症,甚至在药效上超越了前任医师,尽管其医术受限于药材资源 [Data: Entities (819); Sources (1
50)]. 他不仅医术精湛,还极富谋略与远见,其居所内藏有暗格,存放着虚假身份文件、亲笔证明、名单以及控制手段,显现出其在生前便已布局长远,为后人留下重重谜题 [Data: Entities (793); Relationships (1365)]。
墨大夫的个性极具矛盾性:他既是韩立的上司与前任,又是被韩立称为“墨老鬼”的令人畏惧的存在,反映出韩立对其既敬且畏的心理 [Data: Entities (482, 819);
Relationships (822)]. 他行事沉稳,善于隐藏真实意图,例如在与韩立对峙时,其“虚实掌法”看似凶猛却只用半成功力,实则是一种试探与保护 [Data: Entitie
s (620); Relationships (1070)]. 他在关键时刻出声阻止铁奴的攻击,展现出对局势的绝对掌控力,其声音具有压倒性的权威,足以让铁奴立即停手 [Data: Entities (655); Relationships (1140)]。
此外,墨大夫的死亡并非表面所见的简单事件。他的尸体被发现于屋内,其身上藏有香囊,且在临终前留下遗书,暗示其早已预料到未来变局,并为韩立安排了复杂
的任务与交易 [Data: Entities (776, 1348, 1350); Relationships (1340)]. 他甚至在临死前察觉到自身伤口异样,意识到中了“缠香丝”毒药,心理防线开始动 摇,这表明他并非毫无防备,而是身陷险境 [Data: Entities (650, 631, 632); Relationships (1124, 1098, 1099)]。
更耐人寻味的是,墨大夫身后还有一位神秘人,始终紧随其后,寸步不离,身份不明,可能为护卫或随从,暗示其生前并不孤单,背后或有更深的势力支撑 [Data:
Entities (461); Relationships (782)]. 他最终的故乡位于岚州,是韩立必须前往的地理节点,也是整个事件推动的关键所在 [Data: Entities (1159, 2188); Relationships (2228)]。
综上所述,墨大夫不仅是一位医术超群的医生,更是一位布局深远、心思缜密的智者,其生前行为与死后遗局深刻影响了韩立的命运,其形象融合了医者仁心、权谋深沉与神秘莫测的特质 [Data: Entities (1171, 819, 793, 1159); Relationships (1365, 1340, 1124, 1099, 2188, +more)].
GraphRAG代表了RAG技术发展的一个重要方向,它通过将非结构化文本转化为结构化知识图谱,实现了从简单的“信息检索”到基于“知识结构”的“智能推理”的范式转变。
问题是GraphRAG目前在成本、资源消耗和数据增量更新方面存在挑战。
不过他有效地解决了传统RAG在处理复杂、跨文档和全局性问题时所面临的核心局限。它提供了一种强大方式,使得LLM能够基于可验证的知识基础生成答案,从而显著减少了幻觉(hallucination)的风险。
对于希望为企业构建高精度、高可解释性生成式AI应用的开发者和研究人员来说,GraphRAG是一个值得深入探索和关注的前沿技术。
本文转载自AI取经路,作者:AI取经路
