39.图RAG-使用Light RAG(lightrag 节点和关系生成中文)

内容参考于:图灵AI大模型全栈

LightRag它的使用很简单

它的索引会在本地保存一些文件,下方是对文件的说明

faiss_index_chunks.index:

文本块向量索引文件,FAISS 格式的二进制文件。存储文档拆分后每一小段文本对应的向量数据,用于快速相似检索。纯向量检索(naive 模式)时,会优先在这个文件里匹配和问题最相似的文本段落,文件本身无法用记事本直接打开。

faiss_index_chunks.index.meta.json:

文本块向量索引的配置说明文件。记录对应索引的向量维度、构建参数等元信息,程序加载向量索引时会先读取此文件做校验,不建议手动修改内容。

faiss_index_entities.index:

实体向量索引文件,FAISS 格式的二进制文件。存储知识图谱中所有实体(人物、地点、法宝等)对应的向量数据。图检索模式下,会先通过这个文件定位和问题最相关的实体节点,再顺着关系链路查找答案。

faiss_index_entities.index.meta.json:

实体向量索引的配置说明文件。记录实体索引的向量维度、构建参数等元信息,用于程序加载索引时的校验和配置匹配。

faiss_index_relationships.index:

关系向量索引文件,FAISS 格式的二进制文件。存储知识图谱中所有实体关系对应的向量数据,配合实体索引一起使用,用于快速匹配和问题相关的实体关联链路。

faiss_index_relationships.index.meta.json:

关系向量索引的配置说明文件。记录关系索引的向量维度、构建参数等元信息,保障程序能正确加载对应的向量索引。

kv_store_doc_status.json:

文档处理状态登记表。记录所有插入过的文档信息、处理进度(切块、抽实体、向量化等步骤状态)以及文档唯一标识,用于实现增量插入去重 ------ 文档内容不变时重复运行插入会自动跳过,避免重复调用大模型。

kv_store_entity_chunks.json:

实体 - 文本块对应表。记录每个实体分别来自哪些文本段落,也能反向查询某段文本中包含哪些实体。图检索时,找到相关实体后会通过这个文件定位到对应的原文片段,用于后续生成答案。

kv_store_full_docs.json:

完整原文存储库。存放你插入的所有原始文档全文,每个文档对应唯一的文档 ID,相当于整套系统的原始素材库。

kv_store_full_entities.json:

实体详情档案库。存放知识图谱里所有实体节点的完整信息,包括实体名称、实体类型、详细描述、来源出处等,和实体向量索引一一对应 ------ 索引存向量,这里存实体的真实文本内容。

kv_store_full_relations.json:

关系详情档案库。存放知识图谱里所有关系连线的完整信息,包括源实体、目标实体、关系描述、关系强度、关键词等,和关系向量索引一一对应。

kv_store_llm_response_cache.json:

大模型调用缓存文件。保存所有大模型的调用返回结果(比如实体抽取、关系抽取的输出)。重复处理相同内容时直接读取缓存,无需重复调用大模型,节省 API 费用和运行时间,执行清缓存操作会清空此文件内容。

kv_store_relation_chunks.json:

关系 - 文本块对应表。记录每条关系分别来自哪些文本段落,用于检索时追溯关系的原文出处,支撑答案溯源。

kv_store_text_chunks.json:

文本块原文存储库。存放文档拆分后所有小段文本的原文内容,每个文本块有唯一 ID,同时记录了它所属的文档、在原文中的位置。和文本块向量索引一一对应 ------ 索引存向量,这里存向量对应的真实文本内容。

运行下图红框的代码,就出现下图蓝框的文件,文件说明如上,它们就是LightRag保存的源文件、向量、json、关系等数据,下图红框的代码如果之前已经保存的数据不会进行添加

代码中用到了neo4j,需要配置环境变量,如下图红框的内容,neo4j的地址、用户名、密码信息

LightRag默认生成的内容是英文的,需要在配置文件中添加下图红框的内容SUMMARY_LANGUAGE=zh

或者写下图红框的内容让它生成中文内容

知识图谱,它的生成好不好取决于使用的模型

代码,下方的代码只是一个实例,实际场景不会写就去问ai,可以让ai基于下方的代码添加

python 复制代码
# 导入异步编程模块,用来写异步函数、管理事件循环,调用大模型/数据库时不卡住程序
import asyncio
# 导入操作系统模块,用来读取环境变量、操作文件路径
import os

# 导入 LightRAG 核心类和查询参数类
# LightRAG:是一个轻量级的图增强RAG框架,自动从文本里提取知识图谱,结合向量+图谱做问答
# QueryParam:查询参数对象,用来指定问答模式(向量检索/局部图谱/全局图谱/混合)
from lightrag import LightRAG, QueryParam
# 导入 LightRAG 封装好的 OpenAI 兼容大模型调用函数,带缓存功能
# 作用:只要是兼容 OpenAI 接口的大模型(比如通义千问),都可以用这个函数统一调用
from lightrag.llm.openai import openai_complete_if_cache, openai_embed
# 导入 dotenv 的环境变量加载函数,读取 .env 文件里的配置
from dotenv import load_dotenv
# 导入向量函数封装类,用来把自定义的向量模型包装成 LightRAG 能识别的格式
from lightrag.utils import EmbeddingFunc
# 导入流水线状态初始化函数,LightRAG 要求必须初始化处理流水线
from lightrag.kg.shared_storage import initialize_pipeline_status
# 导入 sentence-transformers 库的 SentenceTransformer,用来加载本地向量模型
from sentence_transformers import SentenceTransformer
# 导入 LightRAG 的提示词模板,这里默认使用内置模板
from lightrag.prompt import PROMPTS

