RAG

引言:RAG——chunking+向量数据库+大语言模型。面对大模型上下文有限的情况下所做的妥协

RAG

RAG(Retrieval-Augmented Generation)是一种结合了信息检索文本生成的AI技术。

它通过在生成回答之前先检索相关的外部知识,来提高大语言模型回答的准确性和时效性。

简单来说RAG是一个向量数据库+嵌入模型

RAG本质要解决的问题

有时候我们的问题是有一部分文档做支撑的,比如公司的某SOP,当你的问题是关于这些时,那么大模型没有此上文依赖,就会一本正经的胡说八道。

RAG就是为了解决这个问题而提出的,可以把文档中相关于我们问题的一部分抽取出来,询问大模型,然后大模型再给出他的回答。

RAG其实是在面对大模型上下文有限的情况下所做的妥协

RAG的核心组件

RAG系统主要由两个核心组件构成:

R——检索器,从大量文档或知识库中找到与用户问题最相关的信息,一般使用向量数据库

G——生成器,基于检索到的相关信息生成最终回答,依赖LLM(chatgpt、deepseek)

RAG系统架构全貌

很多人以为RAG就是”向量数据库+模型”,这个理解基本正确,但实际上RAG是一个更复杂的系统。

RAG系统的典型工作流程如下:

1
用户问题 → 文档预处理 → 向量化 → 相似度检索 → 上下文增强 → 生成回答
  1. 文档预处理:将文档切分成小块(chunking),清理和格式化文本,去除噪声内容,转换为向量
    • chunking这一步非常关键,决定了我们建立向量的质量
    • 切割的方式有很多种,比如按字数、按句子等
    • 但每种方式都有其缺点,比如按句子划分“我是李白。我爱作诗“,在这种情况下,用户提问李白喜欢做什么,与“我爱作诗”可能就会差很远。
  2. 向量化存储:使用嵌入模型(如OpenAI Embeddings、Sentence-BERT)将文档块转换为向量,然后存储到向量数据库中
    • 嵌入:将非结构化数据转换成计算机可以理解的数值数据(向量)的过程
    • 切块后的向量维度一般是固定的,比如text-embedding-3-small转换后的向量维度一般在1000维
  3. 用户查询处理:将用户问题也转换为向量表示,在向量空间中计算相似度
    • 计算向量之间的距离或角度来判断向量的偏差,进而找到TopK,得到最准确的答案
    • 一般在系统内会预设提示词(System promote),比如:你是一个程序员,你精通xxx,请你回答问题
  4. 检索相关文档:找出最相关的Top-K个文档块,通常K=3到10之间
  5. 上下文构建:将检索到的文档内容与用户问题组合,形成完整的prompt
  6. 生成最终回答:大语言模型基于增强后的上下文生成回答、确保回答基于检索到的事实信息

完整架构图

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
┌─────────────────┐    ┌─────────────────┐    ┌─────────────────┐
│ 文档输入 │ │ 用户查询 │ │ 最终回答 │
│ (PDF/Word/ │ │ "RAG是什么?" │ │ "RAG是一种..." │
│ Web/...) │ │ │ │ │
└─────────┬───────┘ └─────────┬───────┘ └─────────┬───────┘
│ │ ▲
▼ ▼ │
┌─────────────────┐ ┌─────────────────┐ │
│ 文档处理模块 │ │ 查询处理模块 │ │
│ • 文本清理 │ │ • 查询理解 │ │
│ • 文档分块 │ │ • 查询扩展 │ │
│ • 元数据提取 │ │ • 意图识别 │ │
└─────────┬───────┘ └─────────┬───────┘ │
│ │ │
▼ ▼ │
┌─────────────────┐ ┌─────────────────┐ │
│ 嵌入模型 │ │ 嵌入模型 │ │
│ • 文档向量化 │ │ • 查询向量化 │ │
│ • 批量处理 │ │ • 实时处理 │ │
└─────────┬───────┘ └─────────┬───────┘ │
│ │ │
▼ ▼ │
┌─────────────────┐ ┌─────────────────┐ │
│ 向量数据库 │◄───┤ 检索引擎 │ │
│ • 向量存储 │ │ • 相似度搜索 │ │
│ • 索引管理 │ │ • 结果排序 │ │
│ • 元数据存储 │ │ • 结果过滤 │ │
└─────────────────┘ └─────────┬───────┘ │
│ │
▼ │
┌─────────────────┐ │
│ 上下文构建 │ │
│ • 文档整合 │ │
│ • Prompt工程 │ │
│ • 长度控制 │ │
└─────────┬───────┘ │
│ │
▼ │
┌─────────────────┐ │
│ 生成模型 │──────────────┘
│ • 文本生成 │
│ • 答案合成 │
│ • 质量控制 │
└─────────────────┘

