LightRAG 源码阅读

项目地址:LightRAG 本文分析基于 Git commit 86195c613e0ced0e2fe8e1293b7d5c952359a7a1 版本

技术栈

  • Python 3.10+
  • FastAPI
  • 键值存储 (KV Storage):
    • JSON文件存储
    • Redis
    • PostgreSQL
    • MongoDB
  • 图数据库 (Graph Storage):
    • NetworkX (内存图)
    • Neo4j
    • PostgreSQL (图扩展)
    • MongoDB
    • Memgraph
  • 向量数据库 (Vector Storage):
    • NanoVectorDB (轻量级)
    • Milvus
    • Faiss
    • Qdrant
    • PostgreSQL (pgvector扩展)
    • MongoDB

目录结构

LightRAG/
├── lightrag/                    # 【核心】主要代码包
│   ├── __init__.py             # 包初始化,导出主要类
│   ├── lightrag.py             # 【核心】主要的LightRAG类定义
│   ├── operate.py              # 【核心】核心操作逻辑(查询、插入等)
│   ├── base.py                 # 【核心】基础抽象类定义
│   ├── types.py                # 类型定义
│   ├── utils.py                # 【核心】工具函数和缓存机制
│   ├── prompt.py               # 【核心】提示词模板
│   ├── namespace.py            # 命名空间定义
│   ├── constants.py            # 常量定义
│   ├── exceptions.py           # 异常定义
│   ├── rerank.py               # 重排序功能
│   ├── utils_graph.py          # 图相关工具
│   │
│   ├── kg/                     # 【核心】知识图谱存储层
│   │   ├── __init__.py         # 存储实现映射
│   │   ├── json_kv_impl.py     # JSON键值存储实现
│   │   ├── postgres_impl.py    # PostgreSQL存储实现
│   │   ├── mongo_impl.py       # MongoDB存储实现
│   │   ├── redis_impl.py       # Redis存储实现
│   │   ├── neo4j_impl.py       # Neo4j图数据库实现
│   │   ├── milvus_impl.py      # Milvus向量数据库实现
│   │   ├── faiss_impl.py       # Faiss向量数据库实现
│   │   ├── qdrant_impl.py      # Qdrant向量数据库实现
│   │   └── shared_storage.py   # 共享存储逻辑
│   │
│   ├── llm/                    # 【核心】大语言模型集成层
│   │   ├── openai.py           # OpenAI API集成
│   │   ├── azure_openai.py     # Azure OpenAI集成
│   │   ├── ollama.py           # Ollama本地模型集成
│   │   ├── zhipu.py            # 智谱AI集成
│   │   ├── hf.py               # HuggingFace模型集成
│   │   ├── nvidia_openai.py    # Nvidia AI集成
│   │   └── llama_index_impl.py # LlamaIndex集成
│   │
│   ├── api/                    # 【核心】Web服务和API
│   │   ├── lightrag_server.py  # FastAPI服务器主文件
│   │   ├── config.py           # API配置
│   │   ├── auth.py             # 认证机制
│   │   ├── routers/            # API路由
│   │   │   ├── query_routes.py # 查询API路由
│   │   │   ├── document_routes.py # 文档管理API
│   │   │   ├── graph_routes.py # 图可视化API
│   │   │   └── ollama_api.py   # Ollama兼容API
│   │   └── webui/              # 前端静态文件
│   │
│   └── tools/                  # 辅助工具
│       └── lightrag_visualizer/ # 可视化工具
├── lightrag_webui/             # 【核心】前端Web界面
│   ├── src/                    # React源代码
│   ├── package.json            # 前端依赖配置
│   ├── vite.config.ts          # 构建配置
│   └── tailwind.config.js      # 样式配置
├── examples/                   # 示例代码
│   ├── lightrag_openai_demo.py # OpenAI使用示例
│   ├── lightrag_ollama_demo.py # Ollama使用示例
│   └── unofficial-sample/      # 社区贡献示例
├── tests/                      # 测试代码
│   ├── test_lightrag_ollama_chat.py
│   └── test_graph_storage.py
├── docs/                       # 文档
├── k8s-deploy/                 # Kubernetes部署配置
├── reproduce/                  # 论文复现代码
├── pyproject.toml              # 【核心】Python项目配置
├── docker-compose.yml          # Docker部署配置
├── env.example                 # 环境变量模板
└── README.md                   # 项目说明文档