# 执行加载 .env 文件,把里面的 API 密钥、接口地址等配置读到系统环境变量里
# 好处:敏感信息不写死在代码里,换环境直接改 .env 文件就行
load_dotenv()

# ========== 全局配置变量 ==========
# 本地向量模型的文件路径,这里用的是中文效果很好的 bge-large-zh-v1.5 模型
# 前面加 r 是原始字符串语法,避免路径里的反斜杠被当成转义符
model_name = r'D:\huanjing\ai模型\BAAI\bge-large-zh-v1___5'
# LightRAG 的工作目录,用来存缓存、日志、临时文件
WORKING_DIR = 'light_test'
# 加载本地向量模型到内存,后面生成向量都用这个模型
# 为什么用本地模型:1. 中文语义匹配效果更好;2. 不用调API,不花钱;3. 速度更快
model = SentenceTransformer(model_name)


# 自定义大模型调用函数(异步函数,前面加 async 表示是异步的)
# 作用:把通义千问的调用封装成 LightRAG 要求的格式,LightRAG 提取图谱、生成回答都会调用这个函数
# 固定写法,只需要改 model 参数和接口配置就行
async def llm_func(prompt, system_prompt=None, history_messages=[], **kwargs):
    """
    功能:调用大模型生成回复,带缓存功能,相同的问题会直接读缓存不用重复调用
    入参说明:
        prompt : str  当前给大模型的用户提示词(比如提取图谱的指令、问答的问题)
        system_prompt : str = None  系统提示词,给大模型设定角色规则,可选
        history_messages : list = []  历史对话消息列表,多轮对话用,默认空
        **kwargs :  其他额外参数,会透传给大模型接口
    返回值:大模型返回的文本结果
    预期数据类型:str
    """
    # 调用 LightRAG 封装好的 OpenAI 兼容调用函数
    # 为什么用这个:通义千问兼容 OpenAI 的接口格式,直接用这个封装就能接入,不用自己写请求代码
    return await openai_complete_if_cache(
        model="qwen-plus",              # 指定用的大模型名称,这里是通义千问 plus 版
        prompt=prompt,                  # 传入用户提示词
        system_prompt=system_prompt,    # 传入系统提示词
        history_messages=history_messages,  # 传入历史消息
        base_url=os.getenv("DASHSCOPE_BASE_URL"),  # 从环境变量读接口地址
        api_key=os.getenv("DASHSCOPE_API_KEY"),    # 从环境变量读 API 密钥
        **kwargs                        # 透传其他所有额外参数
    )


# 本地向量生成函数(异步)
# 作用:把文本转换成向量(embedding),供向量检索使用,这里用我们本地加载的 bge 模型
async def local_embedding_func(texts):
    """
    功能:批量把文本转换成向量数组
    入参说明:
        texts : List[str]  待转换的文本列表,一次可以传多条
    返回值:numpy 数组格式的向量,每一行对应一条文本的向量
    预期数据类型:numpy.ndarray
    """
    # 调用本地模型生成向量
    # convert_to_numpy=True:返回 numpy 数组格式,向量库能直接用
    # batch_size=2:每批处理2条文本,避免内存占满,电脑配置高可以调大
    embeddings = model.encode(texts, convert_to_numpy=True, batch_size=2)
    # 返回生成好的向量
    return embeddings