为什么不只是”向量数据库+模型”?

简化理解(适合学习):

1
RAG ≈ 向量数据库 + 生成模型

完整实现(适合生产):

1
2
RAG = 文档处理 + 嵌入模型 + 向量数据库 + 检索引擎 + 
上下文管理 + 生成模型 + 质量控制 + 系统编排
  1. 文档处理pipeline:不同格式文档的解析、文本清理和标准化、智能分块策略
  2. 检索优化:查询理解和扩展、多轮检索策略、结果重排序(Re-ranking)
  3. 上下文管理:长度控制(避免超出模型限制)、信息去重和整合、Prompt模板管理
  4. 质量控制:回答相关性检测、幻觉检测和过滤、置信度评估
  5. 系统编排:错误处理和恢复、性能监控、缓存机制

各组件的作用占比

组件 重要性 复杂度 对效果的影响
向量数据库 ⭐⭐⭐⭐⭐ ⭐⭐⭐ 40%
生成模型 ⭐⭐⭐⭐⭐ ⭐⭐ 30%
文档处理 ⭐⭐⭐⭐ ⭐⭐⭐⭐ 15%
检索优化 ⭐⭐⭐ ⭐⭐⭐⭐ 10%
上下文管理 ⭐⭐⭐ ⭐⭐⭐ 5%

关键洞察:

  • 向量数据库和生成模型确实是核心,占70%的效果
  • 但剩余30%的优化往往决定了系统的实用性
  • 生产级RAG系统需要考虑所有组件的协同工作

开源框架

  1. LangChain

    • 最流行的RAG开发框架
    • 丰富的文档加载器和向量存储支持
    • 易于集成各种LLM
  2. LlamaIndex

    • 专注于数据连接和索引
    • 强大的查询引擎
    • 支持复杂的文档结构
  3. Haystack

    • Deepset开发的开源框架
    • 企业级解决方案
    • 强大的pipeline管理

向量数据库

什么是向量数据库?

向量数据库(Vector Database) 是一种专门用于存储、索引和查询高维向量数据的数据库系统。

在RAG和AI应用中,它是实现语义搜索的核心技术。

核心概念:向量、嵌入、相似度计算

1. 向量(Vector):向量是一组数字组成的数组,代表数据的数学表示

示例:[0.1, 0.8, -0.3, 0.5, ...](通常有几百到几千个维度)

可以将文本、图像等非结构化数据转换为计算机可以理解的数值形式(非结构化数据转为可理解的数值形式)

2. 嵌入(Embedding):使用AI模型将文本转换为向量的过程

语义相似的文本会产生相似的向量

1
2
3
"RAG是什么?" → [0.1, 0.8, -0.3, 0.5, ...]
"什么是RAG?" → [0.1, 0.7, -0.2, 0.6, ...] (相似向量)
"今天天气好" → [0.9, 0.1, 0.8, -0.4, ...] (不同向量)

3. 相似度计算

相似度计算是向量数据库的核心,决定了能否准确找到相关文档。以下是三种主要的相似度计算方法:

余弦相似度(Cosine Similarity) - 最常用的方法

  • 原理:计算两个向量之间的夹角余弦值
  • 公式cos(θ) = (A·B) / (|A| × |B|)
  • 取值范围:-1 到 1,值越接近1越相似
  • 优势:不受向量长度影响,适合文本相似度

具体例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 示例向量
vector_A = [1, 2, 3] # "RAG是什么"
vector_B = [1, 2, 2] # "什么是RAG"
vector_C = [4, 0, 1] # "今天天气好"

# 计算余弦相似度
import math

def cosine_similarity(v1, v2):
dot_product = sum(a*b for a,b in zip(v1,v2))
norm_a = math.sqrt(sum(a*a for a in v1))
norm_b = math.sqrt(sum(a*a for a in v2))
return dot_product / (norm_a * norm_b)

# 结果对比
sim_AB = cosine_similarity(vector_A, vector_B) # 0.99 (非常相似)
sim_AC = cosine_similarity(vector_A, vector_C) # 0.53 (不太相似)

欧几里得距离(Euclidean Distance)

  • 原理:计算两点间的直线距离
  • 公式d = √[(x₁-x₂)² + (y₁-y₂)² + ... + (n₁-n₂)²]
  • 取值范围:0 到 ∞,值越小越相似
  • 特点:受向量长度和维度影响较大

具体例子:

1
2
3
4
5
6
7
8
9
10
def euclidean_distance(v1, v2):
return math.sqrt(sum((a-b)**2 for a,b in zip(v1,v2)))

# 使用相同的向量
dist_AB = euclidean_distance(vector_A, vector_B) # 1.0 (距离较近)
dist_AC = euclidean_distance(vector_A, vector_C) # 4.58 (距离较远)

# 转换为相似度 (0到1之间)
sim_AB = 1 / (1 + dist_AB) # 0.5
sim_AC = 1 / (1 + dist_AC) # 0.18

点积(Dot Product)

  • 原理:向量对应元素相乘后求和
  • 公式A·B = a₁×b₁ + a₂×b₂ + ... + aₙ×bₙ
  • 特点:计算最快,但受向量长度影响
  • 适用:向量已归一化的场景

具体例子:

1
2
3
4
5
6
7
8
9
10
def dot_product(v1, v2):
return sum(a*b for a,b in zip(v1,v2))

# 使用归一化向量
norm_A = [0.27, 0.53, 0.80] # 归一化后的向量A
norm_B = [0.35, 0.70, 0.60] # 归一化后的向量B
norm_C = [0.95, 0.00, 0.24] # 归一化后的向量C

dot_AB = dot_product(norm_A, norm_B) # 0.85 (高相似度)
dot_AC = dot_product(norm_A, norm_C) # 0.45 (低相似度)

实际应用中的选择:

方法 优势 劣势 适用场景
余弦相似度 不受长度影响,语义准确 计算稍复杂 文本搜索、推荐系统
欧几里得距离 直观易懂,几何意义明确 受维度影响大 图像识别、聚类分析
点积 计算最快,内存友好 需要归一化预处理 大规模实时搜索

RAG系统中的选择

  • 推荐:余弦相似度(90%的RAG系统都使用)
  • 原因:文本向量通常关注方向而非长度,余弦相似度能更好地捕捉语义相似性
  • 优化:可以预先归一化向量,然后使用点积代替余弦相似度计算

PS:向量归一化(Vector Normalization)

  • 定义:将向量转换为单位向量(长度为1)的过程,保持方向不变
  • 目的:消除向量长度差异,让相似度计算更准确
  • 公式v_norm = v / |v|,其中 |v| = √(v₁² + v₂² + ... + vₙ²)

归一化示例:

1
2
3
4
5
6
7
8
9
10
11
12
# 原始向量
vector = [3, 4, 0]
length = √(3² + 4² + 0²) = √25 = 5

# 归一化后
normalized = [3/5, 4/5, 0/5] = [0.6, 0.8, 0.0]
# 验证:√(0.6² + 0.8² + 0²) = 1 ✓

# 文本向量示例
text_vector = [1, 2, 3, 4]
length = √(1² + 2² + 3² + 4²) = √30 = 5.477
normalized = [0.183, 0.365, 0.548, 0.730]

归一化的优势:

  • 公平比较:不同长度的文本产生的向量可以公平比较
  • 计算简化:归一化后,余弦相似度 = 点积,计算更快
  • 数值稳定:避免因向量长度差异导致的数值不稳定
  • 语义专注:突出向量方向(语义),忽略大小(文本长度)

归一化对相似度计算的影响:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 示例:不同长度文本的向量对比
short_text = [1, 1, 1] # 短文本
long_text = [2, 2, 2] # 长文本(内容相似但更长)

# 未归一化的点积
dot_product = 1*2 + 1*2 + 1*2 = 6 # 偏向长文本

# 归一化后的向量
short_norm = [0.577, 0.577, 0.577] # 长度=1
long_norm = [0.577, 0.577, 0.577] # 长度=1

# 归一化后的点积(等于余弦相似度)
normalized_dot = 0.577*0.577 + 0.577*0.577 + 0.577*0.577 = 1.0
# 结果:完全相似!这才是我们想要的结果

向量数据库工作原理

存储阶段:

1
文档 → 分块 → 嵌入模型 → 向量 → 向量数据库

查询阶段:

1
用户问题 → 嵌入模型 → 查询向量 → 相似度搜索 → 相关文档

为什么需要向量数据库?

1. 语义搜索能力

传统搜索:基于关键词匹配,而向量搜索基于语义理解

示例:查询”如何做饭?”能找到”烹饪方法”、”cooking tips”等相关内容

2. 多语言支持

  • 理解同义词和概念
  • 支持跨语言查询
  • 处理语言变体和方言

3. 高效检索

  • 专门的索引结构(HNSW、IVF等)
  • 快速的相似度计算
  • 支持大规模数据检索

性能考虑