关系图谱

    graph TB
    subgraph "用户交互层"
        WebUI[Web UI<br/>lightrag_webui/<br/>React+TypeScript前端界面]
        CLI[CLI/Python SDK<br/>直接调用LightRAG]
        API[REST API<br/>FastAPI服务器]
    end

    subgraph "API服务层 lightrag/api/"
        Server[lightrag_server.py<br/>FastAPI主服务器<br/>身份验证+配置管理]
        DocRouter[document_routes.py<br/>文档增删改查]
        QueryRouter[query_routes.py<br/>查询接口]
        GraphRouter[graph_routes.py<br/>图谱管理]
        OllamaAPI[ollama_api.py<br/>兼容Ollama的聊天接口]
    end

    subgraph "核心引擎层 lightrag/"
        Main[lightrag.py<br/>核心LightRAG类<br/>文档索引+查询协调]
        Base[base.py<br/>基础抽象类定义<br/>Storage/QueryParam/DocStatus]
        Operate[operate.py<br/>核心操作函数<br/>分块+实体提取+查询]
        Namespace[namespace.py<br/>命名空间管理<br/>多租户支持]
    end

    subgraph "LLM适配器层 lightrag/llm/"
        OpenAI[openai.py<br/>OpenAI适配器]
        Ollama[ollama.py<br/>Ollama适配器]
        Azure[azure_openai.py<br/>Azure适配器]
        Anthropic[anthropic.py<br/>Claude适配器]
        Others[其他LLM适配器<br/>HuggingFace/Bedrock等]
    end

    subgraph "知识图谱存储层 lightrag/kg/"
        GraphStorage[图存储实现<br/>neo4j/networkx/mongo]
        VectorStorage[向量存储实现<br/>faiss/milvus/qdrant]
        KVStorage[KV存储实现<br/>json/redis/postgres]
        DocStatus[文档状态存储<br/>json_doc_status_impl.py]
        SharedStorage[shared_storage.py<br/>共享存储管理+锁机制]
    end

    subgraph "工具与功能模块"
        Prompt[prompt.py<br/>提示词模板管理]
        Rerank[rerank.py<br/>重排序模块]
        Utils[utils.py<br/>通用工具函数<br/>分词/哈希/异步]
        UtilsGraph[utils_graph.py<br/>图处理工具]
        Types[types.py<br/>数据类型定义<br/>KnowledgeGraph等]
        Constants[constants.py<br/>常量配置]
    end

    subgraph "部署与配置"
        Docker[docker-compose.yml<br/>Docker部署配置]
        K8s[k8s-deploy/<br/>Kubernetes部署]
        Config[config.ini.example<br/>env.example<br/>配置文件模板]
    end

    subgraph "测试与示例"
        Tests[tests/<br/>Pytest测试套件<br/>存储+API+功能测试]
        Examples[examples/<br/>各种LLM使用示例<br/>图可视化等]
        Docs[docs/<br/>算法文档<br/>部署指南]
    end

    %% 用户交互层连接
    WebUI -->|HTTP请求| API
    CLI -->|直接调用| Main
    API -->|路由| Server

    %% API层内部连接
    Server --> DocRouter
    Server --> QueryRouter
    Server --> GraphRouter
    Server --> OllamaAPI

    %% API到核心层
    DocRouter -->|文档操作| Main
    QueryRouter -->|查询请求| Main
    GraphRouter -->|图谱操作| Main
    OllamaAPI -->|聊天请求| Main

    %% 核心层内部
    Main --> Base
    Main --> Operate
    Main --> Namespace
    Operate --> Prompt
    Operate --> Rerank

    %% 核心层到LLM
    Main -->|调用LLM| OpenAI
    Main -->|调用LLM| Ollama
    Main -->|调用LLM| Azure
    Main -->|调用LLM| Anthropic
    Operate -->|实体提取/查询生成| Others

    %% 核心层到存储层
    Main -->|存储操作| GraphStorage
    Main -->|向量检索| VectorStorage
    Main -->|键值存储| KVStorage
    Main -->|文档状态| DocStatus
    Namespace --> SharedStorage

    %% 工具模块支持
    Main --> Utils
    Main --> Types
    Main --> Constants
    Operate --> UtilsGraph
    GraphStorage --> UtilsGraph

    %% 配置与部署
    Server -.配置加载.-> Config
    Docker -.容器化部署.-> Server
    K8s -.K8s部署.-> Server

    %% 测试与文档
    Tests -.测试覆盖.-> Main
    Tests -.测试覆盖.-> Server
    Examples -.使用示例.-> Main

    style Main fill:#ff9999,stroke:#333,stroke-width:3px
    style Server fill:#99ccff,stroke:#333,stroke-width:3px
    style GraphStorage fill:#99ff99,stroke:#333,stroke-width:2px
    style VectorStorage fill:#99ff99,stroke:#333,stroke-width:2px
  

核心数据模型

  • LightRAG (lightrag.py):系统主控制器和统一入口
    • 协调所有组件的工作
    • 提供统一的API接口
    • 管理配置和生命周期
    • 控制文档插入和查询流程
  • BaseKVStorage (base.py):键值存储抽象基类
    • 定义键值存储的统一接口
    • 管理文档、文本块、LLM缓存等结构化数据
    • 提供upsert、get、filter等基础操作
  • BaseVectorStorage (base.py):向量存储抽象基类
    • 管理实体、关系、文本块的向量嵌入
    • 提供向量相似度查询功能
    • 支持批量向量操作
  • BaseGraphStorage (base.py):图存储抽象基类
    • 管理知识图谱的节点和边
    • 提供图遍历和查询功能
    • 支持实体关系的图结构操作
  • EmbeddingFunc (utils.py):嵌入函数封装器
    • 封装不同的嵌入模型
    • 提供统一的文本向量化接口
    • 管理嵌入维度和批处理
  • CacheData (utils.py):缓存数据结构
    • 封装LLM响应缓存信息
    • 管理缓存类型和元数据
    • 支持缓存的序列化和反序列化
  • DocumentManager (api/routers/document_routes.py):文档管理器
    • 处理文档上传和管理
    • 管理文档状态和元数据
    • 支持批量文档处理
  •  Entity & Relation (隐式数据结构):知识图谱核心数据
    • Entity: 表示知识图谱中的实体节点
    • Relation: 表示实体间的关系边
    • 存储在各种存储后端中
  • TextChunk (隐式数据结构):文本分块数据
    • 存储文档分割后的文本片段
    • 包含位置信息、token数量等元数据
    • 作为RAG检索的基本单元

数据模型关系图

    classDiagram
    class LightRAG {
        +working_dir: str
        +llm_model_func: callable
        +embedding_func: EmbeddingFunc
        +insert(text)
        +query(query, param)
        +initialize_storages()
    }
    
    class QueryParam {
        +mode: str
        +top_k: int
        +max_tokens: int
        +conversation_history: list
        +stream: bool
        +response_type: str
    }
    
    class EmbeddingFunc {
        +embedding_dim: int
        +func: callable
        +embed(texts)
    }
    
    class BaseKVStorage {
        <<abstract>>
        +namespace: str
        +workspace: str
        +get_by_id(id)
        +upsert(data)
        +filter_keys(keys)
    }
    
    class BaseVectorStorage {
        <<abstract>>
        +embedding_func: EmbeddingFunc
        +query(query_embedding)
        +upsert(data)
    }
    
    class BaseGraphStorage {
        <<abstract>>
        +upsert_node(node_id, node_data)
        +upsert_edge(src_id, tgt_id, edge_data)
        +get_neighbors(node_id)
    }
    
    class CacheData {
        +return_value: str
        +cache_type: str
        +chunk_id: str
        +original_prompt: str
        +queryparam: dict
    }
    
    class DocumentManager {
        +input_dir: str
        +workspace: str
        +upload_document(file)
        +get_document_status(doc_id)
    }
    
    class Entity {
        +entity_name: str
        +entity_type: str
        +description: str
        +source_id: str
    }
    
    class Relation {
        +src_id: str
        +tgt_id: str
        +description: str
        +keywords: str
        +weight: float
    }
    
    class TextChunk {
        +content: str
        +tokens: int
        +chunk_order_index: int
        +full_doc_id: str
        +file_path: str
    }
    
    %% 核心关系
    LightRAG --> QueryParam : uses
    LightRAG --> EmbeddingFunc : contains
    LightRAG --> BaseKVStorage : manages
    LightRAG --> BaseVectorStorage : manages
    LightRAG --> BaseGraphStorage : manages
    LightRAG --> DocumentManager : uses
    
    %% 存储关系
    BaseKVStorage --> TextChunk : stores
    BaseKVStorage --> CacheData : stores
    BaseVectorStorage --> Entity : stores embeddings
    BaseVectorStorage --> Relation : stores embeddings
    BaseGraphStorage --> Entity : stores nodes
    BaseGraphStorage --> Relation : stores edges
    
    %% 数据关系
    Entity --> Relation : connected by
    TextChunk --> Entity : contains
    TextChunk --> Relation : contains
    
    %% 功能关系
    EmbeddingFunc --> BaseVectorStorage : provides embeddings
    QueryParam --> BaseVectorStorage : configures query
    CacheData --> BaseKVStorage : cached in
  