# 初始化 LightRAG 实例的异步函数
async def initialize_rag():
    """
    功能:创建并初始化 LightRAG 对象,配置好大模型、向量模型、存储方式、中文参数
    入参:无
    返回值:初始化完成的 LightRAG 实例
    预期数据类型:LightRAG
    """
    # 创建 LightRAG 实例,传入所有核心配置
    rag = LightRAG(
        # 指定大模型调用函数,LightRAG 提取图谱、生成回答都会调用这个函数
        llm_model_func=llm_func,
        # 配置向量模型:用 EmbeddingFunc 包装一下,告诉 LightRAG 向量的维度、最大长度、调用函数
        embedding_func=EmbeddingFunc(
            embedding_dim=1024,   # 向量维度,bge-large-zh-v1.5 输出的向量就是1024维,必须和模型对应
            max_token_size=8192,  # 单次最多处理的token数,超过会自动截断,根据模型能力设置
            func=local_embedding_func  # 传入我们自定义的本地向量生成函数
        ),
        # ========== 向量存储方式配置 ==========
        # 作用:存储文本块、实体、关系的向量数据,负责语义相似度检索
        # 可选值及说明:
        #   NanoVectorDBStorage   :默认值,纯Python实现零依赖,本地文件持久化,适合小数据量快速调试
        #   FaissVectorDBStorage  :Facebook开源高性能向量库,检索快、省内存,单机性能优秀,无需额外服务(当前使用)
        #   PGVectorStorage       :PostgreSQL + pgvector插件,适合已有PG技术栈的生产环境,数据统一管理
        #   MilvusVectorDBStorage :工业级分布式向量数据库,适合十万/百万级以上大数据量生产环境
        #   QdrantVectorDBStorage :易用性强,支持丰富元数据过滤,部署简单,适合中大数据量
        #   ChromaVectorDBStorage :轻量向量库,LangChain生态兼容好,适合快速原型验证
        #   MongoVectorDBStorage  :基于MongoDB向量能力,适合已有MongoDB技术栈的团队,不用额外引入组件
        # 注意:切换非默认存储需要提前安装对应的依赖包,否则会报导入错误
        vector_storage="FaissVectorDBStorage",
        # ========== 知识图谱存储方式配置 ==========
        # 作用:存储知识图谱的实体节点和关系边,负责局部/全局图谱推理、多跳关联查询
        # 可选值及说明:
        #   NetworkXStorage :默认值,纯Python内存图计算库,本地文件持久化,零依赖,适合小数据量调试
        #   Neo4JStorage    :工业级主流原生图数据库,支持Cypher查询语言,功能完善生态成熟(当前使用)
        #   PGGraphStorage  :基于PostgreSQL的AGE图插件,可与pgvector共用数据库,适合统一PG技术栈
        #   MemgraphStorage :高性能内存图数据库,兼容Cypher语法,查询速度极快,适合高实时性场景
        # 注意:注释里说明,开启window相关功能时,用asyncio.run启动会报「Event loop is closed」错误
        #      所以后面主程序用 get_event_loop 的方式启动协程来规避这个问题
        graph_storage='Neo4JStorage',
        # 核心:通过 addon_params 指定输出语言和中文实体类型
        addon_params={
            "language": "Simplified Chinese",  # 输出语言:简体中文
            # 通用7类,这个可以让ai根据文档生成一个特定的类型,也可以使用LightRag默认的
            # "entity_types": ["人物", "组织", "地点", "事件", "物品", "概念", "时间"]  # 中文实体分类

        }
    )
    # IMPORTANT: Both initialization calls are required!
    # 1. 初始化所有存储后端(向量库、图谱库),建立连接、创建必要的数据结构
    await rag.initialize_storages()
    # 2. 初始化文本处理流水线的状态,保证图谱提取的全流程能正常运行
    await initialize_pipeline_status()

    # 返回初始化好的 RAG 实例
    return rag


# 主函数:程序的核心业务逻辑都在这里
async def main():
    # 先调用初始化函数,拿到 LightRAG 实例
    rag = await initialize_rag()

    # 第一次执行解开下方注释的代码,初始化索引(源文档、向量、知识图谱)
    # 打开本地西游记文本文件,只读模式,utf-8编码防止中文乱码
    with open('西游记.txt', 'r', encoding='utf-8') as f:
        data = f.read()
    # 创建异步任务:把文本插入到 RAG 系统里(自动切分、提取图谱、生成向量、存入存储)
    task = loop.create_task(rag.ainsert(data))
    # 等待插入任务执行完成
    await asyncio.gather(task)


    # 清全部缓存
    # 作用:清空 LightRAG 的所有缓存(大模型调用缓存、中间结果缓存),改了提示词/配置后可以清一下重新跑
    # await rag.aclear_cache()

    # da1 = await rag.aquery("谁告诉孙悟空有金箍棒的", param=QueryParam(mode="naive"))  # 传统向量检索
    # print('向量检索结果:', da1)
    # print("---------------------------------------------------------------------------------------------------------")
    # da2 = await rag.aquery("谁告诉孙悟空有金箍棒的", param=QueryParam(mode="local"))  # 底层精确
    # print('底层精确结果:', da2)
    # print("---------------------------------------------------------------------------------------------------------")
    # da3 = await rag.aquery("谁告诉孙悟空有金箍棒的", param=QueryParam(mode="global"))  # 高层全局
    # print('高层全局结果:', da3)
    # print("---------------------------------------------------------------------------------------------------------")
    # da4 = await rag.aquery("谁告诉孙悟空有金箍棒的", param=QueryParam(mode="hybrid"))  # 推荐融合
    # print('混合检索结果:', da4)

    # 增量操作  插入新的数据  重新插入后续的内容
    # 作用:后续有新的文档,可以继续调用 ainsert 追加进去,不用重建整个知识库
    # await rag.ainsert(data)


if __name__ == "__main__":
    # asyncio.run(main())
    # 为什么不用 asyncio.run:Windows 环境下,LightRAG 内部多协程交互后容易出现 RuntimeError: Event loop is closed 报错
    # 替代方案:手动获取事件循环,运行主函数,兼容性更好,能规避上述报错
    loop = asyncio.get_event_loop()
    loop.run_until_complete(main())