RAG技术完全指南——从小白到进阶

0. 阅读导引 (๑•̀ㅂ•́)و✧

嗨~欢迎来到 RAG 的奇妙世界!这篇文章是写给所有对 AI 应用开发感兴趣的朋友的。无论你是:

  • 刚听说”大模型”想了解怎么用它做知识库的 纯小白 🐣
  • 写过几个 RAG Demo 但总觉得”还不够好用”的 进阶选手 💪
  • 想知道 2025-2026 年 RAG 领域最新进展的 前沿追踪者 🔭

这篇文章都会给你满满的干货!(保证不水字数,每一节你都能用上)

读完你会获得什么?

  1. 理解 RAG 为什么诞生,解决了什么问题
  2. 掌握从数据清洗到检索生成的全链路技术细节
  3. 了解 GraphRAG、Agentic RAG、Self-RAG 等 2025 年前沿方案
  4. 能用代码实现一个生产级的 RAG 系统

本文约 2 万字,建议收藏后慢慢啃 (。・ω・。)


1. 故事从 GPT 说起——RAG 为什么要存在? 🤔

1.1 大模型的”知识诅咒”

2022 年 11 月 ChatGPT 横空出世,全世界都被它的能力震惊了。但很快,人们发现了几个让人头疼的问题:

问题一:它在胡说八道 (Hallucination / 幻觉)

1
2
用户:请介绍一下 OpenPipe 这个公司。
ChatGPT(2023年初版本):OpenPipe 是一家专注于...(言之凿凿地编造)

因为训练数据里没有 OpenPipe 的信息,模型就”脑补”了一个答案——还不是低调地脑补,而是带着 100% 的自信说出来 (╯°□°)╯︵ ┻━┻

问题二:它的知识有时效性

1
2
用户:最新的 iPhone 是哪款?
ChatGPT(训练截止 2023.1):iPhone 14 系列(如果现在已经是 2025 年...)

模型的”知识”冻结在训练结束的那一天。之后发生的一切——新论文、新产品、新政策——它一概不知。

问题三:它不了解你的私有数据

1
2
用户:我们公司内部的项目 X 目前进度如何?
ChatGPT:对不起,我没有关于贵公司内部项目的信息。

公司内部的文档、数据库、Wiki——这些才是日常工作中最需要 AI 辅助的地方,但大模型天然看不到这些数据。

1.2 三种”土办法”的尝试与失败

人们一开始想了几招来让模型”知道更多”:

方法 核心思路 致命问题
🗣️ 全量 Prompt 注入 每次提问时把所有相关文档塞进 Prompt Token 上限+成本爆炸(一次查询可能烧掉 $0.5)
🧠 微调 (Fine-tuning) 用新数据重新训练模型 训练成本高、无法增量更新、容易”灾难性遗忘”
📚 全量数据训练 从零训练专属大模型 百万美元起步、需要海量 GPU、非大厂不可为

1.3 RAG 的核心理念:检索 + 生成 = 最佳拍档

2020 年,Meta(当时还叫 Facebook AI)的论文 Retrieval-Augmented Generation for Knowledge-Intensive NLP Tasks 提出了一个后来改变了整个 AI 应用范式的架构——RAG

核心思想其实特别简单:

用人话解释就是:

别让模型背答案,给它一个”开卷考试”的环境 📖

每次用户提问时,系统先去知识库里”翻书”找到相关内容,然后把问题和参考资料一起交给 LLM,让它”参考这些材料来回答”。

这样做的三大好处:

  1. 解决幻觉:模型有据可依,不再凭空捏造(至少大幅减少)
  2. 解决时效性:知识库随时更新,不需要重新训练模型
  3. 解决私域数据:你的文档、数据库、API 都可以作为知识源

2025 年 RAG 已经成了几乎所有 AI 应用的标配架构。 从 GitHub Copilot 的代码检索、到 Notion AI 的文档问答、到 Perplexity 的联网搜索增强——底层都是 RAG 的变体。


2. RAG 的核心四大件 🔧

如果把 RAG 比作一辆车,它有四个核心部件:

咱们一个一个解剖 (๑•̀ω•́)ノ🔪

2.1 数据预处理——“把书拆成卡片” 📇

在 RAG 中,”知识”存储在知识库里。但你不能把一本 500 页的书直接丢给检索系统——那样什么都搜不出来。你需要切块(Chunking)

2.1.1 为什么需要切块?

  • 向量模型(Embedding Model)有输入长度限制(通常是 512 或 1024 tokens)
  • 太长的文本会被截断,丢失语义
  • 太短的文本缺乏上下文,检索精度下降
  • 检索时你希望返回的块”恰好”包含答案,不多不少

2.1.2 切块策略全家福

① 固定大小分块(Fixed-size Chunking)

最简单粗暴的方式。设定一个 chunk_size(如 500 tokens),从头到尾均匀切割。

1
2
3
4
5
6
7
8
9
# 最基础的固定分块(使用 LangChain)
from langchain.text_splitter import CharacterTextSplitter

splitter = CharacterTextSplitter(
chunk_size=500,
chunk_overlap=50, # 块与块之间的重叠,防止关键信息被切到边界
separator="\n"
)
chunks = splitter.split_text(document)

优点:简单、快、不需要模型
缺点:经常在句子中间切断,有些块完全没有语义价值

⚠️ overlap 的重要性:假设你的文档里有一段:

1
...根据量子力学原理,薛定谔方程描述了量子态的演化...

如果不设 overlap,可能变成:

  • Chunk 42: ...根据量子力学原理,薛定谔方程 ← 不完整!
  • Chunk 43: 描述了量子态的演化... ← 缺主语!

设了 overlap(比如 50 tokens)后:

  • Chunk 42: ...根据量子力学原理,薛定谔方程
  • Chunk 43: ...薛定谔方程描述了量子态的演化... ← cover 了整个句子 (ง •̀_•́)ง
② 递归分块(Recursive Chunking)

先在”大边界”上切(如段落 \n\n),如果块太长再在”小边界”上切(如句子 ),层层递归。

1
2
3
4
5
6
7
from langchain.text_splitter import RecursiveCharacterTextSplitter

splitter = RecursiveCharacterTextSplitter(
chunk_size=1000,
chunk_overlap=200,
separators=["\n\n", "\n", "。", ",", " ", ""] # 优先级从高到低
)

这是目前最常用的切块方式,在成本和效果之间取得了很好的平衡。

