Elasticsearch 混合检索一句 `retriever.rrf`,把语义召回与关键词召回融合到极致
1. 为什么要混合检索
-
全文检索(Lexical)
BM25 基于词频 & 逆文档频率,适合 代码、SKU、专用名词 等需要精确匹配的场景。 -
语义检索(Semantic)
稀疏/稠密向量捕捉上下文与同义词,能理解 自然语言问题,召回“不含关键词但含同义含义”的文档。
痛点:只用其中一种都容易漏召回或误召回;混合检索(Hybrid)把两种结果 加权融合,既保留精确命中,又能补充语义相关。
2. 先决条件与模型
组件 | 版本建议 | 作用 |
---|---|---|
Elasticsearch | ≥ 8.11 | 内置 semantic_text 字段 & RRF |
ELSER v2 | Inference API | 默认稀疏向量模型,零样本即用 |
Kibana | 同版本 | Data Visualizer 上传 TSV |
Serverless / Cloud 已默认带 Inference Endpoint;自托管需先
POST _ml/inference/<model_id>
。
3. 创建“双通道”Mapping
PUT /semantic-embeddings
{"mappings": {"properties": {"semantic_text": { # 语义向量通道"type": "semantic_text"},"content": { # 原文关键词通道"type": "text","copy_to": "semantic_text" # 自动送进向量流水线}}}
}
semantic_text
字段写入即异步调用 Inference 生成 稀疏向量copy_to
可保留原文供 BM25,也让模型见到完整上下文
4. 数据落盘:上传 MS MARCO 子集
- 下载
msmarco-passagetest2019-top1000.tsv
- Kibana ➜ Machine Learning ➜ Data Visualizer
- Override settings:第一列
id
,第二列content
- Import 为
test-data
(≈ 18 万行)
5. reindex 批量补嵌入
POST /_reindex?wait_for_completion=false
{"source": {"index": "test-data","size" : 500 # 小 batch 快速观察},"dest": { "index": "semantic-embeddings" }
}
- 拿到
task_id
,可GET _tasks/<id>
实时看进度 - 数据量大可先小批量测试,确认 OK 再全量
提示:老索引若已在生产,可零停机开一个临时别名,用 reindex + alias swap 平滑切换。
6. Query DSL:RRF 融合
GET /semantic-embeddings/_search
{"retriever": {"rrf": {"retrievers": [{ # 1⃣ 关键词 BM25"standard": {"query": {"match": {"content": "How to avoid muscle soreness while running?"}}}},{ # 2⃣ 语义稀疏向量"standard": {"query": {"semantic": {"field": "semantic_text","query": "How to avoid muscle soreness while running?"}}}}],"rank_constant": 60 # 可选,默认 60,调大则更强调高位文档}},"size": 10 # 返回融合后前 10
}
返回字段亮点
"_score": 0.0328, // RRF 加权得分
"_rank" : 1, // 融合后排名
"semantic_text.inference.embeddings": { … } // 稀疏向量局部
7. ES|QL 写法简例
FROM semantic-embeddings
| RETRIEVE RRF(STANDARD(match(content, "muscle soreness running")),STANDARD(semantic(semantic_text, "muscle soreness running")))
| LIMIT 10
ES|QL 的
RETRIEVE RRF()
语法目前仍在 8.x 试验标志下,若未开启可继续用 DSL。
8. 常见坑 & 调优
问题 | 解决方案 |
---|---|
语义结果不佳 | 确认写入是否触发 Inference;ELSER 支持 ml.inference_id 显式指定 |
rank_constant 怎么调? | 一般 10–100 之间实验: • 调大 ➜ 关键词靠前 • 调小 ➜ 语义影响更大 |
条目多但 _score 很低 | 属 RRF 正常特性,可忽略,但排序仍正确 |
reindex 太慢 | 利用 pipeline + max_docs 分段;或先临时 alias 新写旧读,后台慢迁移 |
9. 小结
混合搜索流程三步走:
- 双字段索引 ——
content
做 BM25,semantic_text
存稀疏向量 - 批量重建嵌入 —— 用 reindex 触发 Inference Pipeline
- RRF 融合查询 ——
retriever.rrf
同时喂 BM25 与语义查询
这样即可在 Elastic Stack 中零依赖外部向量库,享受 “含义 + 关键词” 一拍即合的搜索体验。试试把你公司的 FAQ、文档、产品描述喂给它,你会惊喜发现搜索结果瞬间「既准又全」!