懒懒笔记 | 课代表带你梳理【RAG课程 19:基于知识图谱的RAG】


大家好呀!这里是你们的课代表懒懒~不知不觉,我们已经来到课程的最后一节,为我们的坚持和努力点赞!

通过之前的学习,我们已经学会了如何使用RAG增强大模型的能力。有细心的小伙伴发现,传统的RAG在召回上存在碎片化的局限。为了解决传统RAG存在的问题, 知识图谱RAG 应运而生,本节课就让懒懒带大家回顾如何搭建知识图谱RAG吧!

 

知识图谱RAG,超越朴素RAG!

朴素RAG:😔

  • 检索返回的内容孤立、缺乏结构关联。
  • 回答在需要跨文档、跨段落推理的场景中,召回片段之间缺乏逻辑和语义连贯性。
  • 无法识别文档间的主题关系,信息冗余与漏召。

知识图谱RAG:😊

  • 实体-关系-属性的三元组形式显式建模语义关联,使系统能够清晰刻画知识之间的语义路径。
  • 在图谱上进行结构感知的检索或路径遍历,更有针对性地召回与用户问题语义相关、逻辑连贯的信息。
  • 跨文档实体的对齐与融合,增强主题聚合与复杂推理能力。

知识图谱:让机器理解世界的“知识网络”

一切从图说起 🔥

(图(Graph)结构)

图是一种由一组节点(或称为顶点)和节点之间的边组成的数据结构,例如图1 可以表示地铁路线图,每个顶点表示一个站点,每个边表示站点之间的连通性。

知识图谱(Knowledge Graph, KG)是一种用 “图” 结构表示知识的方式,通常由三元组组成,其中每个三元组包含一个实体、关系以及与该实体相关的另一个实体,结构形式为 (实体1)—[关系]→(实体2)。

(知识图谱示例(图源网络))

知识图谱有三个重要组成部分,即实体,关系和属性,每个部分的解释如下:

  • 实体(Entity):知识图谱的基本构成单位,代表现实世界中的物品、概念、事件等。例如图2中的每个人物、城市、课程都是实体。🤔
  • 关系(Relationship):描述实体间联系的方式,通常用边来表示。比如图2中男士与Bonn的关系是“lives in”,表示这位男士生活在Bonn City。🤔
  • 属性(Attribute):实体或关系的附加信息。例如图2中Bonn City有“Type”,“Population”和“Area”三个属性。🤔

从文档中构建知识图谱

拆解流程🔥

 

RAG系统的“智能外挂”:知识图谱如何赋能RAG?

构建知识图谱

要构建基于知识图谱的RAG系统,就像是在搭建一座“知识摩天楼”。
现成的结构化数据(比如数据库、RDF、Neo4j)就像预制好的砖块,拆开就能直接用,省时又省力。
而非结构化数据(比如那些长篇大论的文字)嘛,就像一堆散落的乐高零件,需要你仔细翻找,才能拼出点有用的东西,比如人名、地名、日期,以及它们之间的“小八卦”(比如“谁创办了什么”“谁住在哪儿”)。

怎么处理这些“碎片”呢?

可以采用传统的信息抽取方法,主要分两步:

  • NER(命名实体识别):像一位“考古专家”,负责从文本中挖掘出关键的实体,例如人物、组织、地点等;
  • 关系抽取:这位专家则专注于搞清楚实体之间的“人际关系”,比如“张三是某公司的创始人”。

当然,还有另一种更“现代”的做法——直接派出大型语言模型(LLM)这个“全能大工匠”。它能在一句话中同时识别实体、提取关系,甚至顺带润色输出,效率极高。

所以,搭建知识图谱就是这么一件“从零件到艺术品”的事情,有了这些工具,轻松又有趣!

图3 利用大模型构建知识图谱流程