③ 句子分块(Sentence-based Chunking)

按句子边界切分,每个 chunk 包含 N 个完整句子。保证每块都是完整的语义单元。

1
2
3
4
5
6
7
8
9
10
11
import spacy

nlp = spacy.load("zh_core_web_sm") # 中文可用 spacy 或 jieba

def sentence_chunk(text, max_sentences=5):
doc = nlp(text)
sentences = [sent.text for sent in doc.sents]
chunks = []
for i in range(0, len(sentences), max_sentences):
chunks.append(" ".join(sentences[i:i+max_sentences]))
return chunks
④ 语义分块(Semantic Chunking)✨ 2025 前沿!

这也是2024-2025 年最热门的 chunking 趋势——让模型自己判断”哪里该切”。

工作原理

  1. 遍历文档中的每个句子
  2. 计算相邻句子的 Embedding 相似度
  3. 当相似度降到阈值以下 → 此处切开(话题转换点)
  4. 每个 chunk 内部保持语义连贯
1
2
3
4
5
6
7
8
9
# langchain 的 SemanticChunker
from langchain_experimental.text_splitter import SemanticChunker
from langchain_openai import OpenAIEmbeddings

splitter = SemanticChunker(
embeddings=OpenAIEmbeddings(),
breakpoint_threshold_type="percentile", # 或 "standard_deviation"
breakpoint_threshold_amount=95 # 低于第95百分位的相似度就切
)

2025 年新进展

  • Agentic Chunking:用一个轻量 LLM 遍历文档,每读一段就决定”这里该不该切”,比固定阈值更灵活
  • Chunk Linking:切块后不只存单独的块,还存块与块之间的”前一页/后一页”关系,让 LLM 可以在回答时”翻页”
⑤ 小文档策略(Small-to-Big)✨ 更高级

不是把所有文档都切成小块。而是:

  1. 检索时用小 chunk(精准匹配)
  2. 喂给 LLM 时用大 chunk 或完整段落(提供充足上下文)

实现方式有:

  • 父文档检索器(Parent Document Retriever):检索小 chunk,返回它所属的整个文档
  • 句子窗口检索(Sentence Window Retrieval):检索命中句子,返回前后各 K 句
1
2
3
4
5
6
7
# LlamaIndex 的 SentenceWindowNodeParser
from llama_index.core.node_parser import SentenceWindowNodeParser

parser = SentenceWindowNodeParser(
window_size=3, # 前后各取 3 句
window_metadata_key="window"
)

2.1.3 处理特殊格式 ⚡

现实世界的数据远比纯文本复杂:

格式 关键挑战 解决方案
📊 PDF 表格、图片、两栏排版 LlamaParse / Marker / Docling 做结构化解析
🌐 网页 导航栏、广告、评论噪音 readability 算法提取正文 + CSS selector
💻 代码 函数调用关系跨越多个文件 AST(抽象语法树)分块 + 依赖图
📋 表格 每个单元格单独看都没意义 转 Markdown 表格 + 表头重复策略
🏢 企业文档 PPT、Excel、Confluence、飞书 Unstructured.io (支持 20+ 格式)

2.1.4 Document Cleaning 也别忽略! 🧹

脏数据 = 坏检索。你应该至少做:

1
2
3
4
5
6
7
8
9
10
11
import re

def clean_text(text: str) -> str:
# 1. 移除多余的空白行
text = re.sub(r'\n\s*\n\s*\n+', '\n\n', text)
# 2. 替换特殊 Unicode 字符
text = text.replace('​', '') # 零宽空格
text = text.replace('\xa0', ' ') # 非断空格
# 3. 统一全角/半角
# (根据你的语料决定是否做)
return text.strip()

2.2 向量化——“让机器能’看懂’文字” 🔢

把文本切成块后,下一步是把它变成计算机可以”比较相似度”的形式——向量(Vector / Embedding)

2.2.1 向量和 Embedding 到底是什么?

想象一个三维空间:

  • 🍎 “苹果”在 (0.8, 0.1, 0.3)
  • 🍌 “香蕉”在 (0.7, 0.2, 0.4) ← 离苹果很近
  • 🚗 “汽车”在 (-0.5, 0.8, 0.6) ← 离苹果很远

一个好的 Embedding 模型做的事情就是:把语义相似的文字,映射到相近的向量位置上

真实世界使用的向量不是 3 维,而是 768、1024 甚至 4096 维——维度越高,能编码的语义信息越丰富。

2.2.2 如何选 Embedding 模型?

截至 2025 年中,主流选择(按场景):

模型 维度 最大 Tokens 适用场景 语言
BGE-M3 (BAAI) 1024 8192 通用中文/多语言 中英+100+语言
GTE-Qwen2-7B 3584 32768 长文档+中文旗舰 中英文
Jina Embeddings v3 1024 8192 多语言+任务特定 LoRA 89种语言
Cohere Embed v3 1024 512 英文商业应用 英文
text-embedding-3-large (OpenAI) 3072/256/1024 8191 灵活维度+高精度 50+语言
NV-Embed-v2 (NVIDIA) 4096 4096 MTEB 榜首 2025 英文

🏆 2025 年 MTEB 榜单(海量文本 Embedding 基准测试)前三名:

  1. NV-Embed-v2(NVIDIA)— 2025.02 发布
  2. GTE-Qwen2-7B-instruct — 阿里出品,中文最强
  3. SFR-Embedding-2-R(Salesforce)

2.2.3 动手实践:给文档生成向量

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
# 使用 BGE-M3 给中文文档生成 Embedding
from sentence_transformers import SentenceTransformer

# BGE-M3 支持中英双语,且能输出 Dense + Sparse 两种向量
model = SentenceTransformer("BAAI/bge-m3", device="cuda")

# 示例文档块
chunks = [
"RAG 是检索增强生成的缩写...",
"向量数据库用于存储和检索高维向量...",
"苹果公司推出了新款 iPhone..."
]

# 生成 Dense Embedding(常规用途)
dense_embeddings = model.encode(
chunks,
normalize_embeddings=True, # L2 归一化,使得内积 = 余弦相似度
show_progress_bar=True
)

# BGE-M3 还能同时输出 Sparse Embedding(用于关键词匹配)
sparse_embeddings = model.encode(
chunks,
return_dense=False,
return_sparse=True # 输出词袋向量
)
print(f"向量维度:{dense_embeddings.shape}") # (3, 1024)

