数据截至 (上游 commit f791fa4751f6)
LightRAG — 架构与原理
30 秒导读: LightRAG 是港大数据智能实验室(HKUDS)开源的一套 RAG(检索增强生成)框架。它的卖点是:入库时不只把文档切块塞进向量库,还额外用 LLM 把文档抽成一张知识图谱(实体是点、关系是边);查询时把问题拆成「高层主题词 + 低层实体词」两路,分别去图谱的边和点上做检索——这就是论文名字里的 dual-level retrieval(双层检索)。比起纯向量 RAG,它更擅长回答需要「跨多个文档串联关系」的问题。
本项目较大(lightrag.py 4469 行、operate.py 5995 行),本文档拆成 index + 4 章。本页给你 Layer 0(这是什么)+ Layer 1(顶层全景)+ 阅读地图;各机制的原理与源码走读在分章里。
1. 这是什么(零基础也能懂)
一句话定义: LightRAG 是一个「文档进、带引用的答案出」的 RAG 引擎,核心差异是它在普通向量检索之外,额外维护了一张由 LLM 抽出来的知识图谱,查询时同时利用图谱的点和边。
解决什么问题 / 给谁用:
假设你有一堆 PDF / 网页 / 内部文档,想做一个「问答机器 人」:用户问一句话,机器人从你的文档里找答案并附上出处。最朴素的做法(naive RAG)是:把文档切成小块、每块算个向量、问题也算个向量、找最相似的几块塞给 LLM。
这套朴素做法在一类问题上很弱:需要把分散在多处的信息串起来的问题。例如「A 公司和 B 公司是什么关系?」——答案可能要拼接「A 投资了 C」+「C 被 B 收购」这种跨块的链条。纯向量检索找的是「和问题最像的块」,不擅长沿关系链跳转。
LightRAG 给这类问题用的是「知识图谱」这条额外的检索通道。它面向:
- 做企业内部知识库 / 文档问答的工程师;
- 需要「实体—关系」推理而非单纯语义相似的检索场景;
- 想要可切换存储后端(本地文件 → PostgreSQL / Neo4j / Milvus 等)的部署者。
它能做什么(功能):
- 入库:把文本/PDF 等切块,抽实体与关系,建知识图谱 + 三个向量库(实体、关系、原文块)。
- 查询:支持
local/global/hybrid/mix/naive/bypass六种模式(见下)。 - 增量更新与删除:新文档增量并入图谱;删文档时回收并重建受影响的图谱节点。
- 带引用回答:回答末尾生成
### References引用列表,可溯源到具体文档。 - 可插拔:LLM、嵌入模型、重排模型、四类存储后端都可替换;还能给「抽取 / 查询 / 关键词 / 视觉」配不同的 LLM(role-specific)。
用起来什么样: 最小调用(示意,基于 LightRAG/QueryParam 真实 API):
# 示意,非源码。展示对外 API 形状
import asyncio
from lightrag import LightRAG, QueryParam
async def main():
rag = LightRAG(
working_dir="./my_kb", # 本地文件存储默认落在这里
llm_model_func=my_llm, # 你的 LLM 调用函数
embedding_func=my_embedding, # 你的嵌入函数
)
await rag.initialize_storages() # 必须先初始化存储
await rag.ainsert("OpenAI 由 Sam Altman 等人在 2015 年创立。") # 入库
# 查询:mix 模式 = 知识图谱 + 向量,双管齐下
res = await rag.aquery("谁创立了 OpenAI?", param=QueryParam(mode="mix"))
print(res)
asyncio.run(main())
对外入口与参数对象都在 lightrag/__init__.py:5-11 的 __all__ 里导出:LightRAG、QueryParam、RoleLLMConfig、RoleSpec、ROLES。QueryParam 的字段定义在 lightrag/base.py:89-164。
一句话直觉/类比: 把它想成「图书馆 + 关系网」两套索引并存。纯向量 RAG 只有「按内容相似度找书页」;LightRAG 额外有一张「谁认识谁、谁属于谁」的关系网,问到需要顺藤摸瓜的问题时,它能沿着关系网爬。
2. 顶层全景(它大概怎么转)
LightRAG 有两条主线:入库(indexing) 和 查询(query)。两条线共用同一组存储。
2.1 整体数据流
入库主线 (ainsert) 查询主线 (aquery)
┌────────────────────────────┐ ┌──────────────────────────────┐
│ 文档原文 │ │ 用户问题 │
│ │ 切块 (chunker) │ │ │ LLM 抽关键词 │
│ ▼ │ │ ▼ │
│ 文本块 chunks │ │ 高 层词 hl_keywords │
│ │ LLM 抽实体/关系 │ │ 低层词 ll_keywords │
│ ▼ (extract_entities) │ │ │ │
│ 实体 + 关系 (per-chunk) │ │ ├─ ll → 查实体VDB → 图的点 │
│ │ 跨块合并去重+LLM摘要 │ │ ├─ hl → 查关系VDB → 图的边 │
│ ▼ (merge_nodes_and_edges)│ │ └─ query → 查原文块VDB(mix)│
│ 写入 4 套存储 ↓ │ │ ▼ │
└────────────┬────────────────┘ │ 融合+截断+找支撑块+组prompt │
▼ │ ▼ (_build_query_context) │
┌──────────────────┐ ◄───── 查询读 ──┤ LLM 生成带引用的答案 │
│ 知识图谱 (点+边) │ └──────────────────────── ──────┘
│ 实体向量库 │
│ 关系向量库 │
│ 原文块向量库 + KV │
└──────────────────┘
怎么读这张图: 左边入库把文档「灌」进中间的四套存储;右边查询从这四套存储里「捞」内容。两条线唯一的耦合点就是中间的存储。
2.2 部件一句话职责
| 部件 | 干什么 | 在哪个文件 |
|---|---|---|
LightRAG(门面类) | 对外 API:ainsert/aquery 等;持有所有存储句柄 | lightrag/lightrag.py:389(class LightRAG) |
| 入库流水线 | 切块→抽取→合并→落库的多阶段 worker | lightrag/pipeline.py(_PipelineMixin) |
| 分块器 | 4 种切块策略:Fix / Recursive / Vector / Paragraph | lightrag/chunker/(chunking_by_*) |
| 实体/关系抽取 | 用 LLM 把 chunk 抽成实体行 + 关系行 | lightrag/operate.py:3827(extract_entities) |
| 合并去重 | 同名实体跨块合并、描述太多时 LLM 摘要 | lightrag/operate.py:3399(merge_nodes_and_edges) |
| 双层检索 | 把问题拆词、分走图的点和边 | lightrag/operate.py:4428(kg_query)、:4315(_perform_kg_search) |
| 上下文组装 | 融合实体/关系/块、token 预算、找支撑块、组 prompt | lightrag/operate.py:5688(_build_query_context) |
| 提示词 | 所有 LLM prompt 模板 | lightrag/prompt.py(PROMPTS) |
| 存储抽象 | 4 类存储的抽象基类 | lightrag/base.py(BaseKVStorage/BaseVectorStorage/BaseGraphStorage/DocStatusStorage) |
| 存储工厂 | 名字 → 具体后端类 | lightrag/kg/factory.py:17(get_storage_class) |
2.3 四套存储 + 默认后端
LightRAG 把数据切成四种角色的存储,每种角色可以选不同后端实现。门面类的默认值在 lightrag/lightrag.py:401-410:
| 存储角色 | 存什么 | 抽象基类 | 默认后端 |
|---|---|---|---|
| KV 存储 | 原文、文本块、LLM 缓存 | BaseKVStorage | JsonKVStorage(本地 JSON 文件) |
| 向量存储 | 实体/关系/原文块的嵌入向量 | BaseVectorStorage | NanoVectorDBStorage |
| 图存储 | 知识图谱的点和边 | BaseGraphStorage | NetworkXStorage(NetworkX 内存图) |
| 文档状态存储 | 每篇文档的处理状态机 | DocStatusStorage | JsonDocStatusStorage |
这四个角色在 __post_init__ 里被实例化成具体句柄:text_chunks、full_docs、llm_response_cache、chunk_entity_relation_graph、entities_vdb、relationships_vdb、chunks_vdb、doc_status(lightrag/lightrag.py:1410-1479)。换后端只是换 kv_storage / vector_storage / graph_storage / doc_status_storage 这四个字符串,工厂会 lazy-import 对应实现(lightrag/kg/factory.py:17)。
2.4 六种查询模式(主线分叉)
QueryParam.mode 决定走哪条检索路径(lightrag/base.py:93-100):
| 模式 | 检索什么 | 直觉 |
|---|---|---|
local | 只用低层词查实体(图的点) | 「围绕具体实体的局部信息」 |
global | 只用高层词查关系(图的边) | 「跨实体的全局主题」 |
hybrid | local + global 都做并融合 | 「点和边都要」 |
mix | hybrid + 额外的原文块向量检索(默认) | 「图谱 + 朴素向量,全都要」 |
naive | 只做原文块向量检索,不碰图谱 | 「退化成普通向量 RAG」 |
bypass | 不检索,直接问 LLM | 「纯聊天,不查库」 |
默认 mode="mix",即知识图谱与向量检索一起上(lightrag/base.py:93)。
3. 阅读地图(建议顺序)
按「先入库、再查询、再细节」的顺序读最顺:
01-indexing.md— 入库流水线。 文档怎么变成知识图谱 + 向量库:分块、用 LLM 抽实体/关系、跨块合并去重、描述太多时的 LLM 摘要、写入四套存储。读完你会理解图谱是「攒」出来的。02-dual-level-retrieval.md— 双层检索(本项目的灵魂)。 查询时怎么把问题拆成高层/低层关键词,分别去图的边和点上检索,以及 local/global/hybrid/mix 的具体分叉。03-context-assembly.md— 上下文组装。 检索到的实体/关系/块如何融合、token 预算怎么切、怎么从实体回溯到支撑它的原文块、怎么组成最终 prompt 并生成带引用的回答。04-internals.md— 深入与边界。 巧妙之处(增量去重、缓存键、map-reduce 摘要)、边界与局限、与兄弟项目(MiniRAG / RAG-Anything / GraphRAG)的对比、完整代码地图。
如果你只想快速判断「LightRAG 适不适合我的场景」,读完本页第 1、2.4 节就够了。