day38大模型程序开发-GraphRAG实操
三、GraphRAG快速部署与调用方法详解
1.GraphRAG安装
注,以下实验环境均为Ubuntu系统,以更好的模拟真实企业应用场景,其中大多数方法也可以直接迁移至Windows操作系统中。下面我们的操作以AutoDL平台上进行!
Step 1.使用pip安装graphrag
pip install graphrag=0.5
Step 2.创建检索项目文件夹
需要在任何合适的目录下创建一个任意名字的文件夹,该文件夹内部需要再创建一个名字固定为input的子文件夹,用于保存上传的数据集文件。mkdir -p ./openl/input
Step 3.上传数据集
将数据集文件上传至input目录下。只可以上传txt格式文件。
Step 4.初始化项目文件
初始化项目文件后,会生成GraphRAG项目需要的相关配置文件。
graphrag init --root ./openl
Step 5.修改项目配置
注意:graphrag 本身只支持openai系列模型使用。而deepseek-v3和Qwen系列(如Qwen3-235B-A22B、Qwen3-4B)正好采用了和GPT模型完全一样的调用方法和输出格式。因此deepseek-v3和Qwen系列也是目前除GPT外可以无缝接入GraphRAG的大模型。
-
打开.env文件(ls -all),填写大模型的 API-KEY。可以使用DeepSeek-V3模型何其对应的api_key

-
打开setting.yaml文件,填写模型名称和base url:
- DeepSeek-V3模型和向量模型,此处的向量模型使用的硅基流动中的Qwen/Qwen3-Embedding-8B为例。
2.Indexing检索流程实现
一切准备就绪后,即可开始执行GraphRAG索引过程。可以借助GraphRAG脚本自动执行indexing。
graphrag index --root ./openl
运行结束后,各知识图谱相关数据集都保存在output文件夹中:
3.全局搜索问答实现
GraphRAG v 0.5版本bug修复
GraphRAG最新版 v0.5版本当使用OpenAI的Embedding模型时,会出现运行报错,此时需要修改lancedb.py文件才可正常运行。在Ubuntu服务器下,脚本文件地址如下:
autodl修改GraphRAG源码,源码路径:
/root/miniconda3/lib/python3.12/site-packages/graphrag/vector_stores/lancedb.py
打开脚本文件后需修改如下内容:
当使用Qwen/Qwen3-Embedding-8B向量模型时,需手动设置N = 4096。