2.2.4 Dense vs Sparse Embedding——两种”找相似”的哲学 🧘

1
2
3
4
5
6
7
8
9
10
11
Dense Embedding(稠密向量)         Sparse Embedding(稀疏向量)
─────────────────────────── ──────────────────────────
[0.023, -0.451, 0.892, ...] "apple": 0, "banana": 2.3,
连续、密集的浮点数 "computer": 0, "fruit": 1.8...
大部分维度是 0

优点:捕获语义相似度 优点:精确关键词匹配
"苹果" ≈ "富士苹果" ✅ "苹果" ≈ "iPhone 苹果" ❌
"苹果" = "苹果公司" ✅

代表模型:BGE, OpenAI, Cohere 代表方法:BM25, SPLADE

2025 年的趋势混合使用!Dense 捕获语义+ Sparse 精确匹配 = 天作之合(见 2.3 节)


2.3 检索——“大海捞针的艺术” 🎣

向量化之后,你把所有文档块的向量存到了向量数据库里。现在用户来了一个问题,你怎么找到最相关的内容?

2.3.1 基础向量检索:KNN 与 ANN

暴力最近邻(KNN):拿问题的向量跟库里的 N 个向量逐一算距离 → 太慢了,百万级数据要几分钟。

近似最近邻(ANN):牺牲一点点精度,换回百倍速度。主流算法:

算法 原理 代表实现 适用场景
HNSW 多层跳表图 Qdrant, Weaviate, pgvector 高召回率要求
IVF (倒排索引) 先聚类再搜索 Faiss, Milvus 千万级以上数据
DiskANN 磁盘上的图索引 Microsoft DiskANN, LanceDB 内存有限的大数据量
PQ (乘积量化) 向量压缩 配合 IVF/HNSW 使用 内存压缩 10-30x

2.3.2 混合检索(Hybrid Search)—— 2025 年的标配 ⊹⋛

纯向量检索的问题:

1
2
3
4
5
用户问题:"CPU 怎么超频?"
纯向量检索结果:
✅ "AMD Ryzen 7 超频教程..."(语义相关)
❌ "CPU 是什么意思?"(也相似,但不包含超频方法)
❌ "CPU超频是一种...的行为"(可能字数不够详尽)

混合检索 = 向量检索 + 关键词检索 → 合并排序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 混合检索示例(使用 RRF: Reciprocal Rank Fusion)
from typing import List

def reciprocal_rank_fusion(
dense_results: List[str], # 向量检索排名 (rank 1, 2, 3...)
sparse_results: List[str], # BM25 检索排名
k: int = 60 # RRF 平滑参数
) -> List[str]:
scores = {}

# 向量检索得分
for rank, doc_id in enumerate(dense_results):
scores[doc_id] = scores.get(doc_id, 0) + 1 / (k + rank + 1)

# BM25 检索得分
for rank, doc_id in enumerate(sparse_results):
scores[doc_id] = scores.get(doc_id, 0) + 1 / (k + rank + 1)

# 按融合分降序排列
return sorted(scores.keys(), key=lambda x: scores[x], reverse=True)

2.3.3 Reranker(重排序)——检索的最后一道关口 🎯

即使混合检索的结果,通常第一条也不一定是”最有用”的。你需要一个 Reranker 做”精排”。

原理:检索阶段粗筛 50-100 条 → Reranker 用更强的模型逐条打分 → 只保留 Top 5-10

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# 使用 BGE-Reranker-v2 进行精排
from FlagEmbedding import FlagReranker

reranker = FlagReranker("BAAI/bge-reranker-v2-m3", use_fp16=True)

# 候选文档列表
query = "RAG 系统如何减少幻觉?"
candidates = [
"RAG 通过引入外部知识库来约束 LLM 输出,从而减少幻觉现象",
"GPT-4 在数学推理方面的准确率达到了 97%",
"增加检索到的相关文档数量可以显著降低幻觉率",
"RAG 系统的延迟主要取决于向量检索的速度"
]

# 逐条打分
pairs = [[query, doc] for doc in candidates]
scores = reranker.compute_score(pairs, normalize=True)

# 按分数排序
ranked = sorted(zip(candidates, scores), key=lambda x: x[1], reverse=True)
for doc, score in ranked:
print(f"[{score:.3f}] {doc}")

2025 年 Reranker 选型指南

Reranker 规模 特点
BGE-Reranker-v2-m3 0.5B 多语言,性价比之王
Cohere Rerank v3 API 商业最稳,多语言
Jina Reranker v2 0.3B 超快,多语言
gte-Qwen2-7B-instruct 7B 精度第一,中文特化

2.3.4 查询重写(Query Rewriting)——让你的问题更”聪明” 💡

用户提出的问题往往很随意。直接检索效果时好时坏。

1
2
3
4
5
6
7
8
9
10
11
12
13
# 示例:多轮对话中的查询重写
chat_history = [
{"role": "user", "content": "介绍一下 Transformer"},
{"role": "assistant", "content": "Transformer 是 Google 在 2017 年提出的..."},
{"role": "user", "content": "它的注意力机制怎么工作?"} # ← "它"指的是?
]

# 使用 LLM 将模糊问题 → 独立完整的问题
rewritten_query = llm.rewrite(
user_query="它的注意力机制怎么工作?",
chat_history=chat_history
)
# → "Transformer 模型中的自注意力机制是如何工作的?"

常见的查询优化技术

技术 做法 效果
HyDE 先用 LLM 生成一个”假设答案”,用假设答案做向量检索 大幅提升长问题的召回率
Multi-Query 从一个问题生成 3-5 个变体,并行检索取并集 提高召回覆盖度
Query Decomposition 复杂问题拆成子问题,逐步检索 适合多跳推理
Step-back Prompting 先生成高层次的”背景问题”,再结合原始问题 提升抽象问题检索质量
Self-Query 让 LLM 从问题中抽取元数据过滤条件 “2024年的AI论文”→ year=2024

2.3.5 Multi-hop Retrieval(多跳检索)🔗

有些问题需要”先搜 A,再从 A 的结果里搜 B”:

1
2
3
4
5
6
7
问题:"马斯克创办的第一家AI公司的现任CEO是谁?"

需要多步推理!

