RAG介绍

RAG 详细介绍

RAG(Retrieval-Augmented Generation,检索增强生成) 是一种将外部知识检索大语言模型生成相结合的技术架构。它解决的核心问题是:LLM 的训练数据有截止日期,且不包含你的私有数据,通过 RAG 可以让 LLM “现查现答”。


一、为什么需要 RAG

LLM 有三个根本性限制:

限制 表现 RAG 如何解决
知识截止 模型不知道训练数据之后发生的事 实时检索最新文档
无私有数据 模型不了解你的公司内部文档、代码、业务数据 检索你的私有知识库
幻觉问题 模型会一本正经地编造事实 基于真实文档生成,可溯源验证

其他方案对比:

方案 原理 缺点
微调 Fine-tuning 用你的数据重新训练模型 成本高、更新慢、不适合频繁变化的知识
长上下文直接塞 把所有文档放进 Prompt 有 token 上限、成本高、检索不精确
RAG 先检索相关片段,再交给 LLM 灵活、成本低、知识可实时更新

二、RAG 的核心工作流程

整个流程分为两大阶段:离线索引在线查询

阶段一:离线索引(Indexing) —— 构建知识库

把你的文档变成 LLM 能高效检索的格式。

1
2
3
4
5
6
7
8
原始文档                   分块                    向量化                  存入向量数据库
┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────────┐
│ PDF │ │ Chunk 1 │ │ [0.12, │ │ │
│ Markdown │ ───► │ Chunk 2 │ ───► │ 0.87, │ ───► │ Vector DB │
│ Word │ Split │ Chunk 3 │ Embed │ -0.34, │ Store │ (Chroma/ │
│ SQL │ │ Chunk 4 │ │ ...] │ │ Pinecone) │
│ 网页 │ │ ... │ │ │ │ │
└──────────┘ └──────────┘ └──────────┘ └──────────────┘

步骤详解

第 1 步:文档加载(Load)

把各种格式的原始文档读取为纯文本。

1
2
3
4
5
6
7
8
# 支持的文档类型示例
PDF → PyPDF / Unstructured 解析
Markdown → 直接读取
Word → python-docx 解析
网页 → BeautifulSoup / Playwright 爬取
数据库 → 查询结果转文本
代码文件 → 直接读取 + 保留文件路径元信息
Figma设计稿 → 导出标注文档后读取

第 2 步:文档分块(Chunk / Split)

大文档不能整篇存入,需要切成小段。这是 RAG 质量的关键环节

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

splitter = RecursiveCharacterTextSplitter(
chunk_size=500, # 每块约 500 个字符
chunk_overlap=100, # 相邻块重叠 100 个字符,保证上下文连贯
separators=["\n\n", "\n", "。", ".", " "], # 优先在段落/句子边界切
)

chunks = splitter.split_documents(documents)

分块策略的核心考量:

参数 太小 太大
chunk_size 丢失上下文,答案碎片化 混入无关内容,检索精度下降
chunk_overlap 相邻块断裂,语义不连贯 冗余存储,检索到重复内容

常见的分块策略:

1
2
3
4
5
1. 固定大小分块          → 简单粗暴,适合通用场景
2. 按语义段落分块 → 按标题/段落自然边界切分
3. 递归分块 → 先按大边界切,切不开再用小边界(最常用)
4. 按代码结构分块 → 按函数/类/模块切分代码文件
5. 父子分块(Parent-Child)→ 检索用小块,传给LLM时用大块

第 3 步:向量化(Embedding)

将每个文本块转化为一个高维数学向量(通常 768~3072 维)。语义相近的文本,向量距离也近

1
2
3
4
5
6
7
8
9
10
11
from openai import OpenAI

client = OpenAI()

response = client.embeddings.create(
model="text-embedding-3-small", # OpenAI 的 Embedding 模型
input="用户登录后跳转到仪表盘页面"
)

vector = response.data[0].embedding
# 结果:[0.0123, -0.0456, 0.0789, ...] (1536 维浮点数数组)

为什么要向量化?因为关键词搜索有局限:

1
2
3
4
5
用户问:"怎么查看数据面板"
文档写的是:"导航到 Dashboard 页面查看统计信息"

关键词搜索 → 匹配不上(没有共同关键词)
向量搜索 → 能匹配(语义相近,向量距离小)

常见 Embedding 模型:

模型 维度 特点
OpenAI text-embedding-3-small 1536 性价比高,英文和中文都不错
OpenAI text-embedding-3-large 3072 更精确,成本更高
BAAI/bge-large-zh-v1.5 1024 开源,中文表现优秀,可本地部署
nomic-embed-text 768 开源,配合 Ollama 可完全本地化

第 4 步:存入向量数据库(Vector Store)

向量数据库专门用于高效存储和检索高维向量。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import chromadb

client = chromadb.PersistentClient(path="./vector_db")

collection = client.get_or_create_collection(
name="project_docs",
metadata={"hnsw:space": "cosine"} # 使用余弦相似度
)

# 批量存入
collection.add(
ids=["doc_1", "doc_2", "doc_3"],
documents=["文本块1", "文本块2", "文本块3"],
embeddings=[vector_1, vector_2, vector_3],
metadatas=[
{"source": "prd.md", "section": "登录模块"},
{"source": "schema.sql", "table": "users"},
{"source": "api.md", "endpoint": "/dashboard"},
]
)

常见向量数据库:

数据库 部署方式 适合场景
ChromaDB 本地/嵌入式 原型开发、小型项目
PostgreSQL + pgvector 本地/云 已有 PG 的项目(比如你用 Supabase)
Pinecone 云托管 生产环境,免运维
Weaviate 自托管/云 需要混合搜索(向量+关键词)
Qdrant 自托管/云 高性能,支持过滤
FAISS 内存/本地 纯研究实验用

阶段二:在线查询(Retrieval + Generation) —— 用户提问时

1
2
3
4
5
6
用户提问                    向量化查询            检索相关文档             LLM 生成回答
┌──────────┐ ┌──────────┐ ┌──────────────┐ ┌──────────┐
│ "仪表盘 │ │ Query │ │ 匹配到的 │ │ 基于检索 │
│ 数据怎么 │ ───► │ Vector │ ───► │ Top-K 文档块 │ ───► │ 到的内容 │
│ 看?" │ Embed │ [0.1,..] │ Search │ + 相似度分数 │ Prompt │ 生成答案 │
└──────────┘ └──────────┘ └──────────────┘ └──────────┘

步骤详解

第 1 步:查询向量化

把用户的问题用同一个 Embedding 模型转为向量。

第 2 步:向量检索

在向量数据库中找到与查询向量最相似的 Top-K 个文档块。

1
2
3
4
5
6
7
8
9
10
11
12
13
results = collection.query(
query_texts=["仪表盘数据怎么看"],
n_results=5, # 返回最相似的 5 个块
)

# 结果示例:
# results["documents"][0] = [
# "Dashboard 页面展示关键业务指标,包括日活用户数...",
# "console_dashboard_rpc_v2 函数返回组织的统计摘要...",
# "进入控制台后,首页即为仪表盘,顶部有时间筛选器...",
# ...
# ]
# results["distances"][0] = [0.12, 0.18, 0.23, ...] # 距离越小越相似

第 3 步:构造增强 Prompt

将检索到的文档块拼入 Prompt,让 LLM 基于这些真实内容回答。

1
2
3
4
5
6
7
8
9
10
11
12
13
context_docs = "\n\n---\n\n".join(results["documents"][0])

prompt = f"""请基于以下参考资料回答用户的问题。
如果参考资料中没有相关信息,请明确说明你不确定。
不要编造参考资料中没有的内容。

## 参考资料
{context_docs}

## 用户问题
仪表盘数据怎么看?

## 回答"""

第 4 步:LLM 生成回答

LLM 基于 Prompt 中的真实文档内容生成回答,而不是凭空编造。


三、进阶 RAG 技术

基础 RAG(上面介绍的)也叫 Naive RAG。实际生产中常用更高级的变体:

1. 查询优化(Query Transformation)

用户的问题往往不适合直接检索,需要先优化:

1
2
3
4
5
6
7
8
9
原始问题:"这个项目的数据库有啥问题"


┌──── 查询改写 ────┐
│ │
"数据库性能瓶颈" "数据库Schema设计问题" ← 拆成多个子查询
│ │
▼ ▼
分别检索,合并结果

常见技术:

  • 查询改写(Query Rewriting):让 LLM 将模糊问题改写为更适合检索的精确表述
  • HyDE(假设文档嵌入):让 LLM 先生成一段假设性答案,用这段答案去检索(往往比用问题检索更准)
  • 多查询(Multi-Query):将一个问题生成多个角度的子查询,分别检索后合并

2. 混合检索(Hybrid Search)

向量搜索擅长语义匹配,但对精确关键词(如函数名 console_dashboard_rpc_v2)可能不如关键词搜索。混合检索结合两者:

1
2
3
4
5
6
7
用户查询

├──► 向量搜索(语义相似度)──► 结果集 A

├──► 关键词搜索(BM25) ──► 结果集 B

└──► RRF 融合排序 ──► 最终结果(取两者之长)

3. 重排序(Re-ranking)

初步检索返回的 Top-K 结果中,排序不一定准确。用一个专门的重排序模型精细打分:

1
初步检索 Top-20 ──► Reranker 模型精排 ──► 取 Top-5 给 LLM

常用重排序模型:Cohere Rerank、BAAI/bge-reranker-v2-m3(开源)。

4. 父子分块(Parent-Child Chunking)

检索时用小块保证精确度,传给 LLM 时返回该小块所属的大块保证上下文完整:

1
2
3
4
5
6
7
8
9
大文档
├── 父块 1(2000 字)
│ ├── 子块 1.1(200 字) ← 命中检索
│ ├── 子块 1.2(200 字)
│ └── 子块 1.3(200 字)
├── 父块 2(2000 字)
│ └── ...

检索命中子块 1.1 → 返回父块 1 整段给 LLM

5. 自适应 RAG(Adaptive RAG)

不是所有问题都需要检索。让 LLM 先判断是否需要查资料:

1
2
3
4
5
6
7
8
用户问题


路由判断(LLM/分类器)

├── 需要检索 → RAG 流程
├── 直接回答 → LLM 直接生成
└── 需要工具 → 调用 API / 数据库

四、RAG 系统的典型架构图

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
┌─────────────────────────────────────────────────────────────┐
│ 用户界面 │
└──────────────────────────┬──────────────────────────────────┘
│ 用户提问

┌─────────────────────────────────────────────────────────────┐
│ 查询处理层 │
│ ┌──────────┐ ┌──────────────┐ ┌────────────────────┐ │
│ │ 查询改写 │ │ 意图识别/路由 │ │ 多查询生成 │ │
│ └──────────┘ └──────────────┘ └────────────────────┘ │
└──────────────────────────┬──────────────────────────────────┘
│ 优化后的查询

┌─────────────────────────────────────────────────────────────┐
│ 检索层 │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────────┐ │
│ │ 向量检索 │ │ 关键词检索 │ │ 知识图谱检索 │ │
│ │ (Semantic) │ │ (BM25) │ │ (Graph RAG) │ │
│ └──────┬───────┘ └──────┬───────┘ └────────┬─────────┘ │
│ └──────────────────┼───────────────────┘ │
│ ▼ │
│ ┌──────────────┐ │
│ │ 融合 + 重排序 │ │
│ └──────────────┘ │
└──────────────────────────┬──────────────────────────────────┘
│ Top-K 相关文档

┌─────────────────────────────────────────────────────────────┐
│ 生成层 │
│ ┌──────────────────────────────────────────────────────┐ │
│ │ System Prompt + 检索到的文档 + 用户问题 → LLM → 回答 │ │
│ └──────────────────────────────────────────────────────┘ │
│ ┌──────────────┐ ┌──────────────┐ │
│ │ 答案质量检查 │ │ 幻觉检测 │ │
│ └──────────────┘ └──────────────┘ │
└──────────────────────────┬──────────────────────────────────┘
│ 最终回答(附来源引用)

返回用户

五、RAG 与其他技术的关系

1
2
3
4
5
6
7
8
9
10
11
            ┌─────────────────┐
│ AI Agent │ ← Agent 可以把 RAG 作为一个"工具"
│ (自主行动) │
└────────┬────────┘
│ 调用
┌──────────────┼──────────────┐
▼ ▼ ▼
┌──────────┐ ┌──────────┐ ┌──────────┐
│ RAG │ │ 搜索API │ │ 数据库 │
│ (知识检索)│ │ (联网) │ │ (查询) │
└──────────┘ └──────────┘ └──────────┘
  • RAG 是一种”给 LLM 补充知识”的技术,本身不具备行动能力
  • Agent 是”能自主行动的系统”,可以把 RAG 当作其中一个工具
  • 两者经常组合使用:Agent 需要查资料时调用 RAG,需要执行操作时调用其他工具

六、一句话总结

RAG = 先从你的知识库中找到相关内容,再让 LLM 基于这些真实内容回答问题。 它让 LLM 从”凭记忆回答”变成”查资料后回答”,大幅提升准确性和可控性,是当前将 LLM 应用于私有数据场景最主流的技术方案。