构建GlobalSearch(全局搜索)搜索引擎并进行问答
模块导入:
import tiktoken
import pandas as pd
from graphrag.query.indexer_adapters import (
read_indexer_communities,
read_indexer_entities,
read_indexer_reports,
)
from graphrag.query.llm.oai.chat_openai import ChatOpenAI
from graphrag.query.llm.oai.typing import OpenaiApiType
from graphrag.query.structured_search.global_search.community_context import (
GlobalCommunityContext,
)
from graphrag.query.structured_search.global_search.search import GlobalSearch
数据加载与预处理:
INPUT_DIR = "./demo/output"
LANCEDB_URI = f"{INPUT_DIR}/lancedb"
COMMUNITY_LEVEL = 2 #社区层级
COMMUNITY_TABLE = "create_final_communities"
COMMUNITY_REPORT_TABLE = "create_final_community_reports"
ENTITY_TABLE = "create_final_nodes"
ENTITY_EMBEDDING_TABLE = "create_final_entities"
#相关指定数据加载,包含:实体表、社区表等
community_df = pd.read_parquet(f"{INPUT_DIR}/{COMMUNITY_TABLE}.parquet")
entity_df = pd.read_parquet(f"{INPUT_DIR}/{ENTITY_TABLE}.parquet")
report_df = pd.read_parquet(f"{INPUT_DIR}/{COMMUNITY_REPORT_TABLE}.parquet")
entity_embedding_df = pd.read_parquet(f"{INPUT_DIR}/{ENTITY_EMBEDDING_TABLE}.parquet")
#数据预处理:根据上面加载好的数据表格重构实体和社区报告等对象集合
#read_indexer_communities函数表示为特征解析器
communities = read_indexer_communities(community_df, entity_df, report_df)
reports = read_indexer_reports(report_df, entity_df, COMMUNITY_LEVEL)
entities = read_indexer_entities(entity_df, entity_embedding_df, COMMUNITY_LEVEL)
创建Global Search模式的上下文构建器 (context_builder):
token_encoder = tiktoken.get_encoding("cl100k_base")
context_builder = GlobalCommunityContext(
community_reports=reports,
communities=communities,
entities=entities,
token_encoder=token_encoder,
)
代码解释如下:
GlobalCommunityContext:- 这是一个为 Global Search 模式创建的上下文构建器。它负责从不同的数据源(如社区报告、实体、社区信息等)中提取相关数据,并构建一个用于查询的大范围上下文。
- 这个构建器帮助为整个文档集合(而不是某个特定实体)构建一个全局的知识背景,以便模型能够对广泛的问题做出响应。
community_reports=reports:- 这里传入的是之前从文件中读取的
reports数据(也就是COMMUNITY_REPORT_TABLE表格的数据)。 - 这些报告包含了关于不同社区的详细信息,如主题、摘要、影响力、发现等。
- 在全局搜索中,这些社区报告有助于构建整个数据集的高层次概览。
- 这里传入的是之前从文件中读取的
communities=communities:communities数据包含社区的结构和信息。在图谱中,社区可能代表了不同的主题、领域或相关性较高的实体群体。- 这部分数据通常用于在全局搜索时进行分组和排名,比如通过社群的重要性或与查询的相关性来排序。
entities=entities:entities是从之前的索引步骤中提取的实体数据(来自ENTITY_TABLE表)。- 这些实体(如人物、地点、事件等)可以用来扩展全局搜索的范围。它们提供了具体的名词、对象和概念,有助于为全局查询提供上下文。
- 注意: 如果不希望在全局搜索中使用社区权重来影响排名(例如,不考虑某个社区对某个实体的影响),可以将
entities参数设为None。
token_encoder=token_encoder:token_encoder是用于对文本进行编码的工具。它将文本转化为可以输入到模型中的 token 序列,以便在模型中使用。
配置全局搜索参数(结合下面的“构建全局搜索引擎”使用):
context_builder_params = {
"use_community_summary": False,
"shuffle_data": True,
"include_community_rank": True,
"min_community_rank": 0,
"community_rank_name": "rank",
"include_community_weight": True,
"community_weight_name": "occurrence weight",
"normalize_community_weight": True,
"max_tokens": 12_000,
"context_name": "Reports",
}
map_llm_params = {
"max_tokens": 1000,
"temperature": 0.0,
"response_format": {"type": "json_object"},
}
reduce_llm_params = {
"max_tokens": 2000,
"temperature": 0.0,
}
构建全局搜索引擎:
llm = ChatOpenAI(
api_key="sk-4b79f3a3ff334a15a1935366ebb425b3",
model="deepseek-chat",
api_base="https://api.deepseek.com",
api_type=OpenaiApiType.OpenAI,
max_retries=20,
)
search_engine = GlobalSearch(
llm=llm,
context_builder=context_builder,
token_encoder=token_encoder,
max_data_tokens=12_000,
map_llm_params=map_llm_params,
reduce_llm_params=reduce_llm_params,
allow_general_knowledge=False,
json_mode=True,
context_builder_params=context_builder_params,
concurrent_coroutines=32,
response_type="multiple paragraphs",
)
这里创建了一个 全局搜索(Global Search) 引擎,利用前面配置的上下文构建器 (context_builder)、语言模型 (llm)、以及其它相关的参数进行搜索。对应参数详解如下:
llm:- 这个参数传入的是已经配置好的语言模型(LLM)。全局搜索引擎将使用这个模型来生成回答。
context_builder:- 上下文构建器,它负责根据社区报告、社区等信息来构建查询的上下文。
token_encoder:token_encoder是用来处理文本的编码工具,通常是一个模型专用的 tokenizer。在这里,我们使用了tiktoken.get_encoding("cl100k_base"),它是 OpenAI 模型的一个编码器,用来将文本转化为 token 格式。
max_data_tokens:- 最大数据 token 数量。它控制了模型可以处理的最大上下文大小。你可以根据实际使用的模型的最大 token 限制来设置(例如,如果模型最大 token 限制是 8K,则可以设置为 5000,留一些余量)。这个设置控制了全局搜索时在上下文窗口内所使用的最大 token 数。
map_llm_params和reduce_llm_params:- 这些参数会影响 LLM 在不同阶段生成内容的方式(例如,
max_tokens和temperature等)。 - 在GraphRAG框架中,
map_llm_params(映射阶段)和reduce_llm_params(归约阶段)代表了模型处理任务时的双向策略。在复杂图谱中,关键证据往往隐藏在非直觉性的间接连接里。只有充分展开搜索树才能确保不遗漏重要分支。通过这种分治策略,GraphRAG既能像蜘蛛网一样广泛感知环境变化,又能像激光般精准定位关键节点,在知识密集型任务中实现效率与准确性的平衡。 - Map阶段对应发散思维过程,使用较宽松的温度系数鼓励多样化候选集生成;适用于头脑风暴式探索潜在关联路径。
- Reduce阶段侧重收敛决策,采用低温度强化逻辑一致性;常用于冲突消解和最优解筛选。
- 这些参数会影响 LLM 在不同阶段生成内容的方式(例如,
allow_general_knowledge:- 这个参数控制是否允许模型在回答中加入通用知识。如果设置为
True,LLM 会尝试将 外部知识 加入到查询的结果中。这对于需要广泛知识背景的任务可能有帮助,但也有可能导致模型生成 虚假信息(hallucinations)。为了避免这个问题,默认值设置为False。
- 这个参数控制是否允许模型在回答中加入通用知识。如果设置为
json_mode:- 这个参数控制结果的格式。如果设置为
True,LLM 会生成结构化的 JSON 格式 输出。如果设置为False,则返回的是更自由形式的文本回答。对于结构化数据的处理,通常使用 JSON 格式。
- 这个参数控制结果的格式。如果设置为
context_builder_params:- 这是传入给上下文构建器的参数,用来进一步配置如何构建查询上下文(例如是否使用社区简要摘要、是否包含社区排名等)。
concurrent_coroutines:- 这个参数控制并发协程的数量。全局搜索引擎支持并发处理多个查询,如果你需要同时处理多个请求,可以增加这个值。比如设置为
32,意味着最多可以同时处理 32 个查询。
- 这个参数控制并发协程的数量。全局搜索引擎支持并发处理多个查询,如果你需要同时处理多个请求,可以增加这个值。比如设置为
response_type:- 这个参数定义了模型生成的响应的类型和格式。它是一个自由文本的描述,指明返回的内容应该是什么样的格式。在这里,
"multiple paragraphs"表示模型会生成多段文字的回答,适合长篇的说明或报告。
- 这个参数定义了模型生成的响应的类型和格式。它是一个自由文本的描述,指明返回的内容应该是什么样的格式。在这里,
执行全局搜索:
from IPython.display import display, Code, Markdown
result = await search_engine.asearch("请帮我介绍下什么是执剑人?")
display(Markdown(result.response))
点击查看完整代码
"""
GraphRAG 全局搜索示例
使用 DeepSeek API 进行知识图谱的全局搜索查询
"""
import tiktoken
import pandas as pd
from graphrag.query.indexer_adapters import (
read_indexer_communities,
read_indexer_entities,
read_indexer_reports,
)
from graphrag.query.llm.oai.chat_openai import ChatOpenAI
from graphrag.query.llm.oai.typing import OpenaiApiType
from graphrag.query.structured_search.global_search.community_context import (
GlobalCommunityContext,
)
from graphrag.query.structured_search.global_search.search import GlobalSearch
# ==================== 配置参数 ====================
INPUT_DIR = "./openl/output"
LANCEDB_URI = f"{INPUT_DIR}/lancedb"
COMMUNITY_LEVEL = 2 # 社区层级
# 数据表名称
COMMUNITY_TABLE = "create_final_communities"
COMMUNITY_REPORT_TABLE = "create_final_community_reports"
ENTITY_TABLE = "create_final_nodes"
ENTITY_EMBEDDING_TABLE = "create_final_entities"
# ==================== 数据加载 ====================
# 相关指定数据加载,包含:实体表、社区表等
print("正在加载数据...")
community_df = pd.read_parquet(f"{INPUT_DIR}/{COMMUNITY_TABLE}.parquet")
entity_df = pd.read_parquet(f"{INPUT_DIR}/{ENTITY_TABLE}.parquet")
report_df = pd.read_parquet(f"{INPUT_DIR}/{COMMUNITY_REPORT_TABLE}.parquet")
entity_embedding_df = pd.read_parquet(f"{INPUT_DIR}/{ENTITY_EMBEDDING_TABLE}.parquet")
print(f"社区数量: {len(community_df)}")
print(f"实体数量: {len(entity_df)}")
print(f"报告数量: {len(report_df)}")
# ==================== 数据预处理 ====================
# 根据上面加载好的数据表格重构实体和社区报告等对象集合
# read_indexer_communities 函数表示为特征解析器
print("\n正在预处理数据...")
communities = read_indexer_communities(community_df, entity_df, report_df)
reports = read_indexer_reports(report_df, entity_df, COMMUNITY_LEVEL)
entities = read_indexer_entities(entity_df, entity_embedding_df, COMMUNITY_LEVEL)
# ==================== 构建上下文 ====================
token_encoder = tiktoken.get_encoding("cl100k_base")
context_builder = GlobalCommunityContext(
community_reports=reports,
communities=communities,
entities=entities,
token_encoder=token_encoder,
)
# ==================== 上下文构建参数 ====================
context_builder_params = {
"use_community_summary": False, # 不使用社区摘要
"shuffle_data": True, # 打乱数据
"include_community_rank": True, # 包含社区排名
"min_community_rank": 0, # 最小社区排名
"community_rank_name": "rank", # 社区排名字段名
"include_community_weight": True, # 包含社区权重
"community_weight_name": "occurrence weight", # 社区权重字段名
"normalize_community_weight": True, # 归一化社区权重
"max_tokens": 12_000, # 最大 token 数
"context_name": "Reports", # 上下文名称
}
# ==================== LLM 参数配置 ====================
# Map 阶段参数(并行处理多个社区)
map_llm_params = {
"max_tokens": 1000,
"temperature": 0.0,
"response_format": {"type": "json_object"},
}
# Reduce 阶段参数(汇总所有结果)
reduce_llm_params = {
"max_tokens": 2000,
"temperature": 0.0,
}
# ==================== 初始化 LLM ====================
llm = ChatOpenAI(
api_key="sk-e18a9xxxxb0dc550e",
model="deepseek-chat",
api_base="https://api.deepseek.com",
api_type=OpenaiApiType.OpenAI,
max_retries=20,
)
# ==================== 初始化搜索引擎 ====================
print("\n正在初始化搜索引擎...")
search_engine = GlobalSearch(
llm=llm,
context_builder=context_builder,
token_encoder=token_encoder,
max_data_tokens=12_000, # 最大数据 token 数
map_llm_params=map_llm_params,
reduce_llm_params=reduce_llm_params,
allow_general_knowledge=False, # 不允许使用通用知识
json_mode=True, # 使用 JSON 模式
context_builder_params=context_builder_params,
concurrent_coroutines=32, # 并发协程数
response_type="multiple paragraphs", # 响应类型:多段落
)
# ==================== 执行搜索 ====================
async def search_query(query: str):
"""执行单次搜索查询"""
print(f"\n{'='*80}")
print(f"问题: {query}")
print(f"{'='*80}")
print("正在搜索...\n")
result = await search_engine.asearch(query)
print("回答:")
print("-" * 80)
print(result.response)
print("-" * 80)
return result
async def chat_loop():
"""连续对话循环"""
print("\n" + "="*80)
print("GraphRAG 知识问答系统")
print("="*80)
print("数据集: 义乌房价信息")
print("输入 'exit'、'quit' 或 'q' 退出")
print("输入 'clear' 清屏")
print("="*80)
conversation_history = []
while True:
try:
# 获取用户输入
user_input = input("\n👤 你: ").strip()
# 检查退出命令
if user_input.lower() in ['exit', 'quit', 'q', '退出']:
print("\n👋 再见!感谢使用 GraphRAG 知识问答系统。")
break
# 清屏命令
if user_input.lower() in ['clear', '清屏']:
import os
os.system('cls' if os.name == 'nt' else 'clear')
print("\n" + "="*80)
print("GraphRAG 知识问答系统")
print("="*80)
continue
# 空输入检查
if not user_input:
print("⚠️ 请输入问题")
continue
# 执行搜索
print("\n🤖 AI: ", end="", flush=True)
result = await search_engine.asearch(user_input)
print(result.response)
# 保存对话历史
conversation_history.append({
"question": user_input,
"answer": result.response,
"context_data": result.context_data
})
except KeyboardInterrupt:
print("\n\n👋 检测到中断信号,正在退出...")
break
except Exception as e:
print(f"\n❌ 发生错误: {str(e)}")
print("请重试或输入 'exit' 退出")
# 显示对话统计
if conversation_history:
print(f"\n📊 本次会话统计:")
print(f" - 总提问数: {len(conversation_history)}")
print(f" - 平均回答长度: {sum(len(h['answer']) for h in conversation_history) // len(conversation_history)} 字符")
async def demo_queries():
"""演示模式:运行预设的查询示例"""
print("\n" + "="*80)
print("演示模式:运行预设查询")
print("="*80)
demo_questions = [
"义乌的房价趋势如何?哪些区域房价最高?",
"义乌有哪些热门新盘?价格如何?",
"义乌二手房市场的情况怎么样?",
"义乌有哪些人才购房政策?",
"稠城街道和北苑街道的房价有什么区别?"
]
for i, query in enumerate(demo_questions, 1):
print(f"\n[{i}/{len(demo_questions)}]")
await search_query(query)
if i < len(demo_questions):
input("\n按 Enter 继续下一个问题...")
print("\n✅ 演示完成!")
async def main():
"""主函数:选择运行模式"""
print("\n" + "="*80)
print("GraphRAG 知识问答系统 - 启动中...")
print("="*80)
print(f"✅ 数据加载完成")
print(f"✅ 搜索引擎初始化完成")
print(f"✅ 使用模型: deepseek-chat")
print("\n请选择运行模式:")
print("1. 连续对话模式(推荐)")
print("2. 演示模式(运行预设问题)")
print("3. 单次查询模式")
while True:
choice = input("\n请输入选项 (1/2/3): ").strip()
if choice == "1":
await chat_loop()
break
elif choice == "2":
await demo_queries()
break
elif choice == "3":
query = input("\n请输入你的问题: ").strip()
if query:
await search_query(query)
break
else:
print("❌ 无效选项,请输入 1、2 或 3")
return None
# 运行主函数
if __name__ == "__main__":
import asyncio
# 在普通 Python 环境中运行
result = asyncio.run(main())
拓展
问题1:graphRAG是不是只支持txt文档?
不是的!GraphRAG 支持多种文档格式,不仅仅是 TXT。
GraphRAG 支持的输入格式
- 文本文件 (默认)
input:
type: file
file_type: text # 纯文本
base_dir: "input"
file_pattern: ".*\\.txt$"
- CSV 文件
input:
type: file
file_type: csv # CSV 格式
base_dir: "input"
file_pattern: ".*\\.csv$"
# CSV 特定配置
source_column: "source" # 来源列
text_column: "text" # 文本内容列
title_column: "title" # 标题列(可选)
timestamp_column: "date" # 时间戳列(可选)
- Azure Blob Storage
input:
type: blob
container_name: "your-container"
storage_account_blob_url: "https://..."
connection_string: "..."
如何处理其他格式(PDF、Word、Markdown 等)
虽然 GraphRAG 原生只支持 TXT 和 CSV,但你可以通过预处理来支持其他格式:
方案 1:转换为 TXT(推荐)
创建一个预处理脚本:
# preprocess.py
import os
from pathlib import Path
# 处理 PDF
from PyPDF2 import PdfReader
def pdf_to_txt(pdf_path, output_dir):
reader = PdfReader(pdf_path)
text = ""
for page in reader.pages:
text += page.extract_text() + "\n"
output_path = Path(output_dir) / f"{Path(pdf_path).stem}.txt"
with open(output_path, 'w', encoding='utf-8') as f:
f.write(text)
print(f"转换完成: {output_path}")
# 处理 Word
from docx import Document
def docx_to_txt(docx_path, output_dir):
doc = Document(docx_path)
text = "\n".join([para.text for para in doc.paragraphs])
output_path = Path(output_dir) / f"{Path(docx_path).stem}.txt"
with open(output_path, 'w', encoding='utf-8') as f:
f.write(text)
print(f"转换完成: {output_path}")
# 处理 Markdown
def md_to_txt(md_path, output_dir):
with open(md_path, 'r', encoding='utf-8') as f:
text = f.read()
output_path = Path(output_dir) / f"{Path(md_path).stem}.txt"
with open(output_path, 'w', encoding='utf-8') as f:
f.write(text)
print(f"转换完成: {output_path}")
# 批量转换
def convert_all(source_dir, output_dir):
os.makedirs(output_dir, exist_ok=True)
for file in Path(source_dir).rglob("*"):
if file.suffix == ".pdf":
pdf_to_txt(file, output_dir)
elif file.suffix == ".docx":
docx_to_txt(file, output_dir)
elif file.suffix == ".md":
md_to_txt(file, output_dir)
if __name__ == "__main__":
convert_all("./raw_docs", "./input")
安装依赖:
pip install PyPDF2 python-docx
方案 2:使用 CSV 格式(结构化数据)
如果你有多个文档,可以整理成 CSV:
id,title,text,source,date
1,"义乌房价报告","浙江金华义乌房价...","房天下","2026-03-05"
2,"市场分析","根据最新数据...","安居客","2026-03-04"
然后配置
settings.yaml
:
input:
type: file
file_type: csv
base_dir: "input"
file_pattern: ".*\\.csv$"
text_column: "text"
title_column: "title"
source_column: "source"
timestamp_column: "date"
实际工作流程示例
# 1. 准备原始文档(PDF、Word、Markdown 等)
mkdir raw_docs
# 将你的文档放入 raw_docs/
# 2. 转换为 TXT
python preprocess.py
# 3. 运行 GraphRAG 索引
graphrag index --root ./openl
# 4. 查询
python demo.py
高级:自定义输入加载器
如果需要更复杂的处理,可以自定义输入加载器:
from graphrag.index.input import BaseInputLoader
class CustomLoader(BaseInputLoader):
def load(self):
# 自定义加载逻辑
# 返回 DataFrame,包含 text 列
pass
总结
原生支持:TXT、CSV
通过转换支持:PDF、Word、Markdown、HTML 等任何格式
推荐做法:统一转换为 TXT 或整理成 CSV,这样最简单可靠

浙公网安备 33010602011771号