步骤1:检索 → "马斯克创办的第一家AI公司" → 答案:DeepMind(不是,是 xAI 对吗...
实际上是:OpenAI(2015年联合创办))
步骤2:检索 → "OpenAI 的现任 CEO" → 答案:Sam Altman
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# 多跳检索的简化实现
def multi_hop_retrieval(query: str, retriever, llm, max_hops=3):
current_query = query
all_contexts = []

for hop in range(max_hops):
# 本轮检索
docs = retriever.search(current_query, top_k=5)
all_contexts.extend(docs)

# 让 LLM 判断:还缺什么信息?
response = llm.check_completeness(
original_query=query,
current_context=all_contexts
)

if response["is_complete"]:
break # 信息够了,停止检索

current_query = response["next_query"] # 下一轮要搜的问题

return all_contexts

2.4 生成——“最后一步:写出漂亮答案” ✍️

检索到相关知识后,最后一步是把”问题 + 检索到的上下文”拼成一个 Prompt,交给 LLM 生成答案。

2.4.1 Prompt 模板设计

一个标准的 RAG Prompt 模板:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
基于以下参考资料,回答用户的问题。

## 指令
1. 只使用参考资料中的信息来回答
2. 如果参考资料不足以回答,请明确说明"根据现有资料无法回答"
3. 引用资料时注明来源编号,如 [1]、[2]
4. 用中文回答,语气友好专业

## 参考资料
[1] {doc1_content}

[2] {doc2_content}

[3] {doc3_content}

## 用户问题
{user_query}

## 回答

2.4.2 上下文预算管理 ⚖️

2025 年的模型上下文窗口虽然很大(Gemini 2.5 Pro 支持 1M tokens!),但你不可能无限制塞文档——

  • 💰 成本:每 1000 tokens 都要钱
  • ⏱️ 速度:Prompt 越长、生成越慢
  • 🧠 注意力稀释:”大海捞针”测试显示,Prompt 太长时模型容易”忽略”中间内容
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
def allocate_context_budget(
ranked_docs: list, # 按相关性排好序的文档
max_tokens: int = 4000, # 最大上下文预算
min_per_doc: int = 200 # 每篇文档最少保留
) -> list:
"""智能分配上下文预算:高分文档多给 tokens"""
selected = []
remaining = max_tokens

# 按分数加权分配
total_score = sum(doc["score"] for doc in ranked_docs)

for doc in ranked_docs:
if remaining < min_per_doc:
break
# 按相关性比例分配 tokens
allocated = max(
min_per_doc,
int(doc["score"] / total_score * max_tokens)
)
allocated = min(allocated, remaining) # 不能超预算

selected.append({
"content": doc["content"][:allocated * 4], # 粗略估算
"source": doc["source"],
"score": doc["score"]
})
remaining -= allocated

return selected

2.4.3 引用(Citation)机制

没有引用的 RAG = 新版胡说八道 (눈_눈)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
def format_with_citation(docs: list, answer: str) -> str:
"""给答案添加引用标记"""
citation_map = {}

# 在答案中找到与各文档匹配的句子,添加引用
for i, doc in enumerate(docs):
source_id = f"[{i+1}]"
# 简化做法:追加引用列表
citation_map[source_id] = doc["source"]

# 在答案末尾添加引用列表
citations = "\n\n---\n**参考资料:**\n"
for i, doc in enumerate(docs):
citations += f"[{i+1}] {doc['title']}{doc['source']}\n"

return answer + citations

3. 进阶 RAG:2024-2025 前沿方案 🚀

基础 RAG(Naive RAG)虽然能用,但在实际生产中有很多短板。业界在近几年发展出了成熟的进阶 RAG方案。

3.1 Advanced RAG——给基础 RAG 加”配件” 🧩

3.2 GraphRAG——让 LLM 理解”大局” 🌐

这是 2024 年微软研究院提出的最有影响力的 RAG 改进之一

核心洞察:传统 RAG 只能检索”块级”信息,无法理解数据集的全局结构。比如:

1
2
3
4
5
6
7
8
9
10
11
传统 RAG:
问:"这个数据集主要讨论了哪些主题?"
答:(只能看到检索到的几个文档块,无法概括全部)🤷

GraphRAG:
问:"这个数据集主要讨论了哪些主题?"
答:通过知识图谱聚类发现 5 个主题社区:
1. 医疗健康(占 35%)
2. 教育培训(占 28%)
3. 科技创新(占 20%)
...(基于全局图谱结构)✅

GraphRAG 工作流

GraphRAG 最佳适用场景

  • ✅ 数据集级别的问答(”总结这些文档的主题”)
  • ✅ 发现意外的关联(”A 和 B 之间有什么联系?”)
  • ✅ 大规模语料的宏观分析
  • ❌ 精确事实查询(传统 RAG 更高效)
  • ❌ 实时性要求高的场景(图谱构建慢)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# 简化版 GraphRAG 实现思路
import networkx as nx

# Step 1: 用 LLM 从文档中提取实体和关系
entities_relations = llm.extract_graph(text) # [(实体1, 关系, 实体2), ...]
# e.g., [("Transformer", "提出于", "2017"), ("Transformer", "用于", "NLP"), ...]

# Step 2: 构建 NetworkX 图谱
G = nx.Graph()
for e1, rel, e2 in entities_relations:
G.add_edge(e1, e2, relation=rel)

# Step 3: 社区检测
from networkx.algorithms import community
communities = community.louvain_communities(G)
# → [{节点集1}, {节点集2}, ...]

# Step 4: 对每个社区用 LLM 生成摘要
community_summaries = []
for nodes in communities:
summary = llm.summarize(f"这个社区包含: {nodes}")
community_summaries.append(summary)

微软 GraphRAG 的官方实现:github.com/microsoft/graphrag

GraphRAG 的致命伤:贵 💸

上面的流程看起来很美好,但实际跑一下就知道了——贵得离谱。原因:

  1. 图谱构建阶段:每个文档块都要调 LLM 提取实体和关系,一个中型数据集(10万文档块)就需要 10万+ 次 LLM 调用
  2. 社区摘要生成:Leiden 检测出几百个社区后,每个社区都要调 LLM 做总结
  3. 查询时双路检索:既要向量搜具体事实,又要匹配社区摘要,每次查询的 Token 消耗翻倍

实际测试数据:

1
2
3
4
5
6
7
8
9
数据集:1,000 篇中等长度技术文档(~2M tokens)

GraphRAG 构建成本:
- 实体提取:~50M tokens → GPT-4o 约 ¥250
- 社区摘要:~30M tokens → GPT-4o 约 ¥150
- 总计:~¥400(仅构建一次)