处理文本构建知识图谱就像拆书搭积木,步骤如下:

  1. 文本分块:先把长文本切成小块,方便操作,这步和普通RAG的“切片”手法一样。
  2. 提取图元素:用LLM发“指令”从每块文本里找出关键积木:
  • 第一步:找实体(例如名字、类型、描述),就像从一堆零件里挑出有用的砖块。
  • 第二步:找关系(谁和谁有啥联系,比如“合作”“位于”),把砖块之间的连接线画出来。
  1. 合并实体和关系:最后,把不同文本块中重复的积木合并,拼成一个完整、连贯的知识图谱!

简单来说,就是“切片-找积木-拼完整”,过程清晰又高效!😮

使用知识图谱召回

其流程如图所示:

(Knowledge Graph Based RAG 流程示例)

基于知识图谱的RAG系统回答过程有这样几个步骤!😮

  • 检索阶段,根据用户输入问题从知识图谱中召回相关的实体、关系以及原文内容。
  • 增强阶段,将召回的结构化结果整合为额外的上下文信息,与用户输入一起提供给大模型,以增强其理解和推理能力。
  • 生成阶段,系统通过大模型生成答案,该过程与朴素RAG系统一致。

两大知识图谱RAG系统供你挑选

GraphRAG

GraphRAG通过构建知识图谱和分层社区结构,将局部信息聚合为全局理解。

其核心优势在于:

  • 利用LLM从文档中提取实体、关系和事实声明构建知识图谱,通过图结构索引捕捉语义关联。
  • 通过分层社区检测,使用图社区算法将图谱划分为紧密关联的社区,递归生成从局部到全局的摘要。
  • 采用Map-Reduce式回答生成方法,借助社区摘要的并行处理与聚合,生成全面且多样的全局回答。

GraphRAG工作流程

1.将文档拆分为文本块,并使用LLM提取其中的实体、关系及事实声明

2.基于提取结果构建知识图谱,将实体和关系转化为图节点和边,对重复实例进行聚合并生成节点描述,同时根据关系出现频率设定边的权重。

3.利用Leiden算法对图谱进行递归划分,形成嵌套的社区结构,每个社区代表一个语义主题,构建从局部到全局的层级结构。

社区新概念!

微软 GraphRAG 在知识图谱的基础上增加了一个社区(Community)的概念。

社区是 GraphRAG 中的高级结构,表示一组相关实体的集合。每个社区包含以下字段:

ID:社区的唯一标识符。
Level:社区的层级(如 0、1、2 等)。
Entity IDs:社区中包含的实体 ID 列表。
Relation IDs:社区中包含的关系 ID 列表。
Text Block IDs:社区中包含的文本块 ID 列表。
Description:社区的描述信息。
Summary:社区的摘要信息。

GraphRAG 在是构建Knowledge Graph后,增加了一个社区摘要(Community Summaries)的生成环节。这一策略使得系统在检索时只关注与查询高度相关的社区,而不是检索整个图。社区摘要对数据全局结构和语义的高度概括, 即使没有问题, 用户也可以通过浏览不同层次的社区摘要来理解语料库!

社区摘要生成由两步组成:

  1. 社区检测:使用图分析算法,获得具有高度连接性的实体簇
  2. 摘要提取:使用LLM根据社区中的实体和关系,提取各类型总结。

(微软 Graph RAG 的社区聚类效果图)

Light RAG

LightRAG的知识图谱是比较基础的 分片+实体/关系

LightRAG 就像一位“轻装上阵”的知识图谱专家,甩掉了复杂的社区聚类,直接构建知识图谱,支持增量更新——新数据只需补充节点和关系,无需重建全图,方便又高效!🧠

它的检索方式也很聪明,采用了双层检索策略

  1. 低层级检索:专注细节,锁定具体实体的信息,比如“某人是谁”“某地在哪”。→
  2. 高层级检索:关注全局,处理宏观主题和趋势,比如“某领域的发展方向”。→

这种设计让 LightRAG 能快速适应动态数据场景,同时保证检索既全面又精准,堪称高效实用的“知识捕手”!

