ElasticsearchNotes

第 14 章:向量与语义搜索

zjc 于 2026-01-14 发布

这是《Elasticsearch 零基础实战指南》的独立章节版。本章从概念、实操和生产排查三个视角展开,代码块保留了原书可直接运行的版本。 关键词搜索擅长精确匹配词项,向量搜索擅长理解语义相似。随着语义检索、推荐召回和 RAG 系统普及,dense_vector 与 kNN 已经成为 Elasticsearch 的重要能力。

本章讲解向量字段、kNN 查询、混合搜索、过滤向量查询、召回与重排,以及向量索引的容量与性能边界。

14.1 为什么需要向量搜索

用户输入:

适合出差的轻便电脑

关键词搜索依赖分词词项是否命中,而语义搜索可以把文本映射成向量:

适合出差的轻便电脑 -> [0.12, -0.34, 0.56, ...]
轻薄笔记本 -> [0.10, -0.30, 0.52, ...]

向量距离越近,语义越相似。它适合:

  1. 同义表达召回;
  2. 图片相似度;
  3. 音频相似度;
  4. 推荐召回;
  5. 知识库问答;
  6. 语义去重。

向量搜索不能替代所有关键词搜索,例如订单号、错误码、品牌型号仍应使用关键词精确匹配。

14.2 dense_vector 字段

Mapping:

PUT /semantic-docs
{
  "mappings": {
    "properties": {
      "title": { "type": "text" },
      "content": { "type": "text" },
      "content_vector": {
        "type": "dense_vector",
        "dims": 768,
        "index": true,
        "similarity": "cosine"
      }
    }
  }
}

参数说明:

参数 说明
dims 向量维度
index 是否构建向量索引
similarity 相似度度量

常见维度:

模型类型 维度示例
小型语义模型 256 / 384
中型通用模型 768 / 1024
大型模型 1536 / 3072

维度越高表达能力通常越强,但内存和计算成本也越高。

14.3 相似度度量

similarity 含义 适用
cosine 夹角相似度 文本语义、归一化向量
dot_product 内积 已归一化向量,通常性能好
l2_norm 欧氏距离 空间坐标、某些图像特征

文本嵌入常使用 cosine。如果模型输出已归一化,使用 dot_product 通常更快,但必须确认向量确实归一化。

14.4 写入向量文档

PUT /semantic-docs/_doc/1
{
  "title": "轻薄笔记本",
  "content": "适合出差使用,重量轻,续航长。",
  "content_vector": [0.12, -0.34, 0.56]
}

实际 768 维向量会很长,不建议在业务数据库中直接保存完整 JSON 明文。同步服务应在内存中调用 Embedding 模型,然后写入 ES。

14.5 kNN 搜索

GET /semantic-docs/_search
{
  "knn": {
    "field": "content_vector",
    "query_vector": [0.11, -0.32, 0.55],
    "k": 10,
    "num_candidates": 100
  }
}

参数说明:

参数 说明
field 向量字段
query_vector 查询向量
k 最终返回近邻数
num_candidates 每个分片候选数量
filter 过滤条件

num_candidates 越大,召回越准确,但查询越慢。常见从 k * 5k * 10 开始压测。

14.6 带过滤条件的 kNN

GET /semantic-docs/_search
{
  "knn": {
    "field": "content_vector",
    "query_vector": [0.11, -0.32, 0.55],
    "k": 10,
    "num_candidates": 200,
    "filter": {
      "bool": {
        "filter": [
          { "term": { "status": "PUBLISHED" } },
          { "range": { "publish_at": { "lte": "now" } } }
        ]
      }
    }
  }
}

过滤条件在候选检索时生效,不是先取 Top K 再过滤。这样可以避免某些过滤条件下结果不足。

14.7 混合搜索

混合搜索同时使用关键词和向量:

GET /products/_search
{
  "query": {
    "multi_match": {
      "query": "轻薄笔记本",
      "fields": ["title^3", "brand", "description"],
      "type": "best_fields"
    }
  },
  "knn": {
    "field": "title_vector",
    "query_vector": [0.11, -0.32, 0.55],
    "k": 50,
    "num_candidates": 300
  },
  "rank": {
    "rrf": {
      "rank_window_size": 100
  }
  },
  "size": 20
}

RRF

Reciprocal Rank Fusion 按名次融合,不直接比较原始分数:

score = sum(1 / (rank_constant + rank_i))

优点:

  1. 不需要归一化 BM25 和向量分数;
  2. 对不同分值分布稳健;
  3. 实现简单;
  4. 常作为默认混合策略。