传统 RAG 构建成本:
- Embedding + 建索引:~¥2

100-200 倍的构建成本差异,让很多团队在 GraphRAG 门口直接劝退。

2024-2026:GraphRAG 的”降本增效”演进之路 🚀

好在学术界和工业界的反应极快,两年内涌现了大量改进方案。

1. LazyGraphRAG(2024.11,微软)

微软自己也知道 GraphRAG 太贵了,同年 11 月推出了 LazyGraphRAG:

1
2
GraphRAG:  先建全量图谱 → 社区检测 → 生成摘要 → 查询
LazyGraphRAG:查询时动态构建子图 → 只处理相关部分 → 查询

核心区别:不预先构建全球图谱,而是等用户提问后,才针对问题动态构建一个”局部子图”。就像你不需要背整本百科全书,只需要在考试时翻到相关的几页。

1
2
3
4
5
6
7
成本对比(同上数据集):
GraphRAG 构建 + 查询:~¥400 构建 + ~¥2/次查询
LazyGraphRAG 按需: ~¥0 构建 + ~¥3/次查询

如果你一个月只有 50 次查询:
GraphRAG 总成本 = ¥400 + ¥100 = ¥500
LazyGraphRAG 总成本 = ¥0 + ¥150 = ¥150

参考:LazyGraphRAG: Setting a new standard for quality and cost — Microsoft, 2024.11

2. LightRAG(2024.10,香港大学)

港大提出的轻量级方案,核心创新是双向检索

1
2
3
4
5
6
7
传统 GraphRAG:实体 → 社区摘要 → 答案
LightRAG:
检索层:
├── 高层(全局):社区摘要 → 宏观背景
└── 低层(局部):具体实体/关系 → 精确事实
融合层:
两级结果加权合并 → LLM 生成

LightRAG 把实体和关系存成 (entity, relation, entity) 三元组,然后用向量检索来匹配,不需要预生成社区摘要,构建成本下降了 90%+。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# LightRAG 的简化核心概念
# 不使用 NetworkX 全局分析,而是把图谱三元组向量化
class LightRAG:
def retrieve(self, query):
# 1. 向量检索相关实体
entities = self.entity_index.search(query, top_k=20)

# 2. 沿图谱边扩展 1-2 跳
extended = self.graph.expand(entities, hops=2)

# 3. 检索局部子图的关系
relations = self.get_relations(extended)

# 4. 直接用 "实体+关系" 文本拼接 Prompt
return self.format_context(extended, relations)

参考:LightRAG: Simple and Fast Retrieval-Augmented Generation — HKU, 2024.10

3. nano-graphrag(2025,社区)

更激进的社区方案,目标是 <100 行核心代码 + 兼容本地小模型:

1
2
3
4
nano-graphrag 的设计取舍:
✅ 保留:实体提取 + 关系构建 + 局部图检索
❌ 砍掉:Leiden 社区检测(太慢)+ 社区摘要生成(太贵)
➕ 新增:增量更新(只重建变更部分,不用全量重跑)
4. GraphRAG-SGLang 加速(2025)

用 SGLang 框架把 GraphRAG 的批量 LLM 调用做流水线并行,实体提取阶段提速 5-10x。

GraphRAG 生态全景图(截至 2026)

方案 发布时间 构建成本 查询质量 适合规模 一句话
GraphRAG 2024.07 💸💸💸💸💸 ⭐⭐⭐⭐⭐ 1K-10K 文档 最全最贵,企业级首选
LazyGraphRAG 2024.11 💸 ⭐⭐⭐⭐ 10K-1M 文档 按需构建,成本友好
LightRAG 2024.10 💸💸 ⭐⭐⭐⭐ 1K-100K 文档 双向检索,性价比之王
nano-graphrag 2025 💸 ⭐⭐⭐ 100-10K 文档 本地/小模型友好
GraphRAG + Agent 2025-2026 💸💸💸 ⭐⭐⭐⭐⭐ 灵活规模 Agent 自主决策,最灵活

我该用哪个?——决策指南

2026 年的共识:GraphRAG 不是”用不用”的问题,而是”用哪个版本”的问题。就像数据库——小项目用 SQLite,大项目用 PostgreSQL,不存在一个万能方案。

3.3 Self-RAG——学会”自我反思”的 RAG 🪞

2023 年底提出的 Self-RAG(Self-RAG: Learning to Retrieve, Generate, and Critique through Self-Reflection)引入了”批判性反思”机制。

核心思路:在生成过程中,模型会不断问自己:

  1. 🤔 我需要去检索吗?(有些问题不需要,比如”你好”)
  2. 📋 检索到的内容真的相关吗?
  3. ✅ 我生成的答案是否得到了资料的支持?
  4. 🎯 答案是否完整、有用?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
# Self-RAG 的简化决策流程
def self_rag_pipeline(query: str) -> str:
# Step 1: 判断是否需要检索
need_retrieval = reflection_llm.decide(
f"这个问题需要检索外部知识吗?\n问题:{query}"
)

if need_retrieval == "不需要":
return llm.generate(query) # 直接回答

# Step 2: 检索后逐段评估相关性
docs = retriever.search(query)
relevant_docs = []
for doc in docs:
is_relevant = reflection_llm.judge(
f"这段内容与问题相关吗?\n问题:{query}\n内容:{doc}"
)
if is_relevant:
relevant_docs.append(doc)

# Step 3: 生成答案
answer = llm.generate_with_context(query, relevant_docs)

# Step 4: 自我评估
supported = reflection_llm.verify(
f"答案的每个声明是否都得到了参考资料的支持?\n答案:{answer}"
)

if not supported:
answer = llm.regenerate_with_constraint(
"重新生成,这次只能使用参考资料中的信息"
)

return answer

3.4 Agentic RAG——RAG 的下一个范式 🤖

如果说传统 RAG 是”检索+生成”的固定流水线,那 Agentic RAG 就是让 AI 自己决定每一步该做什么。

Agentic RAG 的核心是工具调用(Tool Use / Function Calling)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# Agentic RAG 的简化实现(使用 LangGraph)
from langgraph.graph import StateGraph, END
from langgraph.prebuilt import ToolNode

tools = [
vector_search_tool, # 向量检索工具
web_search_tool, # 网络搜索工具
sql_query_tool, # 数据库查询工具
calculator_tool, # 计算工具
graph_search_tool, # 知识图谱检索工具
]