(LightRAG 整体结构(图源 LightRAG 论文))

LightRAG通过以下步骤实现 知识图谱的构建与检索

1.实体和关系抽取 :从文档中提取实体和关系(如实体名称、类型、描述等),完成后进行去重操作,然后进行图索引,构建知识图谱。

  • 节点属性 :包括实体名称、类型、描述、来源文档ID。

  • 关系属性 :包括起点实体、终点实体、关键词、描述、来源文档ID。

2.关键词提取

  • 具体关键词 :关注精确信息(如实体名称、属性),用于底层级检索。

  • 抽象关键词 :关注宏观主题或领域信息,用于高层级检索。

3.双层级检索:

  • 低层级检索 :针对具体关键词,查找特定实体及其属性,提供详细信息。

    (示例关键词:如“beekeeper”“hive”,获取具体实体的属性和关系。)

  • 高层级检索 :针对抽象关键词,分析宏观主题、趋势或多个实体间的关系。

    (示例关键词:如“agriculture”“production”,提供全局性见解。)

4.动态更新: 当有新数据加入时,仅需更新新增的节点和边,无需重建整个知识图谱,降低计算成本。

 

通过这种设计,LightRAG能够 高效 处理具体细节和宏观主题问题,为用户提供精准且深度的答案!

 

 

LightRAG VS GraphRAG🤦‍

🌐对于Legal Dataset,共94篇文章,约5000K Token(约5个水浒传大小)。GraphRAG 生成了 1399 个communities,平均每个社区报告生成需要5000Token, 其中 610 个 level-2 communities, 平均每个level-2的社区占用1000Token。下表对比了创建知识图谱和一次检索所消耗的LLM Token。C_Max为API单次请求最多Token限制,T_extract 代表提取实体和关系消耗Token数,C_extract则表示提取产生的API调用次数。🤦‍

处理阶段

query-召回

文档创建(5000K Token )

方案

GraphRAG

LightRAG

GraphRAG

LightRAG

Token消耗

610*1000

< 100

1399*5000 + T_extract

T_extract

API 调用次数

610*1000/C_Max

1

1399 + C_extract

C_extract

 

实战演练:知识图谱RAG对复杂query的提升

我们基于LightRAG进行实战,来看看需要的配置吧!

 

Dataset

  • 水浒传_utf8.txt (约912K Tokens)

切分及召回

  • NaiveRAG Chunk 1200 Token; top_k: 10

  • LightRAG Chunk 1200 Token ; relation/entity top_k: 60

使用LightRAG构建知识图谱

Step0️⃣:  环境准备。

🔨vLLM

pip install vllm

🔨LightRAG

pip install lightrag-hku

🔨infinity

pip install infinity-emb

🔨LazyLLM

pip3 install lazyllm
lazyllm install full
  • Git clone https://github.com/HKUDS/LightRAG 并安装lightrag pip install “lightrag-hku[api]”

  • 下载语料 水浒传.txt 转为 utf8,并重命名放入比如”novel/shuihu.txt”

  • 主动创建保存知识图谱的目录

 

Step1️⃣:  使用VLLM/Ollama等工具部署openai 兼容格式的LLM服务。

⚠️注:由于文章较长,尝试过多个平台的在线模型,即使换不同的语料,依然会有触发“内容安全限制”的情况。所以只能选择本地部署的LLM。

下载模型(Qwen 2.5-32B-Instruct),推荐从ModelScope下载:

注:由于文章较长,尝试过多个平台的在线模型,即使换不同的语料,依然会有触发“内容安全限制”的情况。所以只能选择本地部署的LLM。

下载模型(Qwen 2.5-32B-Instruct),推荐从ModelScope下载:

python -m vllm.entrypoints.openai.api_server --model /mnt/lustre/share_data/lazyllm/models/Qwen2.5-32B-Instruct/ --served-model-name qwen2 --max_model_len 16144 --host 0.0.0.0 --port 12345

