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:82-157。
一句话直觉/类比: 把它想成「图书馆 + 关系网」两套索引并存。纯向量 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 │
└──────────────────┘
怎么读这张图: 左边入库把文档「灌」进中间的四套存储;右边查询从这四套存储里「捞」内容。两条线唯一的耦合点就是中间的存储。