核心储存数据

    graph LR

subgraph "📄 单个文档"

A[原始文档内容]

end

subgraph "💾 KV数据库 (4种存储)"

B1[完整文档<br/>full_docs]

B2[文本分块<br/>text_chunks]

B3[实体列表<br/>full_entities]

B4[关系列表<br/>full_relations]

end

subgraph "🔍 向量数据库 (3种存储)"

C1[分块向量<br/>chunks_vdb]

C2[实体向量<br/>entities_vdb]

C3[关系向量<br/>relationships_vdb]

end

subgraph "🕸️ 图数据库 (1种存储)"

D1[实体节点 + 关系边<br/>chunk_entity_relation_graph]

end

subgraph "📋 状态数据库 (1种存储)"

E1[处理状态<br/>doc_status]

end

%% 连接关系

A --> B1

A --> B2

A --> B3

A --> B4

A --> C1

A --> C2

A --> C3

A --> D1

A --> E1

%% 样式

classDef kvStyle fill:#f3e5f5,stroke:#4a148c,stroke-width:2px

classDef vectorStyle fill:#e8f5e8,stroke:#1b5e20,stroke-width:2px

classDef graphStyle fill:#fff3e0,stroke:#e65100,stroke-width:2px

classDef statusStyle fill:#fce4ec,stroke:#880e4f,stroke-width:2px

classDef docStyle fill:#e1f5fe,stroke:#01579b,stroke-width:2px

class A docStyle

class B1,B2,B3,B4 kvStyle

class C1,C2,C3 vectorStyle

class D1 graphStyle

class E1 statusStyle
  

存储包括几类数据

  • KV 数据库 
    • 原始完整文档
    • 原始 chunks
    • 实体汇总列表
    • 关系汇总列表
    • LLM响应缓存
  • 向量数据库
    • 分块向量
    • 实体向量
    • 关系向量
  • 图数据库
    • 实体节点和关系边
  • 文档状态数据库
    • 文档处理状态和元数据

核心流程概述

文档插入流程

  • 文档预处理: 文本清理、编码转换、格式标准化
  • 智能分块: 基于token数量和语义边界进行文档分割
  • 实体关系提取: 使用LLM从文本块中提取实体和关系
  • 知识图谱构建: 将提取的实体关系构建成图结构
  • 向量化存储: 生成实体、关系、文本块的嵌入向量并存储
  • 元数据管理: 记录文档状态、处理进度、错误信息

实体关系提取流程

  • 文本预处理: 清理和标准化输入文本
  • 提示词构建: 基于模板生成实体关系提取提示
  • LLM调用: 使用大语言模型进行实体关系识别
  • 结果解析: 解析LLM输出的结构化实体关系数据
  • 数据验证: 验证提取结果的格式和完整性
  • 增量更新: 将新提取的实体关系合并到现有知识图谱

知识图谱管理流程

  • 实体管理: 创建、编辑、删除、合并实体节点
  • 关系管理: 建立、修改、删除实体间的关系边
  • 图遍历: 基于图结构进行邻居查找和路径搜索
  • 图更新: 增量更新图结构,保持数据一致性
  • 图可视化: 生成图的可视化表示供前端展示

查询处理流程

  • 查询解析: 分析用户查询意图和参数配置
  • 多模式检索:
    • Local模式: 基于实体的局部上下文检索
    • Global模式: 基于关系的全局知识检索
    • Hybrid模式: 结合Local和Global的混合检索
    • Naive模式: 简单的向量相似度检索
    • Mix模式: 整合知识图谱和向量检索
  • 上下文组装: 将检索结果组织成结构化上下文
  • LLM生成: 基于上下文生成最终答案
  • 结果后处理: 格式化输出、添加引用信息

向量检索流程

  • 查询向量化: 将用户查询转换为嵌入向量
  • 相似度计算: 在向量空间中计算相似度分数
  • 结果排序: 按相似度分数对检索结果排序
  • 阈值过滤: 根据配置的阈值过滤低质量结果
  • 重排序: 可选的使用重排序模型优化结果顺序
  • 结果聚合: 合并来自不同向量存储的检索结果

核心流程详解

文档插入流程

