DeepSeek使用GraphRAG搭建私有知识库

291 阅读7分钟

背景

过程

1. 安装 graphrag

创建虚拟环境,以免pip安装的软件和系统冲突了


python3 -m venv myenv
# 在 Linux 或 macOS 上
source myenv/bin/activate

然后在虚拟环境里面执行:

pip install graphrag==1.1.0

我的python3 版本我3.10.16,pip 版本为 25.0.1。 如果不能下载,注意可以切换一个pip软件安装的镜像源

(myenv) root@ubuntu:/graphrag# pip --version
pip 25.0.1 from /graphrag/myenv/lib/python3.10/site-packages/pip (python 3.10)

2. 检查graphrag的版本

(myenv) root@ubuntu:/graphrag# pip show graphrag
Name: graphrag
Version: 1.1.0
Summary: GraphRAG: A graph-based retrieval-augmented generation (RAG) system.
Home-page: 
Author: Alonso Guevara Fernández
Author-email: alonsog@microsoft.com
License: MIT
Location: /graphrag/myenv/lib/python3.10/site-packages
Requires: aiofiles, azure-cosmos, azure-identity, azure-search-documents, azure-storage-blob, devtools, environs, fnllm, future, graspologic, httpx, json-repair, lancedb, matplotlib, networkx, nltk, numpy, openai, pandas, pyaml-env, pyarrow, pydantic, python-dotenv, pyyaml, rich, tenacity, tiktoken, tqdm, typer, typing-extensions, umap-learn
Required-by:

3. 创建检索项目文件夹

mkdir -p ./openl/input

然后上传一个数据集 ID3、C4.5决策树的建模流程.txt 文件

目录结构如下:

(myenv) root@ubuntu:/graphrag# ll ./openl/input/
total 20
drwxr-xr-x 2 root root  4096 Mar  4 22:32 ./
drwxr-xr-x 3 root root  4096 Mar  4 22:32 ../
-rw-r--r-- 1 root root 10065 Mar  3 17:21 ID3C4.5决策树的建模流程.txt
(myenv) root@ubuntu:/graphrag# pwd
/graphrag

4. 初始化项目

(myenv) root@ubuntu:/graphrag# graphrag init --root ./openl
Initializing project at /graphrag/openl

5. 填写DeepSeek 配置 参数

打开 .env 文件,填写DeepSeek API-KEY

1741162123212.png

打开 setting.yaml 文件,填写模型名称和反向代理地址:

image-20250108184104855

embeddings 模型可以选择其他的模型,有一些免费的,我用的智普AI。

image.png

我的配置是这样的。

6. GraphRAG 索引Indexing过程执行

使用GraphRAG 脚本自动执行indexing

graphrag index --root ./openl

image.png

运行成功后:

image.png

运行结束后,各知识图谱相关书籍都保存在output文件夹中:

image.png

索引阶段的主要输出内容如下:

  1. 实体表(Nodes Table)

    • 文件名create_final_nodes.parquet
    • 内容:知识图谱中的实体节点(例如:人、地点、组织)。
    • 包含信息
      • 实体的名称(如 "John Doe")。
      • 实体的类别(如 "PERSON", "ORGANIZATION", "LOCATION")。
      • 与社区相关的信息(如实体所属的社区)。
      • 实体的度数(degree),表示该实体在图谱中的连接数。
  2. 关系表(Relationships Table)

    • 文件名create_final_relationships.parquet
    • 内容:知识图谱中实体之间的关系(即图谱的边)。
    • 包含信息
      • 两个实体之间的关系描述(例如 "works for", "lives in")。
      • 关系的强度(数值化,用于衡量关系的显著性或重要性)。
  3. 嵌入向量表(Entity Embedding Table)

    • 文件名create_final_entities.parquet
    • 内容:实体的语义嵌入,用于表示实体的语义信息。
    • 用途:支持语义搜索(通过嵌入计算实体之间的相似性)。
  4. 社区报告表(Community Reports Table)

    • 文件名create_final_community_reports.parquet
    • 内容:社区的摘要信息。
    • 用途:支持全局搜索(通过社区信息回答关于数据集整体的问题)。
  5. 文本单元表(Text Units Table)

    • 文件名create_final_text_units.parquet
    • 内容:被切分的原始文本单元(TextUnits)。
    • 用途:将知识图谱和原始文本结合,为 LLM 提供上下文支持。
  6. 社区表(Community Table)

    • 文件名create_final_Communities.parquet
    • 内容:每个社区基本情况。
  7. 文件表(Documents Table)

    • 文件名create_final_documents.parquet
    • 内容:用于记录所有参与知识图谱构建的文件情况。

为什么用 Parquet 格式保存知识图谱?

  1. 高效存储

  • 知识图谱中的数据通常是结构化的,包含大量的实体、关系、嵌入等。

  • Parquet 的列式存储能够显著减少磁盘占用,同时提高读取效率。

  1. 快速读取

  • 查询阶段需要快速加载实体、关系、嵌入等数据到内存中。

  • Parquet 支持按需加载所需的列,避免了不必要的数据读取。

  1. 兼容性好

  • Parquet 是一个开放的标准,广泛支持各种数据处理工具(如 Pandas、Spark、Hadoop)。
  • GraphRAG 可以在 Python 中使用 Pandas 或其他工具轻松读取这些文件。