线性加权

final_score = text_weight * text_score + vector_weight * vector_score

优点是可解释、可控;缺点是 BM25 与向量分数分布不同,需要调参。

14.8 Rerank

粗召回后,可以使用更重模型重排:

关键词召回 100 条
+ 向量召回 100 条
-> 去重合并
-> Rerank 模型打分
-> 返回 Top 20

常见 Rerank 输入:

query: 适合出差的轻便电脑
document: 轻薄笔记本,重量 1.2kg,续航 18 小时

模型输出相关分数。应用层根据分数重新排序。

适合:

  1. 搜索质量要求高;
  2. 召回集较小;
  3. 允许额外延迟;
  4. 有标注或点击数据;
  5. 可以承担模型成本。

14.9 多路召回架构

flowchart LR
    Query[用户查询] --> Analyzer[查询理解]
    Analyzer --> Keyword[关键词召回]
    Analyzer --> Vector[向量召回]
    Analyzer --> Rule[规则/运营召回]
    Keyword --> Merge[合并去重]
    Vector --> Merge
    Rule --> Merge
    Merge --> Rerank[重排]
    Rerank --> Result[搜索结果]

查询理解可以包括:

  1. 分词;
  2. 改写;
  3. 同义词扩展;
  4. 类目预测;
  5. 语言识别;
  6. 意图识别;
  7. 向量化。

14.10 向量生成链路

原始文本 -> 清洗 -> 截断/分块 -> Embedding 模型 -> 向量 -> Elasticsearch

注意事项:

  1. 写入和查询必须使用同一模型或兼容模型;
  2. 模型升级需要重建向量字段;
  3. 长文本应分块,每块独立向量;
  4. 保留 chunk 元数据,例如文档 ID、位置、标题;
  5. 控制 Embedding 调用成本;
  6. 向量生成失败要有重试和死信;
  7. 批量生成比单条调用更高效。

文档分块示例:

{
  "doc_id": "kb-1001",
  "chunk_id": "kb-1001-0003",
  "chunk_index": 3,
  "title": "退款政策",
  "content": "未发货订单可在 30 分钟内取消。",
  "content_vector": [0.1, 0.2, 0.3],
  "source": "help-center"
}

14.11 RAG 检索

RAG 常用流程:

用户问题 -> 查询改写 -> 混合检索 -> 重排 -> 组装上下文 -> LLM 生成答案

检索建议:

  1. 同时使用关键词和语义召回;
  2. 限制上下文长度;
  3. 返回来源文档和位置;
  4. 权限过滤必须在检索层完成;
  5. 对无结果和低置信结果兜底;
  6. 记录用户反馈;
  7. 不要把私有数据直接暴露给无权限用户。

14.12 性能与容量

向量检索的主要成本:

  1. 内存:HNSW 图和向量数据;
  2. CPU:距离计算;
  3. 磁盘:向量字段存储;
  4. 网络:多分片候选归并;
  5. 构建成本:向量索引写入更慢。

优化方式:

优化 说明
降低维度 选择合适模型,不盲目追求高维
使用量化 依据版本支持选择压缩能力
减少 num_candidates 降低单次候选数
缩小 filter 范围 时间、租户、类目过滤
分索引 按业务或时间拆分
冷热分层 冷向量少查或不留内存
预过滤 避免无权限候选
监控 P99 向量查询延迟单独观测

14.13 关键词与向量选型

查询 建议
订单号、错误码 keyword
品牌型号 keyword + text
中文自然语言 text + IK
同义表达 dense_vector
图片相似 image embedding + kNN
推荐召回 user/item 向量
知识问答 混合搜索 + Rerank
强权限文档 必须加 filter

14.14 版本边界

不同版本对向量检索能力差异较大:

  1. 早期版本支持 script score,但性能有限;
  2. 8.x 引入近似 kNN 与 HNSW 索引;
  3. 后续版本增强过滤、量化、多向量等能力;
  4. 9.x 在性能和语义搜索生态上继续演进;
  5. 兼容发行版能力可能不同。

使用前必须确认当前版本的 Mapping 语法、最大维度、相似度类型和 kNN 参数。

14.15 本章小结

14.16 思考题

  1. 什么场景必须使用关键词搜索而不是向量搜索?
  2. num_candidates 调大会带来什么影响?
  3. 为什么 RRF 比直接相加 BM25 分和向量分更稳?
  4. 模型升级后为什么需要重建向量字段?
  5. RAG 系统中权限过滤为什么必须发生在 ES 查询中?