理解文档插入流程需要结合 ‘核心储存数据’ 章节理解

    sequenceDiagram
    participant User as 用户/API
    participant LightRAG as LightRAG.insert()
    participant DocMgr as DocumentManager
    participant Chunker as operate.py::chunking
    participant Extractor as operate.py::extract
    participant LLM as llm/模型实现
    participant KVStore as BaseKVStorage
    participant VectorStore as BaseVectorStorage
    participant GraphStore as BaseGraphStorage
    
    Note over User, GraphStore: 阶段1: 文档预处理 (蓝色) - 清理文档并生成唯一标识
    rect rgb(225, 245, 254)
        User->>+LightRAG: insert(text, ids, file_paths)
        Note right of User: 用户提供文档内容、可选ID和文件路径
        
        LightRAG->>+DocMgr: process_document(text)
        Note right of LightRAG: 启动文档处理流程,检查重复和状态
        
        DocMgr->>DocMgr: sanitize_text_for_encoding()
        Note right of DocMgr: 清理特殊字符,确保文本编码安全
        
        DocMgr->>DocMgr: compute_mdhash_id()
        Note right of DocMgr: 基于内容生成MD5哈希作为文档唯一ID
        
        DocMgr->>KVStore: upsert(full_docs)
        Note right of DocMgr: 将完整文档存储到KV存储,包含元数据
        
        DocMgr-->>-LightRAG: doc_id, status
        Note left of DocMgr: 返回文档ID和处理状态
    end
    
    Note over User, GraphStore: 阶段2: 智能分块 (紫色) - 按token和语义边界分割文档
    rect rgb(243, 229, 245)
        LightRAG->>+Chunker: chunking_by_token_size()
        Note right of LightRAG: 调用分块函数,传入token限制参数
        
        Chunker->>Chunker: tokenizer.encode()
        Note right of Chunker: 使用tiktoken将文本转换为token序列
        
        Chunker->>Chunker: split_by_sep_with_overlap()
        Note right of Chunker: 按分隔符分割,保持重叠以保证语义连续性
        
        loop 每个文本块
            Chunker->>Chunker: compute_mdhash_id(chunk)
            Note right of Chunker: 为每个文本块生成唯一ID
            
            Chunker->>Chunker: create_chunk_metadata()
            Note right of Chunker: 创建块元数据:token数、顺序、所属文档等
        end
        
        Chunker-->>-LightRAG: chunks_data[]
        Note left of Chunker: 返回包含所有块数据的数组
        
        LightRAG->>KVStore: upsert(text_chunks)
        Note right of LightRAG: 批量存储文本块到KV存储
    end
    
    Note over User, GraphStore: 阶段3: 知识提取 (绿色) - 使用LLM提取实体关系并向量化
    rect rgb(232, 245, 232)
        LightRAG->>+Extractor: extract_entities_and_relations()
        Note right of LightRAG: 启动知识提取流程,处理所有文本块
        
        loop 每个文本块
            Extractor->>+LLM: llm_model_func(extract_prompt)
            Note right of Extractor: 发送结构化提示词到LLM进行实体关系提取
            
            LLM-->>-Extractor: entities_relations_text
            Note left of LLM: 返回格式化的实体关系文本
            
            Extractor->>Extractor: parse_entities_relations()
            Note right of Extractor: 解析LLM输出,提取实体和关系结构化数据
            
            Extractor->>Extractor: embedding_func(entity_text)
            Note right of Extractor: 为实体描述生成向量嵌入
            
            Extractor->>Extractor: embedding_func(relation_text)
            Note right of Extractor: 为关系描述生成向量嵌入
        end
        
        Extractor-->>-LightRAG: entities[], relations[]
        Note left of Extractor: 返回提取的实体和关系数组
    end
    
    Note over User, GraphStore: 阶段4: 存储持久化 (橙色) - 并行存储到多个后端
    rect rgb(255, 243, 224)
        par 并行存储操作
            LightRAG->>VectorStore: upsert(entity_embeddings)
            Note right of LightRAG: 存储实体向量到向量数据库
        and
            LightRAG->>VectorStore: upsert(relation_embeddings)
            Note right of LightRAG: 存储关系向量到向量数据库
        and
            LightRAG->>VectorStore: upsert(chunk_embeddings)
            Note right of LightRAG: 存储文本块向量到向量数据库
        and
            LightRAG->>GraphStore: upsert_node(entities)
            Note right of LightRAG: 将实体作为节点存储到图数据库
        and
            LightRAG->>GraphStore: upsert_edge(relations)
            Note right of LightRAG: 将关系作为边存储到图数据库
        and
            LightRAG->>KVStore: upsert(full_entities)
            Note right of LightRAG: 存储完整实体信息到KV存储
        and
            LightRAG->>KVStore: upsert(full_relations)
            Note right of LightRAG: 存储完整关系信息到KV存储
        end
        
        Note over LightRAG: 等待所有并行存储操作完成
        
        LightRAG->>LightRAG: _insert_done()
        Note right of LightRAG: 执行插入完成后的清理和状态更新
        
        LightRAG-->>-User: 插入完成
        Note left of LightRAG: 向用户返回插入成功状态
    end
  

上面的流程图展示了文档插入的核心流程,除此之外,还有一些工程性的流程需要关注一下

  • 数据修复 _validate_and_fix_document_consistency: 在文档处理流水线开始前调用,检查并修复doc_status(文档状态)和full_docs(完整文档)之间的数据不一致问题。这种不一致可能会出现在各种异常如程序异常退出、后端储存故障等。
  • LLM Response Cache 策略
  • 并发处理策略

分块算法

分块是在 token 格式下进行的,先将字符转使用 Tokenizer 转成 token,再进行分块,但是最终返回出去 content 会使用 Tokenizer 转成为原文

chunk 格式

  • tokens:token 数量
  • content:原始文本
  • chunk_order_index:分块的顺序
{
	"tokens": min(max_token_size, len(tokens) - start),
	"content": chunk_content.strip(),
	"chunk_order_index": index,
}

支持三种分块模式

  • 按分隔符分割:
    • 纯语义分割:split_by_character_only = true 完全按分隔符切分
  • 按 token size 分割:
    • 每块大小控制在max_token_size, 通过 overlap_token_size 控制分块之间重叠区间,保持语义连续性
  • 混合分割:在分隔符分割后的子块基础上,如果超过了 max_token_size,再进行 token size 分割
def chunking_by_token_size(
    tokenizer: Tokenizer,
    content: str,
    split_by_character: str | None = None,
    split_by_character_only: bool = False,
    overlap_token_size: int = 128,
    max_token_size: int = 1024,
) -> list[dict[str, Any]]:
    tokens = tokenizer.encode(content)
    results: list[dict[str, Any]] = []
    if split_by_character:
        raw_chunks = content.split(split_by_character)
        new_chunks = []
        if split_by_character_only:
            for chunk in raw_chunks:
                _tokens = tokenizer.encode(chunk)
                new_chunks.append((len(_tokens), chunk))
        else:
            for chunk in raw_chunks:
                _tokens = tokenizer.encode(chunk)
                if len(_tokens) > max_token_size:
                    for start in range(
                        0, len(_tokens), max_token_size - overlap_token_size
                    ):
                        chunk_content = tokenizer.decode(
                            _tokens[start : start + max_token_size]
                        )
                        new_chunks.append(
                            (min(max_token_size, len(_tokens) - start), chunk_content)
                        )
                else:
                    new_chunks.append((len(_tokens), chunk))
        for index, (_len, chunk) in enumerate(new_chunks):
            results.append(
                {
                    "tokens": _len,
                    "content": chunk.strip(),
                    "chunk_order_index": index,
                }
            )
    else:
        for index, start in enumerate(
            range(0, len(tokens), max_token_size - overlap_token_size)
        ):
            chunk_content = tokenizer.decode(tokens[start : start + max_token_size])
            results.append(
                {
                    "tokens": min(max_token_size, len(tokens) - start),
                    "content": chunk_content.strip(),
                    "chunk_order_index": index,
                }
            )
    return results