7. GraphRAG问答流程

直接使用graphrag 命令方式查询

除了使用Python代码环境进行问答外,若希望快速测试问答性能,也可以直接在命令行中,借助graphrag提供的脚本命令进行快速问答,例如:

graphrag query --root ./openl --method local --query "请帮我介绍下ID3算法"

即可完成问答。

image.png

image.png

构建LocalSearch(本地搜索) 搜索引擎进行回答

首先执行代码需要安装以下依赖:

# 基础数据处理
pip install pandas pyarrow  # 读取 Parquet 文件需要 pyarrow/fastparquet

# 向量数据库
pip install lancedb  # LanceDB 向量存储

# OpenAI 相关
pip install openai tiktoken  # OpenAI API 和分词工具

# 异步支持 (代码中有 await 调用)
pip install aiohttp nest_asyncio

# 其他工具
pip install ipython  # 用于 display(Markdown) 的交互式输出

然后创建python代码如下:

import os

import asyncio

import pandas as pd
import tiktoken

from graphrag.query.context_builder.entity_extraction import EntityVectorStoreKey
from graphrag.query.indexer_adapters import (
    read_indexer_covariates,
    read_indexer_entities,
    read_indexer_relationships,
    read_indexer_reports,
    read_indexer_text_units,
)
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.lancedb import LanceDBVectorStore

INPUT_DIR = "./openl/output"
LANCEDB_URI = f"{INPUT_DIR}/lancedb"

COMMUNITY_REPORT_TABLE = "create_final_community_reports"
ENTITY_TABLE = "create_final_nodes"
ENTITY_EMBEDDING_TABLE = "create_final_entities"
RELATIONSHIP_TABLE = "create_final_relationships"
TEXT_UNIT_TABLE = "create_final_text_units"
COMMUNITY_LEVEL = 2

# read nodes table to get community and degree data
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)

# load description embeddings to an in-memory lancedb vectorstore
# to connect to a remote db, specify url and port values.
description_embedding_store = LanceDBVectorStore(
    collection_name="default-entity-description",
)
description_embedding_store.connect(db_uri=LANCEDB_URI)

print(f"Entity count: {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"Relationship count: {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"Report records: {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"Text unit records: {len(text_unit_df)}")
text_unit_df.head()


sd_api_key = "你的chat模型APIKEY"
openai_api_key = "你的Embending 模型KEY"

llm_model = "deepseek-chat"
embedding_model = "embedding-2"

llm_api_base = "https://api.deepseek.com"
embedding_model_api_base = "https://open.bigmodel.cn/api/paas/v4"


llm = ChatOpenAI(
    api_key=sd_api_key,
    model=llm_model,
    api_base=llm_api_base,
    api_type=OpenaiApiType.OpenAI,  
    max_retries=20,
)

token_encoder = tiktoken.get_encoding("cl100k_base")

text_embedder = OpenAIEmbedding(
    api_key=openai_api_key,
    api_base=embedding_model_api_base,
    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": True,
    "return_candidate_context": True,
    "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", 
)


async def main():
    # 原代码中的异步调用部分
    result = await search_engine.asearch("请帮我介绍下ID3决策树算法")
    print(result.response)

if __name__ == "__main__":
    asyncio.run(main())  # 启动异步事件循环

sd_api_key = "你的chat模型APIKEY" openai_api_key = "你的Embending 模型KEY" 这两个需要使用你自己申请的AIPkey

文件命名为 graphrag_local_search.py

1741231316509.png

然后执行

python3 graphrag_local_search.py

效果如下:

image.png

构建GlobalSearch(全局搜索) 搜索引擎进行回答

python代码如下:

import asyncio

import pandas as pd
import tiktoken

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_TABLE = "create_final_communities"
COMMUNITY_REPORT_TABLE = "create_final_community_reports"
ENTITY_TABLE = "create_final_nodes"
ENTITY_EMBEDDING_TABLE = "create_final_entities"
COMMUNITY_LEVEL = 2

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")

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)

sd_api_key = "你的chat模型APIKEY"
openai_api_key = "你的Embending 模型KEY"

llm_model = "deepseek-chat"
embedding_model = "embedding-2"

llm_api_base = "https://api.deepseek.com"
embedding_model_api_base = "https://open.bigmodel.cn/api/paas/v4"


llm = ChatOpenAI(
    api_key=sd_api_key,
    model=llm_model,
    api_base=llm_api_base,
    api_type=OpenaiApiType.OpenAI,  
    max_retries=20,
)

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,  
    "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,
}

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",  
)


async def main():
    # 原代码中的异步调用部分
    result = await search_engine.asearch("请帮我介绍下ID3决策树算法")
    print(result.response)

if __name__ == "__main__":
    asyncio.run(main())  # 启动异步事件循环

sd_api_key = "你的chat模型APIKEY" openai_api_key = "你的Embending 模型KEY" 这两个需要使用你自己申请的AIPkey

文件命名为 graphrag_global_search.py

image.png

然后执行

python3 graphrag_global_search.py

效果如下:

image.png

这个两个检索技术的区别,主要是广度和深度上的区别