# Agent 自主循环:执行 → 观察 → 思考 → 执行...
# 直到 Agent 认为"信息足够了"才生成最终答案

graph = StateGraph(AgentState)
graph.add_node("agent", agent_node) # Agent 思考
graph.add_node("tools", ToolNode(tools)) # 工具执行
graph.add_edge("tools", "agent") # 工具结果 → Agent 继续思考
graph.add_conditional_edges(
"agent",
should_continue, # Agent 自主决定:继续搜 or 结束
{"continue": "tools", "end": END}
)

2025 年 Agentic RAG 的标志性项目

  • LangGraph:LangChain 的状态图 Agent 框架,支持复杂循环和分支
  • CrewAI:多 Agent 协作框架,每个 Agent 有不同角色
  • LlamaIndex Agent:内置丰富的 RAG 专用工具

3.5 更多前沿方向一览 (✧∀✧)

方向 核心思路 代表工作 成熟度
HyDE 用 LLM 生成假设答案,用答案做向量检索 Precise Zero-Shot Dense Retrieval (2023) ⭐⭐⭐ 成熟
RAPTOR 递归摘要+树状索引,支持多粒度检索 RAPTOR (2024) ⭐⭐ 较新
CRAG 检索后自动纠错+补充检索 Corrective RAG (2024) ⭐⭐ 较新
Adaptive RAG 根据问题复杂度自动选择检索策略 Adaptive RAG (2024) ⭐⭐ 较新
ColBERT Token 级别的延迟交互检索 ColBERT v2 / PLAID (2023) ⭐⭐⭐ 成熟
Late Chunking 先 Embed 整篇文档、再在向量空间切块 Jina AI (2024) ⭐⭐ 较新
Contextual Retrieval Anthropic 的上下文 chunk + BM25 Anthropic (2024) ⭐⭐⭐ 成熟

4. RAG 评估——你的系统到底好不好? 📏

把系统搭起来了,然后呢?你需要一个客观的衡量标准。

4.1 RAGAS——最主流的 RAG 评估框架

RAGAS 定义了几个核心指标:

指标 测量什么 怎么算
Faithfulness(忠实度) 答案是否完全基于上下文?(反幻觉) 从答案拆分声明 → 检查每个声明在上下文中能否找到支持
Answer Relevancy(答案相关性) 答案是否切题? 从答案反向生成问题 → 与原始问题算相似度
Context Precision(上下文精准度) 检索到的文档在 Top-K 中位置是否靠前? 高相关文档 rank 越靠前分越高
Context Recall(上下文召回率) 有没有漏掉关键的参考文档? 答案中引用的信息是否覆盖了参考文档中的关键点
Context Relevancy(上下文相关性) 检索到的文档是否多余? 相关文档数 / 总检索文档数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
from ragas import evaluate
from ragas.metrics import (
faithfulness, answer_relevancy,
context_precision, context_recall
)
from datasets import Dataset

# 准备评估数据
eval_data = Dataset.from_dict({
"question": ["RAG 的工作原理是什么?", "什么是 GraphRAG?"],
"answer": ["RAG 通过检索外部知识库来增强...", "GraphRAG 是微软提出的..."],
"contexts": [
["RAG 是检索增强生成技术的缩写...", "LLM 存在幻觉问题..."],
["GraphRAG 使用知识图谱...", "Leiden 社区检测算法..."]
],
"ground_truth": [
"RAG 结合了检索和生成两个阶段,先从知识库检索相关内容,再交给 LLM 生成答案。",
"GraphRAG 是微软在 2024 年提出的 RAG 改进方案,通过构建知识图谱实现全局理解。"
]
})

# 一键评估
result = evaluate(eval_data, metrics=[
faithfulness,
answer_relevancy,
context_precision,
context_recall
])
print(result)

4.2 人工评估 Checklist

自动化评估虽好,但在实际生产上线前,你至少应该人工过一遍:

1
2
3
4
5
6
7
□ 简单事实查询:"数据库连接端口是多少?"(应该精准命中)
□ 复杂多跳查询:"A 和 B 的关系是什么?"(应该综合多文档)
□ 否定查询:"文档里没提到什么?"(模型不应瞎编)
□ 矛盾查询:故意给冲突信息,看模型如何处理
□ 边界查询:"你好""再见"(不需要检索,应该直接回复)
□ 对抗查询:用看似相关但其实无关的问题(检验检索质量)
□ 长尾查询:问很少见的专业问题(检验知识覆盖率)

5. 从零搭建一个 RAG 系统 🔨

理论讲完了,来写一个完整的 Mini RAG 系统吧!(๑•̀ㅂ•́)و✧

5.1 系统架构

5.2 完整代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
"""
mini-rag: A production-ready RAG system in ~300 lines
依赖: pip install qdrant-client sentence-transformers llama-index-core
"""
from pathlib import Path
from typing import List, Optional, Dict, Any
import hashlib

from qdrant_client import QdrantClient
from qdrant_client.models import Distance, VectorParams, PointStruct, Filter
from sentence_transformers import CrossEncoder
from llama_index.core import Settings
from llama_index.core.node_parser import SentenceSplitter
from llama_index.embeddings.huggingface import HuggingFaceEmbedding
from llama_index.readers.file import PyMuPDFReader

# ─── 配置 ─────────────────────────────────────────
EMBED_MODEL_NAME = "BAAI/bge-m3"
RERANKER_MODEL_NAME = "BAAI/bge-reranker-v2-m3"
COLLECTION_NAME = "knowledge_base"
VECTOR_SIZE = 1024
CHUNK_SIZE = 1000
CHUNK_OVERLAP = 200
RETRIEVAL_TOP_K = 20
FINAL_TOP_K = 5


class MiniRAG:
"""一个简洁但功能完整的 RAG 系统"""

def __init__(self, qdrant_url: str = "http://localhost:6333"):
# ─── 向量数据库 ───
self.qdrant = QdrantClient(url=qdrant_url)

# ─── Embedding 模型 ───
self.embed_model = HuggingFaceEmbedding(
model_name=EMBED_MODEL_NAME,
device="cuda"
)

# ─── Reranker ───
self.reranker = CrossEncoder(RERANKER_MODEL_NAME)

# ─── 确保集合存在 ───
self._ensure_collection()