插入向量数据库

不同的向量数据库的 upsert 流程有不同的实现,这里以默认 NanoVectorDB 实现讲解。 包括下面几个关键流程

  • (批量)嵌入:将原始文本 embedding 转成向量,批量操作以提升效率
  • 向量压缩流程:
if len(embeddings) == len(list_data):
	for i, d in enumerate(list_data):
		# 步骤1:降低精度 - Float64/32 → Float16
		vector_f16 = embeddings[i].astype(np.float16)
		# 步骤2:二进制压缩 - 使用zlib算法
		compressed_vector = zlib.compress(vector_f16.tobytes())
		# 步骤3:编码存储 - 二进制 → Base64字符串
		encoded_vector = base64.b64encode(compressed_vector).decode("utf-8")
		# 步骤4:双重存储
		d["vector"] = encoded_vector # 压缩后的版本,用于持久化存储
		d["__vector__"] = embeddings[i] # 原始精度版本,用于内存计算
	client = await self._get_client()
	results = client.upsert(datas=list_data)
	return results

知识图谱构建

知识图谱使用图数据库(默认: NetworkX)建立原始文档之间的实体的关系,关系的提取包括两轮的 LLM 任务:

  • 第一轮提取出基本的实体和关系。
  • 第二轮 Gleaning 可选,基于第一轮的输出再进行一次 二次输出
    • 提高召回率:发现遗漏的实体和关系
    • 提高准确性:修正格式错误和不完整的提取 在进行两轮任务后,会合并两轮输出的实体与关系,选择出描述最详细的结果。 在存储进数据库的时候,还会根据库里已经存在的实体与关系,再进行合并处理。

上下文与提示词

系统中预定义了实体类型在,不在此列表中的会被归类为 Other 类型

[
    "Person",
    "Creature",
    "Organization",
    "Location",
    "Event",
    "Concept",
    "Method",
    "Content",
    "Data",
    "Artifact",
    "NaturalObject",
]
System Prompt
---角色---

您是知识图谱专家,负责从输入文本中提取实体和关系。

---说明---

1. **实体提取与输出:**

* **识别:** 明确识别输入文本中的定义清晰且具有意义的实体。

* **实体详细信息:** 对于每个识别的实体,提取以下信息:

* `entity_name`:实体的名称。如果实体名称不区分大小写,则将每个重要单词的首字母大写(标题大小写)。在整个提取过程中确保**命名一致**

* `entity_type`:使用以下类型之一对实体进行分类:`{entity_types}`。如果提供的实体类型都不适用,则不要添加新的实体类型,将其归类为“其他”。

* `entity_description`:根据输入文本中提供的信息,提供关于实体属性和活动的简洁而全面的描述。

* **输出格式 - 实体:** 每个实体输出4个字段,由`{tuple_delimiter}`分隔,在同一行上。第一个字段必须是字面字符串`entity`

* 格式:`entity{tuple_delimiter}entity_name{tuple_delimiter}entity_type{tuple_delimiter}entity_description`

2. **关系提取与输出:**

* **识别:** 识别先前提取实体之间直接、明确且具有意义的联系。

* **N元关系分解** 如果单个语句描述了涉及两个以上实体(N元关系)的关系,将其分解为多个二元(两个实体)关系对进行单独描述。

* **示例:** 对于“AliceBob和Carol合作完成了项目X”,提取二元关系,如“Alice与项目X合作”、“Bob与项目X合作”和“Carol与项目X合作”,或者“Alice与Bob合作”,基于最合理的二元解释。

* **关系详细信息:** 对于每个二元关系,提取以下字段:

* `source_entity`:源实体的名称。确保与实体提取**命名一致**。如果名称不区分大小写,则将每个重要单词的首字母大写(标题大小写)。

* `target_entity`:目标实体的名称。确保与实体提取**命名一致**。如果名称不区分大小写,则将每个重要单词的首字母大写(标题大小写)。

* `relationship_keywords`:一个或多个总结关系整体性质、概念或主题的高级关键词。此字段中的多个关键词必须用逗号`,`分隔。**不要在此字段中使用`{tuple_delimiter}`分隔多个关键词。**

* `relationship_description`:对源实体和目标实体之间关系性质的简洁解释,提供其连接的明确理由。

* **输出格式 - 关系:** 每个关系输出5个字段,由`{tuple_delimiter}`分隔,在同一行上。第一个字段必须是字面字符串`relation`

* 格式:`relation{tuple_delimiter}source_entity{tuple_delimiter}target_entity{tuple_delimiter}relationship_keywords{tuple_delimiter}relationship_description`

3. **分隔符使用协议:**

* `{tuple_delimiter}`是一个完整、原子的标记,**不得填充内容**。它仅作为字段分隔符。

* **错误示例:** `entity{tuple_delimiter}Tokyo<|location|>Tokyo is the capital of Japan.`

* **正确示例:** `entity{tuple_delimiter}Tokyo{tuple_delimiter}location{tuple_delimiter}Tokyo is the capital of Japan.`

4. **关系方向与重复:**

* 将所有关系视为**无向**,除非明确说明否则。交换源实体和目标实体对于无向关系不构成新的关系。

* 避免输出重复的关系。

5. **输出顺序与优先级:**

* 首先输出所有提取的实体,然后输出所有提取的关系。

* 在关系列表中,优先输出对输入文本核心意义**最显著**的关系。

6. **上下文与客观性:**

* 确保所有实体名称和描述都使用**第三人称**