Step2️⃣:  部署embedding 服务或使用已有平台的openai兼容格式的embedding在线服务。

下载模型(bge-large-zh-v1.5),推荐从ModelScope下载:

infinity_emb v2 --model-id "/mnt/lustre/share_data/lazyllm/models/bge-large-zh-v1.5" --port 19001 --served-model-name bge-large

Step3️⃣: 参照 examples/lightrag_openai_compatible_demo.py 修改LLM服务和embedding服务的配置  配置llm_model_func和embedding_func 的三个参数:model, base_url, api_key。

✅设置处理的文件名和保存知识图谱的文件夹路径

  • WORKING_DIR 为 知识图谱保存的文件夹路径

  • PATH_TO_TXT 为 待解析的小说 <水浒传>,注意检查是否为utf8格式

✅修改llm_model的配置

  • base_url 改为 http://{ip}:{port}/v1

  • model_name 改为 vLLM中配置的 qwen2

✅修改openai_embed的配置

  • base_url 改为 http://{ip}:{port}

  • model_name 改为 infinity中配置的 bge-large

Step 4️⃣ : 调整examples/lightrag_openai_compatible_demo.py 中的main 函数  设置知识图谱的存储目录WORKING_DIR 和文本文件的路径 PATH_TO_TXT。

async def main():
    WORKING_DIR = "****"
    PATH_TO_TXT = "shuihu.txt"
    try:
        embedding_dimension = await get_embedding_dim()
        print(f"Detected embedding dimension: {embedding_dimension}")

        rag = LightRAG(
            working_dir=WORKING_DIR,
            llm_model_func=llm_model_func,
            embedding_func=EmbeddingFunc(
                embedding_dim=embedding_dimension,
                max_token_size=8192,
                func=embedding_func,
            ),
        )
        with open(PATH_TO_TXT, "r", encoding="utf-8") as f:
            await rag.ainsert(f.read())
    except Exception as e:
        print(f"An error occurred: {e}")

Step5️⃣ : 运行Demo,并检查WORKING_DIR中是否有graph_chunk_entity_relation.graphml 生成,其大小约为5M左右表示成功。

 

 

LazyLLM融合LightRAG

让我们看看整体的流程图吧!

  • Step 1: 参考LightRAG构建知识图谱中的方式部署本地LLM和embedding服务✔️
  • Step 2: 设置LightRAG知识图谱的存储目录working_dir 和文本文件的路径 txt_path ,并配置LazyLLM知识库目录dataset_path✔️
import os
import asyncio
import lazyllm
from lazyllm import pipeline, parallel, bind, Retriever
from lightrag import LightRAG, QueryParam
from lightrag.kg.shared_storage import initialize_pipeline_status
from lightrag.utils import setup_logger, EmbeddingFunc
from lightrag.llm.openai import openai_complete, openai_embed

class LightRAGRetriever:
    def __init__(self, working_dir, txt_path, mode="hybrid"):
        self.working_dir = working_dir
        self.txt_path = txt_path
        self.mode = mode
        self.api_key = "empty"
        self.rag = None 
        self.loop = asyncio.new_event_loop()
        setup_logger("lightrag", level="INFO")

        if not os.path.exists(working_dir):
            os.makedirs(working_dir)

        self.loop.run_until_complete(self.initialize_rag())

    async def initialize_rag(self):
        self.rag = LightRAG(
            working_dir=self.working_dir,
            embedding_func=EmbeddingFunc(
                embedding_dim=1024,
                max_token_size=8192,
                func=lambda texts: openai_embed(
                    texts=texts,
                    model="bge-large",
                    base_url="http://0.0.0.0:19001",
                    api_key=self.api_key,
                )
            ),
            llm_model_func=openai_complete,
            llm_model_name="qwen2",
            llm_model_kwargs={"base_url": "http://0.0.0.0:12345/v1", "api_key": self.api_key},
        )
        await self.rag.initialize_storages()
        await initialize_pipeline_status()

        with open(self.txt_path, "r", encoding="utf-8") as f:
            content = f.read()
        await self.rag.ainsert(content)

    def __call__(self, query):
        return self.loop.run_until_complete(
            self.rag.aquery(
                query,
                param=QueryParam(mode=self.mode, top_k=3, only_need_context=True)
            )
        )

    def close(self):
        if self.rag:
            try:
                self.loop.run_until_complete(self.rag.finalize_storages())
            except Exception as e:
                print(f"关闭LightRAG时出错: {e}")
            finally:
                self.loop.close()
                self.rag = None