def _ensure_collection(self):
"""创建集合(如果不存在)"""
collections = [c.name for c in self.qdrant.get_collections().collections]
if COLLECTION_NAME not in collections:
self.qdrant.create_collection(
collection_name=COLLECTION_NAME,
vectors_config=VectorParams(
size=VECTOR_SIZE,
distance=Distance.COSINE
)
)
print("✨ 向量集合已创建")

# ═══════════════════════════════════════════════
# 数据摄入
# ═══════════════════════════════════════════════

def ingest_directory(self, dir_path: str) -> int:
"""批量导入目录中的所有 PDF 和 TXT 文件"""
count = 0
for ext in ["*.pdf", "*.txt", "*.md"]:
for file_path in Path(dir_path).rglob(ext):
count += self.ingest_file(str(file_path))
return count

def ingest_file(self, file_path: str) -> int:
"""导入单个文件并返回摄入的 chunk 数"""
# 读取文件
if file_path.endswith(".pdf"):
reader = PyMuPDFReader()
documents = reader.load(file_path)
text = "\n\n".join([doc.text for doc in documents])
else:
with open(file_path, "r", encoding="utf-8") as f:
text = f.read()

# 分块
splitter = SentenceSplitter(
chunk_size=CHUNK_SIZE,
chunk_overlap=CHUNK_OVERLAP
)
chunks = splitter.split_text(text)

# 生成向量并存储
points = []
for i, chunk in enumerate(chunks):
if len(chunk.strip()) < 50: # 跳过太短的块
continue

embedding = self.embed_model.get_text_embedding(chunk)
chunk_id = hashlib.md5(
f"{file_path}_{i}".encode()
).hexdigest()

points.append(PointStruct(
id=chunk_id,
vector=embedding,
payload={
"text": chunk,
"source": file_path,
"chunk_index": i,
"char_count": len(chunk)
}
))

if points:
self.qdrant.upsert(
collection_name=COLLECTION_NAME,
points=points
)

print(f"📥 已摄入: {file_path} ({len(points)} 块)")
return len(points)

# ═══════════════════════════════════════════════
# 检索
# ═══════════════════════════════════════════════

def retrieve(self, query: str, top_k: int = RETRIEVAL_TOP_K) -> List[Dict]:
"""向量检索 + Reranker 精排"""
# Step 1: 向量检索(粗召回)
query_vec = self.embed_model.get_text_embedding(query)
results = self.qdrant.search(
collection_name=COLLECTION_NAME,
query_vector=query_vec,
limit=top_k
)

if not results:
return []

# Step 2: Reranker 精排
pairs = [[query, hit.payload["text"]] for hit in results]
rerank_scores = self.reranker.predict(pairs)

# Step 3: 按 Reranker 分数排序
ranked = sorted(
zip(results, rerank_scores),
key=lambda x: x[1],
reverse=True
)

return [
{
"text": hit.payload["text"],
"source": hit.payload["source"],
"score": float(score)
}
for hit, score in ranked[:FINAL_TOP_K]
]

# ═══════════════════════════════════════════════
# 生成回答(调用 LLM)
# ═══════════════════════════════════════════════

def build_prompt(self, query: str, contexts: List[Dict]) -> str:
"""构建 RAG Prompt"""
context_text = "\n\n".join([
f"[{i+1}] (来源: {ctx['source']}, 相关度: {ctx['score']:.2f})\n{ctx['text']}"
for i, ctx in enumerate(contexts)
])

return f"""你是一个知识丰富、回答精准的 AI 助手。

## 规则
- 严格基于以下参考资料回答问题
- 资料不足时,明确说明"现有资料无法回答该问题"
- 引用资料时注明编号 [1]、[2] 等
- 中文回答,保持友好专业的语气

## 参考资料
{context_text}

## 用户问题
{query}

## 回答"""

def query(self, llm, user_query: str) -> str:
"""端到端查询"""
contexts = self.retrieve(user_query)

if not contexts:
return "抱歉,我在知识库中没有找到相关信息 (´;ω;`)"

prompt = self.build_prompt(user_query, contexts)
answer = llm.generate(prompt) # 你需要实现自己的 LLM 调用

return answer


# ─── 使用示例 ─────────────────────────────────────
if __name__ == "__main__":
# 初始化
rag = MiniRAG(qdrant_url="http://localhost:6333")

# 导入文档
count = rag.ingest_directory("./my_documents/")
print(f"总共摄入 {count} 个文档块 (๑•̀ㅂ•́)و✧")

# 查询
contexts = rag.retrieve("RAG 系统如何减少大模型的幻觉问题?")
for i, ctx in enumerate(contexts):
print(f"\n[{i+1}] 相关度: {ctx['score']:.3f}")
print(f" 来源: {ctx['source']}")
print(f" 内容: {ctx['text'][:200]}...")

5.3 部署 Checklist

在上生产之前,逐项确认:

1
2
3
4
5
6
7
8
☐ 向量数据库持久化配置(重启不丢数据)
☐ Embedding 模型 GPU 推理优化(batch size 调整)
☐ 检索降级策略(向量挂了 → 回退 BM25)
☐ 速率限制和并发控制
☐ 监控指标接入(检索延迟、token 消耗、用户反馈)
☐ 知识库更新策略(增量 or 全量重建?)
☐ Prompt 注入防护(用户输入可能包含恶意指令)
☐ 成本追踪(每次查询花了多少钱?)

6. 常见问题与最佳实践 ⭐

6.1 幻觉为什么还是存在?

即使加了 RAG,幻觉依然可能出现。原因:

  1. 检索没找到正确答案 → LLM 只好自己”编”
  2. 检索到的内容互相矛盾 → LLM 选择性相信某一方
  3. Prompt 没有强调”不准编造” → 给 LLM 明确的角色约束
  4. LLM 的训练惯性 → 它在训练时学到的知识可能覆盖检索到的信息

降低幻觉的实践清单

1
2
3
4
5
✅ 设 Reranker 提高检索精度
✅ Prompt 里加 "如果资料不足,请直接说不知道"(这句真的有用!)
✅ 降低 Temperature(0.1-0.3 比 0.7 更不容易编造)
✅ 用较小的专用模型(如 phi-4)做"事实核查",二次审查答案
✅ 要求 LLM 逐句标注引用

6.2 Chunk Size 设为多少合适?