* 明确命名主题或对象;**避免使用代词**,如`this article``this paper``our company``I``you``he/she`

7. **语言与专有名词:**

* 整个输出(实体名称、关键词和描述)必须使用`{language}`

* 如果没有提供合适的、广泛接受的翻译或会导致歧义,则应保留专有名词(例如个人名称、地点名称、组织名称)的原语言。

8. **完成信号:** 在所有实体和关系完全提取并输出后,仅输出字面字符串`{completion_delimiter}`

---示例---

{examples}

---待处理的真实数据---

<输入>

实体类型:[{entity_types}]

文本:
{input_text}
User Prompt
---任务---

从待处理输入文本中提取实体和关系。

---说明---

1. **严格遵循格式:** 严格遵循系统提示中指定的实体和关系列表的所有格式要求,包括输出顺序、字段分隔符和专有名词处理。

2. **仅输出内容:** 仅输出提取的实体和关系列表。不要包括任何介绍性或结论性评论、解释或列表前后附加的任何文本。

3. **完成信号:** 在提取并呈现所有相关实体和关系后,输出 `{completion_delimiter}` 作为最后一行。

4. **输出语言:** 确保输出语言为 {language}。专有名词(例如,人名、地名、组织名称)必须保持其原始语言,不得翻译。

<输出>
Continue Extraction Prompt
---任务---

根据上一次提取任务,从输入文本中识别和提取任何**遗漏或格式错误**的实体和关系。

---说明---

1. **严格遵守系统格式:** 严格遵循系统说明中规定的实体和关系列表的所有格式要求,包括输出顺序、字段分隔符和专有名词处理。

2. **关注修正/添加:**

* **不要**重新输出在上一次任务中**正确且完整**提取的实体和关系。

* 如果在上一次任务中**遗漏**了实体或关系,现在根据系统格式提取并输出它。

* 如果在上一次任务中实体或关系**截断、字段缺失或格式不正确**,以指定格式重新输出*修正和完整*版本。

3. **输出格式 - 实体:** 每个实体输出4个字段,字段之间用`{tuple_delimiter}`分隔,单行输出。第一个字段*必须*是字符串`entity`

4. **输出格式 - 关系:** 每个关系输出5个字段,字段之间用`{tuple_delimiter}`分隔,单行输出。第一个字段*必须*是字符串`relation`

5. **仅输出内容:** 仅输出提取的实体和关系列表。不要包括任何介绍性或结论性说明、解释或列表前后附加的文本。

6. **完成信号:** 在所有相关遗漏或修正的实体和关系提取并呈现后,输出`{completion_delimiter}`作为最后一行。

7. **输出语言:** 确保输出语言为{language}。专有名词(例如,人名、地名、组织名)必须保持其原始语言,不得翻译。

<输出>

实体与关系

实体 Node 示例

{
	"entity_name": "LightRAG",
	"entity_type": "organization",
	"description": "LightRAG is a project focused on Retrieval-Augmented Generation, developed by HKUDS, and available on GitHub and PyPI.",
	"source_id": "chunk-b3574b583039b34744f0d1f4dde878d3",
	"file_path": "README-zh.md",
	"timestamp": 1759317110
}

关系 Edge 示例

{
	"src_id": "LightRAG",
	"tgt_id": "Retrieval-Augmented Generation",
	"weight": 1.0,
	"description": "LightRAG implements Retrieval-Augmented Generation technology.",
	"keywords": "implementation, technology",
	"source_id": "chunk-b3574b583039b34744f0d1f4dde878d3",
	"file_path": "README-zh.md",
	"timestamp": 1759317110
}

实体合并 _merge_nodes_then_upsert

在实体更新到数据库时,需要进行合并操作,因为可能已经存在同一个实体多个描述。合并逻辑如下

  • 实体类型:选择出现次数最多的
  • 实体描述:多个描述去重排序后,合并
    • 如果描述少且简单:直接拼接
    • 如果描述多或复杂:使用LLM智能合并成一个综合描述
  • 来源ID:去重
  • 文件路径:去重合并拼接

关系合并 _merge_edges_then_upsert

同样在处理关系的时候,也需要进行相关的合并操作。合并逻辑如下

  • 权重处理:将所有新关系的权重与已存在关系的权重累加
  • 描述去重和排序,再简单合并或者使用 LLM 总结
  • 关键词去重合并
  • 来源信息合并,包括源ID和源文件
  • 自动创建缺失实体:如果出现了不存在的实体,会自动创建一个未知类型的实体并储存,确保知识图谱的完整性,避免"悬空"关系。

查询&检索流程

在理解了文档插入流程后,查询流程我们重点关注被储存的数据是如何用于检索的。

LightRAG 的查询处理会经过下面四层处理

  • API 层:接收用户查询请求,支持多种查询模式(local、global、hybrid、naive、mix、bypass)
  • 查询路由层:根据查询模式选择相应的处理策略,包括知识图谱查询(kg_query)和朴素查询(naive_query)
  • 知识检索层:四阶段处理架构 - 搜索、截断、合并、构建上下文
  • LLM 生成层:基于检索到的上下文生成最终回答

查询模式策略

  • local模式专注于特定实体及其直接关系,适合精确查询
  • global模式分析整体关系模式,适合概念性查询
  • hybrid模式结合local和global优势,提供全面结果
  • mix模式整合知识图谱和向量检索,是推荐的默认模式
  • naive模式仅使用向量相似度搜索
  • bypass模式直接调用LLM

流程时序