def main():
    kg_retriever = None
    try:
        documents = lazyllm.Document(dataset_path="****")
        prompt = ('请你参考所给的信息给出问题的答案。')

        print("正在初始化知识图谱检索器...")
        kg_retriever = LightRAGRetriever(
            working_dir="****",
            txt_path="****"
        )
        print("知识图谱检索器初始化完成!")

        bm25_retriever = lazyllm.Retriever(
            doc=documents,
            group_name="CoarseChunk",
            similarity="bm25_chinese",
            topk=3
        )

        def bm25_pipeline(query):
            nodes = bm25_retriever(query)
            return "".join([node.get_content() for node in nodes])

        def context_combiner(*args):
            bm25_result = args[0]
            kg_result = args[1]   
            return (
                f"知识图谱召回结果:\n{kg_result}"
                f"BM25召回结果:\n{bm25_result}\n\n"
            )

        with lazyllm.pipeline() as ppl:
            with parallel() as ppl.multi_retrieval:
                ppl.multi_retrieval.bm25 = bm25_pipeline
                ppl.multi_retrieval.kg = kg_retriever
            ppl.context_combiner = context_combiner
            ppl.formatter = (lambda nodes, query: dict(context_str=nodes, query=query)) | bind(query=ppl.input)
            ppl.llm = (
                lazyllm.OnlineChatModule().prompt(
                    lazyllm.ChatPrompter(
                        instruction=prompt,
                        extra_keys=['context_str']
                    )
                )
            )

        lazyllm.WebModule(ppl, port=23466).start().wait()

    except Exception as e:
        print(f"\n处理过程中发生错误: {e}")
        import traceback
        traceback.print_exc()

if __name__ == "__main__":
    main()

Step 3 : 运行Demo,则会启动Web端服务,即可向LLM提出问题啦。

————————————

通过这次的深入学习与实战,我们不仅了解了如何让知识图谱RAG“抽取实体、建图索引、逻辑推理”,更真正掌握了:一个能从碎片化信息中构建语义关联、支持复杂推理的RAG系统,不再只是简单的检索工具,而是具备全局视角与结构化思考能力的智能助手!


从分块抽取让系统“识别知识点”,到构建知识图谱为RAG搭建“语义网络”,再到高低层级检索实现从细节到全局的全面回答,我们一步步,把RAG从文本问答助手,进化成了语义理解+知识聚合的超级推理专家。

 

这不仅是功能的叠加,更是一次认知能力的全面升级!知识图谱RAG不再仅仅“回忆知识”,它开始学会“理解语义”、“关联信息”、“生成深度洞察”。

————————————

更多技术交流,欢迎移步“LazyLLM”GZH!

 

                                                                                </div>


维权提醒:如果你或身边的朋友近五年内因投顾公司虚假宣传、诱导交费导致亏损,别放弃!立即联系小羊维权(158 2783 9931,微信同号),专业团队帮你讨回公道! 📞立即免费咨询退费


Source link

未经允许不得转载:紫竹林-程序员中文网 » 懒懒笔记 | 课代表带你梳理【RAG课程 19:基于知识图谱的RAG】

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址
关于我们 免责申明 意见反馈 隐私政策
程序员中文网:公益在线网站,帮助学习者快速成长!
关注微信 技术交流
推荐文章
每天精选资源文章推送
推荐文章
随时随地碎片化学习
推荐文章
发现有趣的