Elasticsearch 向量检索
什么是向量检索?
向量检索(Vector Search) 是将文本、图片、音频等非结构化数据转换为高维向量(Embedding),通过计算向量间的相似度来找到语义相近的内容,而非传统的关键词匹配。
传统搜索: "苹果手机" → 精确匹配 "苹果" "手机" 关键词
向量搜索: "苹果手机" → 语义理解 → 能匹配 "iPhone" "iOS设备" 等
ES 向量检索发展历程
核心概念
1. 🔢 Dense Vector(稠密向量)
PUT /articles
{
"mappings": {
"properties": {
"title": { "type": "text" },
"content": { "type": "text" },
"embedding": {
"type": "dense_vector",
"dims": 1536, // 向量维度(取决于模型)
"index": true, // 开启索引,支持 kNN
"similarity": "cosine" // 相似度算法
}
}
}
}
2. 📐 相似度算法
3. 🏗️ HNSW 索引结构
ES 8.8+ 默认使用 HNSW(Hierarchical Navigable Small World)
优点:
✅ 近似最近邻(ANN),不是精确搜索
✅ 查询速度极快(毫秒级)
✅ 精度可调(accuracy vs speed 权衡)
参数:
m: 每个节点的连接数(默认16,越大越精准但内存越多)
ef_construction: 构建时搜索宽度(默认100)
核心 API 使用
📥 写入向量数据
from elasticsearch import Elasticsearch
from sentence_transformers import SentenceTransformer
es = Elasticsearch("http://localhost:9200")
model = SentenceTransformer("BAAI/bge-large-zh-v1.5")
doc = {
"title": "FreeSWITCH 语音平台",
"content": "FreeSWITCH 是一个开源的电话软交换平台...",
"embedding": model.encode("FreeSWITCH 是一个开源的电话软交换平台").tolist()
}
es.index(index="articles", body=doc)
🔍 kNN 向量搜索
POST /articles/_search
{
"knn": {
"field": "embedding",
"query_vector": [0.12, 0.45, ...], // 查询文本的向量
"k": 10, // 返回最相似的 10 条
"num_candidates": 100 // 候选集大小,越大越精准
},
"_source": ["title", "content"]
}
# Python 实现
query_text = "语音识别方案有哪些?"
query_vector = model.encode(query_text).tolist()
result = es.search(
index="articles",
knn={
"field": "embedding",
"query_vector": query_vector,
"k": 10,
"num_candidates": 100
}
)
🔀 混合检索(Hybrid Search)⭐ 推荐
向量检索 + 关键词检索 结合,效果最好
POST /articles/_search
{
"query": {
"match": {
"content": "语音识别" // BM25 关键词检索
}
},
"knn": {
"field": "embedding",
"query_vector": [0.12, 0.45, ...],
"k": 10,
"num_candidates": 100
},
"rank": {
"rrf": { // RRF 互惠排名融合
"window_size": 50,
"rank_constant": 20
}
}
}
混合检索原理(RRF 融合):
BM25 结果: doc1(0.95) > doc3(0.82) > doc5(0.71) ...
kNN 结果: doc3(0.98) > doc1(0.91) > doc7(0.85) ...
↓ RRF 融合
最终排名: doc1 🥇 doc3 🥈 doc5 🥉 ...
向量模型选型
中文推荐模型
# 使用 BGE 模型(推荐中文场景)
from sentence_transformers import SentenceTransformer
model = SentenceTransformer("BAAI/bge-large-zh-v1.5")
# BGE 模型查询时需加前缀
query = "为查询而写: 语音识别方案"
embedding = model.encode(query, normalize_embeddings=True)
性能优化技巧
1. 向量量化(节省内存)
"embedding": {
"type": "dense_vector",
"dims": 1536,
"index": true,
"similarity": "cosine",
"index_options": {
"type": "int8_hnsw" // int8量化,内存减少75%,精度损失<1%
// "type": "int4_hnsw" // int4量化,内存减少87.5%
// "type": "bbq_hnsw" // 二进制量化,内存减少96%
}
}
2. 调整 HNSW 参数
"index_options": {
"type": "hnsw",
"m": 16, // 增大提升精度(内存+)
"ef_construction": 100 // 增大提升精度(构建慢)
}
// 查询时
"knn": {
"num_candidates": 200 // 增大提升召回率(速度-)
}
3. 分片策略
向量数据量级建议:
< 100万条 → 1~2 个分片
100万~1000万 → 3~5 个分片
> 1000万 → 考虑分索引或向量数据库
完整 RAG 架构示例
结合大语言模型构建知识库问答
用户提问
↓
Embedding 模型(BGE/OpenAI)
↓ 问题向量化
ES 混合检索(kNN + BM25)
↓ 召回 Top-K 相关文档
Rerank 重排序(可选,BGE-Reranker)
↓ 精选 Top-3 上下文
LLM 大模型(GPT/通义/文心)
↓ 结合上下文生成回答
最终答案 ✅
def rag_query(question: str) -> str:
# 1. 向量化问题
query_vector = embed_model.encode(question).tolist()
# 2. ES 混合检索
results = es.search(
index="knowledge_base",
knn={"field": "embedding", "query_vector": query_vector, "k": 5, "num_candidates": 50},
query={"match": {"content": question}},
rank={"rrf": {}}
)
# 3. 拼接上下文
context = "\n".join([hit["_source"]["content"]
for hit in results["hits"]["hits"][:3]])
# 4. 调用 LLM
prompt = f"根据以下内容回答问题:\n{context}\n\n问题:{question}"
return llm.chat(prompt)
ES vs 专业向量数据库
选型建议
总结:ES 的向量检索最大优势是混合检索(BM25 + kNN + RRF),既有传统关键词的精确性,又有语义检索的召回能力,对于大多数 RAG 和语义搜索场景来说是性价比最高的方案,尤其适合团队已有 ES 运维经验的情况。