下面以 mix 模式展开描述整个查询检索的流程

    sequenceDiagram
    participant User as 用户
    participant API as API路由层<br/>query_routes.py
    participant LightRAG as LightRAG核心<br/>lightrag.py
    participant KGQuery as 知识图谱查询<br/>operate.py::kg_query
    participant KeywordExtract as 关键词提取<br/>operate.py::get_keywords_from_query
    participant SearchStage as 搜索阶段<br/>operate.py::_perform_kg_search
    participant NodeData as 实体检索<br/>operate.py::_get_node_data
    participant EdgeData as 关系检索<br/>operate.py::_get_edge_data
    participant VectorSearch as 向量检索<br/>operate.py::_get_vector_context
    participant TruncateStage as 截断阶段<br/>operate.py::_apply_token_truncation
    participant MergeStage as 合并阶段<br/>operate.py::_merge_all_chunks
    participant ContextStage as 上下文构建<br/>operate.py::_build_llm_context
    participant LLM as LLM服务
    
    rect rgb(255, 240, 240)
        Note over User, API: 第一阶段:请求接收与路由
        User->>API: POST /query {query, mode: "mix", params}
        API->>LightRAG: aquery_llm(query, param)
        Note over LightRAG: 检查 mode == "mix"
        LightRAG->>KGQuery: kg_query() [MIX模式]
    end
    
    rect rgb(240, 255, 240)
        Note over KGQuery, KeywordExtract: 第二阶段:关键词提取
        KGQuery->>KeywordExtract: get_keywords_from_query()
        KeywordExtract->>LLM: 提取高级和低级关键词
        LLM-->>KeywordExtract: {high_level_keywords, low_level_keywords}
        KeywordExtract-->>KGQuery: (hl_keywords, ll_keywords)
        Note over KGQuery: MIX模式需要两种关键词
    end
    
    rect rgb(240, 240, 255)
        Note over KGQuery, VectorSearch: 第三阶段:MIX模式三重检索
        KGQuery->>SearchStage: _perform_kg_search() [MIX模式]
        
        Note over SearchStage: MIX模式执行三种检索策略
        
        par 并行执行知识图谱检索
            SearchStage->>NodeData: _get_node_data(ll_keywords)
            Note over NodeData: 基于低级关键词检索实体
            NodeData-->>SearchStage: local_entities, local_relations
        and
            SearchStage->>EdgeData: _get_edge_data(hl_keywords)
            Note over EdgeData: 基于高级关键词检索关系
            EdgeData-->>SearchStage: global_relations, global_entities
        and
            Note over SearchStage: MIX模式独有:向量检索
            SearchStage->>VectorSearch: _get_vector_context(query, chunks_vdb)
            Note over VectorSearch: 向量相似度搜索原始文档块
            VectorSearch-->>SearchStage: vector_chunks + chunk_tracking
        end
        
        Note over SearchStage: 轮询合并三种数据源
        SearchStage->>SearchStage: 合并 entities (local + global)
        SearchStage->>SearchStage: 合并 relations (local + global)
        SearchStage->>SearchStage: 记录 vector_chunks 来源
        SearchStage-->>KGQuery: {final_entities, final_relations, vector_chunks, chunk_tracking}
    end
    
    rect rgb(255, 255, 240)
        Note over KGQuery, ContextStage: 第四阶段:MIX模式容错处理与上下文构建
        
        Note over KGQuery: MIX模式容错检查
        alt 知识图谱检索成功
            Note over KGQuery: entities 或 relations 不为空
        else 知识图谱检索失败但向量检索成功
            Note over KGQuery: chunk_tracking 不为空,继续处理
        else 所有检索都失败
            KGQuery-->>LightRAG: 返回失败响应
        end
        
        KGQuery->>TruncateStage: _apply_token_truncation()
        Note over TruncateStage: 截断实体和关系上下文
        TruncateStage-->>KGQuery: {entities_context, relations_context, filtered_*}
        
        KGQuery->>MergeStage: _merge_all_chunks()
        Note over MergeStage: MIX模式特殊处理:合并KG块和向量块
        MergeStage->>MergeStage: 从filtered_entities/relations获取KG块
        MergeStage->>MergeStage: 合并vector_chunks
        MergeStage->>MergeStage: 应用重排序和去重
        MergeStage-->>KGQuery: merged_chunks (KG + Vector)
        
        KGQuery->>ContextStage: _build_llm_context()
        Note over ContextStage: 构建包含三种数据源的上下文
        ContextStage->>ContextStage: 格式化entities_context
        ContextStage->>ContextStage: 格式化relations_context  
        ContextStage->>ContextStage: 格式化merged_chunks (KG+Vector)
        ContextStage->>ContextStage: 生成引用列表
        ContextStage-->>KGQuery: (context_string, raw_data)
    end
    
    rect rgb(240, 255, 255)
        Note over KGQuery, User: 第五阶段:LLM生成与响应
        KGQuery->>LLM: 系统提示词 + MIX模式上下文 + 用户查询
        Note over LLM: 基于知识图谱+向量检索的综合上下文生成回答
        LLM-->>KGQuery: 生成的回答(流式或非流式)
        KGQuery-->>LightRAG: QueryResult{content, raw_data, is_streaming}
        LightRAG-->>API: 包含三种数据源的结构化响应
        API-->>User: JSON响应 {response, references}
    end
  

关键词提取

提取关键词这一步服务于知识图谱的检索,为了更好的捕获问题中的实体以及其关系,由 LLM 提取 Query 中的低级关键词和高级关键词

  • 低级关键词:用于识别具体实体、专有名词、专业术语、产品名称或具体事项的细节信息
  • 高级关键词:用于捕捉核心意图、主题领域或问题类型的宏观概念
示例
查询:“国际贸易如何影响全球经济稳定?”
输出:
{
"high_level_keywords": ["国际贸易", "全球经济稳定", "经济影响"],
"low_level_keywords": ["贸易协定", "关税", "货币兑换", "进口", "出口"]
}


查询:“森林砍伐对生物多样性的环境影响有哪些?”
输出:
{
"high_level_keywords": ["环境影响", "森林砍伐", "生物多样性丧失"],
"low_level_keywords": ["物种灭绝", "栖息地破坏", "碳排放", "热带雨林", "生态系统"]
}
关键词提取 Prompt
---角色---

您是一位专业的关键词提取专家,专注于分析检索增强生成(RAG)系统中的用户查询。您的目标是识别用户查询中的高层次和低层次关键词,以实现高效文档检索。

---目标---

针对用户查询,您的任务是提取两种不同类型的关键词:

1. **高层次关键词**:用于捕捉核心意图、主题领域或问题类型的宏观概念。

2. **低层次关键词**:用于识别具体实体、专有名词、专业术语、产品名称或具体事项的细节信息。

---规则与限制---