1
2
3
4
5
6
7
场景经验值:
📄 简单 FAQ → 200-500 tokens(短小精悍)
📚 技术文档 → 500-1000 tokens(最常用范围)
📖 长篇文章 → 1000-2000 tokens(保留完整逻辑)
📜 法律合同/学术论文 → 2000-4000 tokens(需要完整上下文)

经验法则:chunk_overlap = chunk_size × 20%

6.3 多语言支持

中文文档需要特别注意:

  • 分词:中文不像英文有空格分隔,Tokenizer 的行为差异大
  • Embedding 模型选型:选 BGE-M3 / GTE-Qwen2 这类明确支持中文的
  • 混合检索:中文的 BM25 分词要用 jieba 而不是默认的空白分割

7. 展望 2026-2027 🎯

站在 2026 年年中回看,RAG 在过去两年经历了从”玩具”到”基础设施”的蜕变。以下几个趋势正在加速:

7.1 从 RAG 到 Agentic RAG——已成定局

2024 年还在讨论”要不要加 Agent”,2025 年 “Agentic RAG” 已经是默认选项。2026 年的新项目,很少有人会从零手写一个固定流水线的 RAG 了——直接用 LangGraph、CrewAI、OpenAI Agents SDK 搭一个带决策循环的 Agent 系统才是主流做法。

1
2
3
2024:Naive RAG(查 → 拼 → 答)
2025:Agentic RAG(思考 → 搜索 → 评估 → 搜索 → 回答)
2026:Multi-Agent RAG(多个 Agent 协作:检索 Agent + 摘要 Agent + 事实核查 Agent)

7.2 Long Context + RAG = 互补而非替代

Gemini 2.5 Pro 的 1M tokens 窗口、Claude 4 的 200K 窗口都很震撼,但并没有杀死 RAG。三个硬道理:

  1. 成本:把 50 万字的文档全塞进 context window,一次查询 $2+;RAG 检索到 5K tokens,一次查询 $0.02
  2. 延迟:1M tokens 的 prefill 需要数十秒;RAG 的检索只有几百毫秒
  3. 注意力稀释:即便模型能读完一整本书,它对第 3 页和第 300 页的”注意力”是不一样的——检索是在帮模型关注真正重要的部分

2026 年的共识:长上下文解决了”能塞多少”的问题,RAG 解决了”该关注什么”的问题。

7.3 多模态 RAG——文本已经不够了

  • ColPali / ColQwen2(2024-2025):直接用视觉 Embedding 检索 PDF 页面截图,不需要先做 OCR 或文本提取。传统方法”PDF → OCR → 文本 → Embedding”的质量损失链被直接绕过了
  • VideoRAG(2025):对视频做时间轴分片,每段提取关键帧+字幕,混合检索
  • AudioRAG(2025-2026):会议录音、播客内容的语音 RAG,Whisper 转写 + 说话人分离 + 时间戳索引

7.4 本地 RAG 的爆发

2025-2026 年 llama.cpp、Ollama、llamafile 的成熟让”完全本地化的 RAG”从概念验证变成了实际可行的方案:

1
2
3
4
5
一台 M3 Max MacBook(128GB 统一内存):
- Embedding: BGE-M3(1.9GB VRAM)
- LLM: Qwen3-32B-Q4_K_M(~20GB)
- 向量库: LanceDB(进程内,零运维)
- 全部本地运行,数据永不离开你的电脑

企业对数据隐私的要求越来越高,本地 RAG 在企业内部知识库、医疗病历检索、法律文书分析等场景正在快速普及。

7.5 知识图谱的回归

GraphRAG 在 2024-2026 的演进(上文已详述)证明了结构化知识在复杂推理场景的不可替代性。结合 LightRAG、LazyGraphRAG 等降本方案,图 + 向量的混合检索正在成为企业级 RAG 的标准配置。

7.6 RAG 即基础设施

2026 年最明显的变化:RAG 不再是一个”功能”,而是像数据库、缓存、消息队列一样的基础设施层。MCP (Model Context Protocol) 的流行加速了这一趋势——RAG 作为标准化的”知识检索工具”被接入各种 AI 应用中。

7.7 最后一个预测 🔮

到 2027 年,”RAG” 这个词可能会消失——不是因为技术死了,而是因为它已经融入了所有 AI 系统的默认架构。

就像今天我们不会说 “我的 Web 应用使用了 Ajax”,因为 Ajax 已经是所有 Web 应用的标配。同样,未来的 AI 应用不会说”我用了 RAG”,因为”检索增强”已经内置在所有 LLM 应用的基因里。


参考资料 📚

  1. Retrieval-Augmented Generation for Knowledge-Intensive NLP Tasks — RAG 原始论文 (Lewis et al., 2020)
  2. GraphRAG: Unlocking LLM discovery on narrative private data — Microsoft, 2024
  3. LazyGraphRAG: Setting a new standard for quality and cost — Microsoft, 2024
  4. LightRAG: Simple and Fast Retrieval-Augmented Generation — HKU, 2024
  5. Self-RAG: Learning to Retrieve, Generate, and Critique through Self-Reflection — Asai et al., 2023
  6. BGE-M3 技术报告 — BAAI, 2024
  7. RAGAS: Automated Evaluation of Retrieval Augmented Generation — 2023
  8. RAPTOR: Recursive Abstractive Processing for Tree-Organized Retrieval — 2024
  9. LangGraph 官方文档
  10. Anthropic - Contextual Retrieval — 2024
  11. ColPali: Efficient Document Retrieval with Vision Language Models — 2024

写在最后 💌

如果只能记住三件事:

  1. RAG 的核心不是”检索”,而是”检索到了对的东西”——Reranker、混合检索、查询重写比花里胡哨的架构重要 10 倍
  2. 没有银弹——Naive RAG / GraphRAG / Agentic RAG / Self-RAG 各有适用场景,不是越新越好,而是越匹配越好
  3. 先跑通再优化——一个能工作的朴素 RAG,比一个永远搭不完的”完美架构”有价值一万倍

这篇文章从基础概念一路讲到 MiniRAG 的完整实现,横跨了 RAG 生态的方方面面。希望它能帮你少走弯路,快速搭建起自己的 RAG 系统 (◍•ᴗ•◍)❤

如果你在实践过程中遇到问题、有更好的方案,或者发现了本文的疏漏——欢迎在评论区交流讨论,我们一起把 RAG 这件事做得更好。

下一篇:向量数据库深度解析——从原理到选型 —— RAG 的”引擎”怎么选?万字长文,现已发布。

Happy RAGging! 🚀