1. 向量维度

  • 更高维度:更精确,但存储和计算开销大
  • 常见维度:384、768、1536(取决于嵌入模型)
  • 需要在精度和性能间平衡

2. 索引类型

  • HNSW:高精度,适合查询密集型应用
  • IVF:适合大规模数据,支持近似搜索
  • LSH:局部敏感哈希,适合快速近似搜索

3. 存储优化

  • 量化:减少存储空间,牺牲少量精度
  • 压缩:平衡精度和性能
  • 分片:支持分布式存储和查询

市场主流向量数据库对比

🏆 市场份额排名(2024年)

排名 产品 市场份额 类型 主要优势
🥇 Pinecone ~35% 云托管 易用性、性能、企业功能
🥈 Chroma ~25% 开源 轻量级、Python友好
🥉 Weaviate ~15% 开源+云 功能丰富、多模态
4️⃣ Qdrant ~12% 开源+云 高性能、Rust编写
5️⃣ Milvus ~8% 开源+云 企业级、大规模
6️⃣ 其他 ~5% 各种 FAISS、OpenSearch等

🥇 最受欢迎:Pinecone

  • ✅ 市场占有率最高,生态最成熟;托管服务,零运维成本;性能优秀,延迟低;企业级功能完善
  • ❌ 成本较高,按使用量付费;供应商锁定风险

🥈 开源首选:Chroma

  • ✅ 开源社区最活跃;极易上手,5分钟可部署;本地开发友好
  • ❌ 功能相对简单;不适合大规模生产

🤔 特殊提及:Neo4j不是传统向量数据库,而是图数据库,但:

  • Neo4j 5.0+ 新增向量索引功能
  • 主要用于图+向量混合查询
  • 适合知识图谱+RAG结合的场景
  • 如果只做向量搜索,不推荐使用

向量数据库演示代码

为了更好地理解向量数据库的工作原理,我们提供了一个简化的演示程序:

核心方法:嵌入 + 相似度计算

1
2
3
4
5
6
7
8
9
10
11
12
13
# 嵌入:向量转换
def simple_hash_embedding(text, dimension=5):
"""简单的哈希嵌入(仅用于演示概念)"""
return [hash(text + str(i)) % 100 / 100.0 for i in range(dimension)]
# 相似度计算
def cosine_similarity(v1, v2):
"""计算余弦相似度"""
dot = sum(a*b for a,b in zip(v1,v2))
norm1 = math.sqrt(sum(a*a for a in v1))
norm2 = math.sqrt(sum(a*a for a in v2))
if norm1 == 0 or norm2 == 0:
return 0
return dot / (norm1 * norm2)

完整案例

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
def vector_database_demo():
"""向量数据库工作原理演示"""
print("🔍 向量数据库概念演示")
print("=" * 40)

# 示例文档库
documents = [
"RAG是什么?RAG是一种AI技术",
"向量数据库用于存储向量数据",
"Python是一种编程语言",
"机器学习需要大量数据"
]

# 1. 文档向量化
print("📚 步骤1:文档向量化")
doc_vectors = []
for i, doc in enumerate(documents):
vector = simple_hash_embedding(doc)
doc_vectors.append(vector)
print(f"文档{i+1}: {doc}")
print(f" 向量: {[round(v, 3) for v in vector]}")

# 2. 查询处理
print("\n🔍 步骤2:查询处理")
queries = ["什么是RAG?", "数据库", "编程"]

for query in queries:
print(f"\n查询: '{query}'")
query_vector = simple_hash_embedding(query)
print(f"查询向量: {[round(v, 3) for v in query_vector]}")

# 3. 相似度计算和排序
similarities = []
for i, doc_vector in enumerate(doc_vectors):
sim = cosine_similarity(query_vector, doc_vector)
similarities.append((sim, i, documents[i]))

similarities.sort(key=lambda x: x[0], reverse=True)

print("📊 相似度排序结果:")
for sim, idx, doc in similarities:
print(f" 文档{idx+1}: {sim:.3f} - {doc}")

print(f"🎯 最相关文档: 文档{similarities[0][1]+1}")

if __name__ == "__main__":
vector_database_demo()

运行示例输出:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
🔍 向量数据库概念演示
========================================
📚 步骤1:文档向量化
文档1: RAG是什么?RAG是一种AI技术
向量: [0.68, 0.8, 0.4, 0.08, 0.72]
文档2: 向量数据库用于存储向量数据
向量: [0.9, 0.71, 0.2, 0.59, 0.13]

🔍 步骤2:查询处理

查询: '什么是RAG?'
查询向量: [0.34, 0.95, 0.9, 0.65, 0.74]
📊 相似度排序结果:
文档1: 0.866 - RAG是什么?RAG是一种AI技术
文档2: 0.748 - 向量数据库用于存储向量数据
🎯 最相关文档: 文档1