1. **输出格式**:输出必须是纯JSON对象格式,不得包含任何说明文字、Markdown代码标记(如```json)或前后缀文本。该输出将直接由JSON解析器处理。

2. **来源依据**:所有关键词必须严格源自用户查询,且两个关键词类别都必须包含实际内容。

3. **简洁性与意义**:关键词应为简洁的单词或意义明确的短语。当多词短语代表单一概念时优先提取完整短语,例如从“苹果公司最新财报”中应提取“最新财报”和“苹果公司”,而非拆分为“最新”“财务”“报告”“苹果”。

4. **特殊情况处理**:对于过于简单、模糊或无意义的查询(如“你好”“好的”“乱码字符”),需返回包含空列表的JSON对象。

---示例---

基于低级关键词检索实体

这一步通过查询低级关键词找到最相关的实体及其关联关系,包括下面3个关键查询

  1. 查询向量库中的实体数据:基于语义相似度找到与低级关键词最相关的实体
  2. 查询知识图谱中的实体数据:找出上一步实体在知识图谱中的详细数据
  3. 查询知识图谱中的关系数据:计算实体在图中的连接度数(用于作重要性评估)

基于高级关键词检索关系

这一步基于高级关键词找到最相关的关系及其连接的实体,专注于发现知识图谱中的关系模式和全局结构信息。 包括下面3份关键查询

  1. 查询向量库中的关系数据:基于语义相似度找到与高级关键词最相关的关系
  2. 查询知识图谱中的关系数据:从知识图谱批量获取关系的详细属性信息
  3. 实体提取:从找到的关系中提取所有涉及的实体

向量检索

基础RAG里面的检索流程, 使用余弦相似度匹配出最相关的 TopK 个分块向量

截断实体和关系上下文

实体和关系分别限定了上下文的长度限制,这一步进行对应的截断操作,截断后输出如下

  • 截断后的上下文数据(用于LLM)
    • entities_context: 格式化且截断后的实体列表,直接用于构建LLM提示词
    • relations_context: 格式化且截断后的关系列表,直接用于构建LLM提示词
  • 过滤后的原始数据(用于后续处理)
    • filtered_entities: 与截断上下文对应的完整原始实体数据
    • filtered_relations: 与截断上下文对应的完整原始关系数据
  • 映射关系(用于数据追溯)
    • entity_id_to_original: 实体名 → 原始实体数据的映射
    • relation_id_to_original: 关系对 → 原始关系数据的映射

Merge 与 Rerank

合并重排来自不同来源的数据块:向量数据块 + 实体数据块 + 关系数据块。

针对实体与关系,会分别进行一轮的选择重排,支持两种策略

  • WEIGHT 策略(权重轮询):基于块在多个实体中的出现频次进行加权选择,频次越高的块被认为越重要。算法通过统计每个块在不同实体的 source_id 中的出现次数,然后使用线性梯度加权轮询算法,优先选择高频块,同时保证每个实体都有机会贡献块。这种方法假设被多个实体共同引用的文档块包含更重要的信息。
  • VECTOR 策略(向量相似度):将查询文本和候选文档块都转换为向量表示,通过计算余弦相似度来衡量语义相关性。算法首先收集所有实体相关的候选块,然后使用嵌入函数计算查询与每个块的相似度分数,按分数排序选择最相关的块。这种方法直接优化查询与文档块之间的语义匹配度。

然后对针对三部分数据再进行一轮的去重合并,通过循环遍历三个数据源(向量块、实体块、关系块)的相同索引位置,按照 向量数据块 -> 实体数据块 -> 关系数据块 的固定优先级顺序依次添加块到结果列表中。算法使用 seen_chunk_ids 集合进行严格去重,确保相同的 chunk_id 只被添加一次,即使它在多个数据源中都出现。

构建 LLM 上下文

这一步比较简单,将上面检索出的分块信息精简处理整合进上下文 ,另外这里还会有一个整体上下文长度的截断策略。

System Prompt
---角色---

您是一位专家人工智能助手,专门从提供的信息库中综合信息。您的主要功能是通过仅使用提供的**上下文**中的信息来准确回答用户查询。

---目标---

生成一个全面、结构良好的答案来回答用户查询。

答案必须整合**上下文**中找到的**知识图谱**和**文档块**中的相关事实。

如果提供了对话历史,请考虑对话历史以保持对话流畅并避免重复信息。

---说明---

**1. 步骤说明:**

- 仔细确定用户在对话历史中的查询意图,以充分理解用户的信息需求。

- 仔细审查**上下文**中的**知识图谱数据**和**文档块**。识别并提取所有与回答用户查询直接相关的信息。

- 将提取的事实编织成一个连贯且逻辑上合理的回答。您自己的知识必须仅用于构建流畅的句子和连接思想,不得引入任何外部信息。

- 跟踪支持响应中呈现的事实文档的reference_id。将reference_id与`参考文档列表`中的条目相关联,以生成适当的引用。

- 在响应末尾生成一个**引用**部分。每个参考文档必须直接支持响应中呈现的事实。

- 在引用部分之后不要生成任何内容。

**2. 内容与定位:**

- 严格遵循**上下文**中提供的**上下文**;不要发明、假设或推断任何未明确陈述的信息。

- 如果答案无法在**上下文**中找到,声明您没有足够的信息来回答。不要尝试猜测。

**3. 格式与语言:**

- 响应必须使用与用户查询相同的语言。

- 使用Markdown进行清晰的格式化(例如,标题、粗体、列表)。

- 响应应呈现为{response_type}。

**4. 引用部分格式:**

- 引用部分应以下面标题:`### 引用`

- 参考列表条目应遵循以下格式:`* [n] 文档标题`。在开方括号(`[`)后不要包括一个撇号(`^`)。

- 引用中的文档标题必须保留其原始语言。

- 每个引用单独一行输出

- 提供最多5个最相关的引用。

- 不要生成脚注部分或任何引用部分之后的文本。

**5. 引用部分示例:**


### 引用

* [1] 文档标题一

* [2] 文档标题二

* [3] 文档标题三


**6. 其他说明**:{user_prompt}

---上下文---

{context_data}
上下文部分
Knowledge Graph Data (Entity):


{entities_str}


Knowledge Graph Data (Relationship):

{relations_str}

Document Chunks (Each entry has a reference_id refer to the `Reference Document List`):


{text_chunks_str}


Reference Document List (Each entry starts with a [reference_id] that corresponds to entries in the Document Chunks):


{reference_list_str}

-- End --