Embedding 与向量检索
Embedding 是 RAG 的基石,理解它才能理解为什么 RAG 能让 LLM”知道”私有知识。
什么是 Embedding?
Embedding 是将文本、图像、声音等数据转换为高维浮点数向量的过程。
1
| "苹果" → [0.23, -0.45, 0.67, 0.89, -0.12, ...] # 1536维向量
|
目标:语义相近的内容在向量空间中距离近。
1 2 3 4 5 6 7 8 9 10 11 12
| 向量空间示意(简化到2维): Y ↑ "猫" | \ | \ | "苹果手机"-----+------"水果" | "香蕉" | "橙子" | +----------------→ X
|
余弦相似度
衡量两个向量”方向”的相似度,取值范围 [-1, 1]:
| 相似度 | 含义 |
|---|
| 1.0 | 完全相同 |
| > 0.8 | 语义相近(通常认为相关) |
| 0.5-0.8 | 有一定关联 |
| < 0.5 | 几乎无关 |
| < 0 | 语义相反 |
1 2 3 4
| import numpy as np
def cosine_similarity(a, b): return np.dot(a, b) / (np.linalg.norm(a) * np.linalg.norm(b))
|
Embedding 模型
主流模型(2025 年)
| 模型 | 维度 | 说明 |
|---|
| text-embedding-3-small | 1536 | OpenAI 性价比方案 |
| text-embedding-3-large | 3072 | OpenAI 最强性能 |
| text-embedding-ada-002 | 1536 | 老版,稳定 |
| BGE-M3 | 1024 | 开源,支持多语言 |
| M3E | 1536 | 国产开源,支持中文 |
| nomic-embed-text | 768 | Ollama 内置,开源 |
中文 Embedding 推荐
1 2 3 4 5 6 7 8 9 10
| from langchain_community.embeddings import HuggingFaceBgeEmbeddings
embedding = HuggingFaceBgeEmbeddings( model_name="BAAI/bge-m3", model_kwargs={"device": "cpu"}, encode_kwargs={"normalize_embeddings": True} )
vector = embedding.embed_query("什么是大模型")
|
API 调用方式
1 2 3 4 5 6 7 8 9
| from openai import OpenAI client = OpenAI()
response = client.embeddings.create( model="text-embedding-3-small", input="要向量化的文本" ) vector = response.data[0].embedding
|
向量数据库
选型对比
| 数据库 | 优点 | 缺点 | 适用场景 |
|---|
| Qdrant | 高性能,支持过滤 | 需要单独部署 | 生产级 RAG |
| Milvus | 功能全,可扩展 | 部署复杂 | 海量向量 |
| pgvector | 基于 Postgres,运维简单 | 性能一般 | 中小规模 |
| Chroma | 轻量,开发友好 | 不适合生产 | 原型/测试 |
| Weaviate | 混合搜索强 | 资源占用高 | 混合检索 |
| FAISS | 内存级,最快 | 无持久化 | 实验/离线 |
向量检索核心操作
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| from qwraps_qdrant import Qdrant
qdrant.upsert( collection_name="docs", points=[ {"id": 1, "vector": embedding, "payload": {"text": "Spring AI是什么"}}, {"id": 2, "vector": embedding2, "payload": {"text": "LangChain怎么用"}}, ] )
results = qdrant.search( collection_name="docs", query_vector=user_query_embedding, limit=5 )
|
ANN 算法(近似最近邻)
暴力计算所有向量距离成本太高,用 ANN 在可接受精度损失下快速检索:
| 算法 | 原理 | 速度 | 精度 |
|---|
| HNSW | 图遍历 | 最快 | 高 |
| IVF | 聚类 + 搜索 | 中 | 中 |
| PQ | 向量压缩 | 快 | 取决于压缩率 |
生产环境推荐 HNSW,Qdrant / Milvus / Weaviate 均支持。
Embedding 在 RAG 中的全流程
1 2 3 4 5 6 7 8 9 10 11 12
| 文档处理: 原始文档 → 分块(Chunking)→ 每个块 → Embedding模型 → 向量 → 存入向量数据库 ↕ 元数据(来源/标题/时间)
用户查询: 用户问题 → Embedding模型 → 查询向量 ↓ 向量数据库:Top-K 相似度搜索 → 召回 K 个相关块 ↓ 上下文拼接:用户问题 + 召回块 → 构造 Prompt ↓ LLM 生成回答
|
分块策略(Chunking)
| 策略 | 方法 | 适用场景 |
|---|
| 固定大小 | 每 N 个 Token 一块 | 通用,快速 |
| 段落切分 | 按段落(换行)切 | 语义完整 |
| 语义切分 | LLM 检测断点 | 质量最高,代价大 |
| 标题切分 | 按 Markdown 标题树 | 结构化文档 |
| 递归字符 | 按换行→句号→词递归切 | 平衡质量与成本 |
1 2 3 4 5 6 7 8 9
| from langchain.text_splitter import RecursiveCharacterTextSplitter
splitter = RecursiveCharacterTextSplitter( chunk_size=500, chunk_overlap=50, separators=["\n\n", "\n", "。", " "] ) chunks = splitter.split_text(long_document)
|
混合检索(Hybrid Search)
单一向量检索有局限,结合关键词搜索(BM25)效果更好:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| query_vector = embedding.embed_query(user_question) query_keywords = extract_keywords(user_question)
vector_results = vector_db.search(query_vector, limit=100)
keyword_results = bm25_index.search(query_keywords, limit=100)
combined = rrf_merge(vector_results, keyword_results, k=60)
final_context = combined[:5]
|