这个演示展示了:

  1. 文档向量化:将文本转换为数值向量
  2. 查询向量化:用户问题也转换为向量
  3. 相似度计算:通过数学方法找到最相关的文档
  4. 结果排序:按相似度高低排列搜索结果

虽然这是一个简化的示例,但它准确地反映了向量数据库在RAG系统中的核心工作原理。

大语言模型选择

RAG系统中的生成模型(LLM)负责基于检索到的上下文生成最终回答。不同模型有各自的特点和适用场景。

中文用DS,英文用GPT

模型 成本 中文能力 上下文长度 RAG适配度 推荐指数
DeepSeek-V3 ⭐⭐⭐⭐⭐ ⭐⭐⭐⭐⭐ 64K ⭐⭐⭐⭐⭐ ⭐⭐⭐⭐⭐
GPT-4o ⭐⭐ ⭐⭐⭐⭐ 128K ⭐⭐⭐⭐⭐ ⭐⭐⭐⭐
Claude-3.5 ⭐⭐⭐ ⭐⭐⭐⭐ 200K ⭐⭐⭐⭐⭐ ⭐⭐⭐⭐
GPT-3.5 ⭐⭐⭐⭐ ⭐⭐⭐ 16K ⭐⭐⭐⭐ ⭐⭐⭐
本地模型 ⭐⭐⭐⭐⭐ ⭐⭐⭐ 变化 ⭐⭐⭐ ⭐⭐⭐

LangChain框架深度解析

什么是LangChain?

LangChain 是目前最流行的大语言模型应用开发框架,由Harrison Chase在2022年创建。它的核心理念是**”链式组合”**,将不同的组件(如模型、数据源、工具等)串联起来,构建复杂的AI应用。

LangChain架构体系

1
2
3
4
5
6
7
8
9
用户查询 → 🔗 RetrievalQA Chain

📄 DirectoryLoader → ✂️ RecursiveCharacterTextSplitter

🧮 Embeddings Model → 🗄️ Vector Store (Chroma)

🔍 Similarity Retriever → 📝 Custom Prompt Template

🤖 LLM Model (DeepSeek) → 💬 Structured Response

LangChain关键组件详解

1. 📄 Document Loaders(文档加载器)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
from langchain_community.document_loaders import (
DirectoryLoader, # 目录批量加载
PDFLoader, # PDF文档
CSVLoader, # CSV数据
WebBaseLoader, # 网页内容
GitHubIssuesLoader # GitHub问题
)

# 支持100+种数据源
loader = DirectoryLoader(
"documents/",
glob="**/*.txt",
loader_cls=TextLoader,
show_progress=True
)

2. ✂️ Text Splitters(文本分割器)

1
2
3
4
5
6
7
8
9
10
11
12
from langchain.text_splitter import (
RecursiveCharacterTextSplitter, # 递归分割(推荐)
TokenTextSplitter, # 按token分割
MarkdownHeaderTextSplitter, # Markdown标题分割
CodeTextSplitter # 代码文件分割
)

text_splitter = RecursiveCharacterTextSplitter(
chunk_size=1000,
chunk_overlap=200,
separators=["\n\n", "\n", "。", " "]
)

3. 🗄️ Vector Stores(向量存储)

1
2
3
4
5
6
7
8
9
10
11
12
13
from langchain_community.vectorstores import (
Chroma, # 开源,轻量级
Pinecone, # 云服务,可扩展
FAISS, # Facebook AI,高性能
Weaviate, # 功能丰富
Qdrant # 高性能,Rust实现
)

vectorstore = Chroma.from_documents(
documents=splits,
embedding=embeddings,
persist_directory="./chroma_db"
)

4. 🔗 Chains(链)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
from langchain.chains import (
RetrievalQA, # 检索问答
ConversationalRetrievalChain, # 对话检索
LLMChain, # 基础LLM链
SequentialChain # 顺序链
)

# 不同的链类型
qa_chain = RetrievalQA.from_chain_type(
llm=llm,
chain_type="stuff", # 合并所有文档
# chain_type="map_reduce", # 先处理后合并
# chain_type="refine", # 逐步精炼
retriever=retriever
)

5. 🤖 Agents(智能体)

1
2
3
4
5
6
7
8
9
10
11
12
from langchain.agents import (
AgentExecutor,
create_react_agent,
create_tool_calling_agent
)

# Agent可以使用工具和推理
agent = create_react_agent(
llm=llm,
tools=[search_tool, calculator_tool],
prompt=agent_prompt
)