下面给你一张“放大镜”式的路线图,把**搜(Search)**从离线采集到在线排序的关键环节逐级拆开,侧重方法论 + 工业实现要点,并给出与 LLM 微调范式(SFT/GRPO)相对应的训练思路与开源工具。
---
# 1 搜索系统全景与层次分工
```Java
┌─ 离线 / 近线 ───────────────────────────────────────────────┐
│ ①内容生产 → ②清洗去重 → ③索引构建(倒排 & 向量) │
└─────────────────────────────────────────────────────────┘
┌─ 在线检索 ────────────────────────────────────────────────┐
│ a. Query 解析 │ b. 召回(稀疏 / 向量 / 混合) │ c. 多阶段排序 │
│ ↳ 纠错、改写、意图 │ 10³–10⁴ 候选 │ 1st / 2nd / 3rd │
└─────────────────────────────────────────────────────────┘
↓
结果组装与展示
↓
用户交互日志(点击、停留…)
↺ 反向喂给训练管线
```
- **多阶段架构**保证 **低延迟**(毫秒级)与 **高相关性**(强排序模型在最后小样本重排)。
- 日志经曝光‑点击‑满意度模型加工后,进入下一轮监督训练或 RL/Bandit 优化,与 LLM 中的 SFT→RLHF 闭环异曲同工。
---
# 2 索引构建与存储
|索引类型|典型结构|适用场景|工程要点|
|---|---|---|---|
|**倒排(Sparse)**|Term → Posting List(BM25 权重)|高频短文本 Query|分片、压缩、增量 merge|
|**向量(Dense)**|Document Embedding → HNSW / IVF‑PQ|语义检索、长句问答|ANN 索引、冷热分层、GPU|
|**混合(Hybrid)**|以上二者打分融合|综合命中率最高|单查询同时走两路检索|
- Pyserini 把 Lucene 倒排与 FAISS 向量统一在同一 Python API,方便科研复现 ([GitHub](https://github.com/castorini/pyserini/blob/master/docs/conceptual-framework.md?utm_source=chatgpt.com "pyserini/docs/conceptual-framework.md at master - GitHub"))。
- [[HNSW]] 是当前主流 ANN 图索引,查询复杂度近似 log N,对大规模向量库效果/速度最佳 ([Pinecone](https://www.pinecone.io/learn/series/faiss/hnsw/?utm_source=chatgpt.com "Hierarchical Navigable Small Worlds (HNSW) - Pinecone"))。
- Vespa 等引擎内建 **Sparse + Dense** 同时检索与融合表达式,减少双系统维护成本 ([Vespa Blog](https://blog.vespa.ai/redefining-hybrid-search-possibilities-with-vespa/?utm_source=chatgpt.com "Redefining Hybrid Search Possibilities with Vespa - part one"))。
---
# 3 查询理解(Query Understanding)
1. **基础处理**:分词、拼写纠错、词形还原、停用词过滤。
2. **意图识别 & 实体抽取**
- Transformer/BERT 在 Google Search 中用于捕获介词、长依存等细粒度语义,显著改善自然语言查询命中率 ([blog.google](https://blog.google/products/search/search-language-understanding-bert/?utm_source=chatgpt.com "Understanding searches better than ever before - Google Blog"))。
- 已落地做法:Query → LLM 服务 → 返回改写 / 结构化槽位;再落入后端检索框架。
3. **高阶优化**:
- Query 重写(QR):同义词扩展、反义词过滤、查询分类(导航 / 信息 / 交易)。
- 实体链接 + 知识图谱补全:把“奥巴马妻子”映射到 (entity:Barack_Obama, relation:spouse)。
---
# 4 召回(Retrieval)
## 4.1 稀疏召回(BM25 / TF‑IDF)
- 经典公式 [[BM25]],Lucene/[[ElasticSearch]]/OpenSearch 默认实现。
- 虽然“老”,但在零样本场景中仍极具鲁棒性,BEIR 基准多任务平均得分仍能压过许多弱语义模型 ([arXiv](https://arxiv.org/abs/2104.08663?utm_source=chatgpt.com "BEIR: A Heterogenous Benchmark for Zero-shot Evaluation of Information Retrieval Models"))。
## 4.2 向量召回(Dense Retrieval)
|模型|结构|特点|
|---|---|---|
|**DPR**|双塔;CLS 向量点积|QA 里首个大规模落地密检 ([Facebook Research](https://research.facebook.com/publications/dense-passage-retrieval-for-open-domain-question-answering/?utm_source=chatgpt.com "Dense Passage Retrieval for Open-Domain Question Answering"))|
|**ColBERT / TCT‑ColBERT**|Late‑interaction;词级向量 MaxSim|高精召回+重排一体,延迟仍可 ms 级 ([GitHub](https://github.com/stanford-futuredata/ColBERT?utm_source=chatgpt.com "stanford-futuredata/ColBERT - GitHub"))|
|**Hybrid**|Reciprocal Rank Fusion/加权线性|降低长尾 query 漏检风险|
Pyserini、Facebook DPR repo 均提供预训练模型与一键评测脚本,方便快速尝试 ([GitHub](https://github.com/castorini/pyserini?utm_source=chatgpt.com "castorini/pyserini - GitHub"), [GitHub](https://github.com/facebookresearch/DPR?utm_source=chatgpt.com "facebookresearch/DPR: Dense Passage Retriever - is a set of tools ..."))。
---
# 5 多阶段排序(Ranking)
|Stage|典型模型|训练范式|
|---|---|---|
|**First‑Pass Ranker** (1k→100)|LambdaMART / LightGBM‑Rank(树)|Pairwise/List‑wise;标签来自点击或人工 qrels ([Doug Turnbull's Blog](https://softwaredoug.com/blog/2022/01/17/lambdamart-in-depth?utm_source=chatgpt.com "LambdaMART in Depth - Doug Turnbull"), [xgboost.readthedocs.io](https://xgboost.readthedocs.io/en/latest/tutorials/learning_to_rank.html?utm_source=chatgpt.com "Learning to Rank — xgboost 3.1.0-dev documentation"))|
|**Second‑Pass Re‑ranker** (100→10)|Cross‑Encoder BERT/T5;ColBERT MaxSim|SFT on MS‑MARCO 或自有标注;蒸馏到轻模型|
|**Context/RL Fine‑Tuning**|Dueling Bandit / SlateQ|类比 GRPO,用线上用户满意度当奖励|
ElasticSearch LTR 插件把 LightGBM/RankLib 模型封装为二阶段重排,只需配置 `rescore` 即可上线实验 ([elasticsearch-learning-to-rank.readthedocs.io](https://elasticsearch-learning-to-rank.readthedocs.io/?utm_source=chatgpt.com "Elasticsearch Learning to Rank: the documentation — Elasticsearch ..."))。
---
# 6 在线反馈与长期优化
- **点击模型**(e.g., DBN、PBM)估计偏好,校正位置偏差;输出伪标签供下一轮监督学习。
- **队列插入实验**:Team‑Draft 或 Balanced Interleaving,比 A/B 更快收敛。
- **Bandit / RL**:Slate‑Level 动作空间;用 IPS/DR 校正离线偏差,再上线上试探。
- **实时特征 / 模型热更新**:Embedding 表异步推送;特征服务与检索节点解耦,<10 分钟生效。
---
# 7 评估指标与公共数据
|离线指标|说明|
|---|---|
|**nDCG@k**|排名位置折扣,标准 IR 指标 ([Wikipedia](https://en.wikipedia.org/wiki/Discounted_cumulative_gain?utm_source=chatgpt.com "Discounted cumulative gain"))|
|MRR / MAP|对唯一答案查询尤重要|
|Recall@k|混合检索看覆盖率|
- **BEIR** 含 18 个跨域数据集,官方脚本自动跑稀疏、向量、混合三类模型,是检索界的 “GLUE” ([GitHub](https://github.com/beir-cellar/beir?utm_source=chatgpt.com "beir-cellar/beir - GitHub"))。
在线侧常看 CTR、Query Success(无翻页直接点击即可消费)、Dwell Time、Refine 率等。
---
# 8 开源框架与工具对照表
|目标|框架|备注|
|---|---|---|
|端到端学术验证|**Pyserini / Anserini**|Lucene + FAISS;可一键下载 MS‑MARCO/BEsT 数据集 ([GitHub](https://github.com/castorini/pyserini?utm_source=chatgpt.com "castorini/pyserini - GitHub"))|
|企业级混合检索|**Vespa**|统一语法写 BM25 + HNSW;向量实时更新 ([docs.vespa.ai](https://docs.vespa.ai/en/tutorials/hybrid-search.html?utm_source=chatgpt.com "Hybrid Text Search Tutorial - Vespa Documentation"))|
|Elastic 生态重排|**Elasticsearch‑LTR / OpenSearch‑LTR**|支持 XGBoost/RankLib 模型热载 ([Elastic](https://www.elastic.co/docs/solutions/search/ranking/learning-to-rank-ltr?utm_source=chatgpt.com "Learning To Rank (LTR) \| Elastic Docs"), [AWS Documentation](https://docs.aws.amazon.com/opensearch-service/latest/developerguide/learning-to-rank.html?utm_source=chatgpt.com "Learning to Rank for Amazon OpenSearch Service"))|
|Dense Index 工具|**ColBERT**, **tct_colbert**|压缩索引 + MaxSim 重排 ([GitHub](https://github.com/stanford-futuredata/ColBERT?utm_source=chatgpt.com "stanford-futuredata/ColBERT - GitHub"), [GitHub](https://github.com/castorini/tct_colbert?utm_source=chatgpt.com "castorini/tct_colbert - GitHub"))|
|ANN 数据库|FAISS / Milvus / Weaviate|多种索引(IVF‑PQ、HNSW、DiskANN)|
---
# 9 与 LLM 微调范式的映射
|LLM 管线|搜索对应|训练数据|
|---|---|---|
|**预训练**(Decoder/Encoder)|语料 + Masked LM/BERT 预训练|公网爬虫、维基、书籍|
|**SFT**|监督 Learning‑to‑Rank(BM25 特征 + 点击伪标签)|日志 Triplets(q, d⁺, d⁻)|
|**奖励模型**|用户满意度 / 长期 GMV 预测网络|多目标混合标签|
|**GRPO / RLHF**|Slate Bandit 策略 / Dueling Bandit|线上实时反馈|
掌握这一映射后,迁移你的“Prompt→SFT→RLHF”思维到“Query→监督排序→Bandit”即可无缝切入搜索端。
---
# 10 推荐学习路径
1. **实验入门**:Pyserini 跑 BM25 → DPR → Hybrid,在 BEIR 验证提升曲线。
2. **树模型排序**:用 Elasticsearch‑LTR 重排倒排候选,体会特征工程对 nDCG 的影响。
3. **向量召回部署**:Milvus / Vespa HNSW,关注召回率‑延迟‑成本三角平衡。
4. **RL 排序**:Offline IPS + SlateQ 模拟 → 小流量 Online A/B。
5. **LLM + 搜索融合**:让 LLM 做 Query Rewriting、答案合成(RAG),把向量检索结果拼接入 Prompt。
---
# 小结
- **搜**并不只是“一招 BM25”,而是一条链路:**Query 理解 → 多路召回 → 多阶段排序 → 在线闭环**。
- 每一环节都已有成熟“显学”模型与框架,可像在 LLM 中玩 SFT/GRPO 那样模块化组装与迭代。
- 掌握倒排‑向量‑混合检索、LambdaMART 排序、LTR 插件及 HNSW 索引之后,你就具备打造工业级搜索的核心能力。祝你早日把“搜”玩成自己的新炼丹炉!
___
召回是搜索推荐系统中的第一个关键步骤,简单来说就是从海量内容中快速筛选出用户可能感兴趣的内容。
想象你在一个巨大的商场里购物,召回就像是快速浏览整个商场,先把可能感兴趣的商品挑出来放进购物篮,而不是仔细检查每件商品[1]。
召回的主要特点:
1. 目的: 从百万甚至千万级的内容库中,快速筛选出几千到一万个候选内容[2]。
2. 特性:
- 速度要快
- 不求特别精准,但不能漏掉用户可能喜欢的内容
- 通常采用简化的特征和模型来保证效率[2]
1. 常见召回方式:
- 基于用户兴趣标签匹配
- 基于热门内容
- 基于协同过滤("喜欢相似商品的用户可能有相似的兴趣")[1]
召回之后,内容会继续经过粗排、精排等环节,进一步筛选和排序,最终呈现给用户最合适的内容[3]。这就像是先用购物篮装下可能要买的东西,然后再仔细挑选最终要购买的商品。
这个环节非常重要,因为它决定了整个推荐系统的上限 - 如果在召回阶段就漏掉了用户真正感兴趣的内容,后续再怎么优化排序也无法弥补这个损失[2]。
Sources
[1]搜索推荐系统中的召回_搜索召回什么意思 - CSDN博客 [https://blog.csdn.net/m0_37583655/article/details/124926600](https://blog.csdn.net/m0_37583655/article/details/124926600)
[2]推荐系统:精排多目标融合与超参数学习方法 - 博客园 [https://www.cnblogs.com/orion-orion/p/18199461](https://www.cnblogs.com/orion-orion/p/18199461)
[3]推荐系统技术演进趋势:召回->排序->重排- 对白的算法屋 - 博客园 [https://www.cnblogs.com/coder-duibai/p/16078962.html](https://www.cnblogs.com/coder-duibai/p/16078962.html)
[4]算法工程师说的召回是什么意思?- 拒海空间 [https://refusea.com/?p=1546](https://refusea.com/?p=1546)
[5]深入理解:推荐系统中的召回与排序(一)| 人人都是产品经理 [https://www.woshipm.com/data-analysis/4542994.html](https://www.woshipm.com/data-analysis/4542994.html)
[6]通俗讲解【布尔召回和向量化召回】原创 - CSDN博客 [https://blog.csdn.net/u012260865/article/details/137115641](https://blog.csdn.net/u012260865/article/details/137115641)
粗排和精排是推荐系统中两个重要的排序环节,它们各有特点和作用:
# 粗排
定位和目的:
- 位于召回和精排之间的过渡环节
- 从几千个候选项中筛选出几百个物品进入精排[1]
- 需要在10-20ms内完成打分,时间要求严格[1]
特点:
- 使用简单的模型和较少的特征[5]
- 算力和延迟约束更严格
- 解空间问题更严重(打分候选集与展现集合差距大)[1]
实现方式:
- 基于规则:对召回结果进行汇总去重,按分数倒排[4]
- 基于模型:使用简单的机器学习模型进行快速打分[4]
# 精排
定位和作用:
- 推荐系统的核心环节[5]
- 对粗排筛选后的候选集进行精确打分和排序[5]
- 从几百个候选中选出最终要展示的几个结果[2]
特点:
- 使用复杂的模型和丰富的特征[5]
- 可以将模型和特征做到极致,追求高精度[2]
- 计算量相对较小,可以采用更复杂的算法[1]
技术实现:
- 可以使用深度神经网络等复杂模型[5]
- 综合考虑更多维度的特征,如用户特征、物品特征、上下文特征等[1]
- 可以进行更精细的个性化推荐[3]
Sources
[1]推荐系统[三]:粗排算法常用模型汇总(集合选择和精准预估) [https://developer.aliyun.com/article/1168781](https://developer.aliyun.com/article/1168781)
[2]推荐系统中为什么要有召回、粗排、精排- xd_xumaomao - 博客园 [https://www.cnblogs.com/xumaomao/p/15237810.html](https://www.cnblogs.com/xumaomao/p/15237810.html)
[3]关于召回、粗排、精排和重排简单介绍- JackYang - 博客园 [https://www.cnblogs.com/BlogNetSpace/p/18203628](https://www.cnblogs.com/BlogNetSpace/p/18203628)
[4]推荐策略产品经理必知必会:粗排、精排、重排模型 - 腾讯新闻 [https://news.qq.com/rain/a/20240520A0512100](https://news.qq.com/rain/a/20240520A0512100)
[5]【推荐】召回+粗排+精排 - 稀土掘金 [https://juejin.cn/post/7405413046901784628](https://juejin.cn/post/7405413046901784628)
[6]推荐系统的主要四个阶段(召回、粗排、精排、重排)- CSDN博客 [https://blog.csdn.net/qq_41750911/article/details/124573064](https://blog.csdn.net/qq_41750911/article/details/124573064)
[7]推荐策略产品经理必知必会③:粗排、精排、重排模型 [https://www.woshipm.com/share/6055285.html](https://www.woshipm.com/share/6055285.html)
# 执行链路
1. **HTTP 请求入口 - `api_server.py:29-67`**
```python
@app.route("/search", methods=["GET"])
def search_routes():
query = request.args.get("q", "") # 获取查询参数 "兰州路线"
size = int(request.args.get("size", 10))
results = search_engine.search(query, size) # 调用搜索引擎
```
2. **搜索引擎主函数 - `route_search.py:171-183`**
```python
def search(self, query: str, size: int = 10) -> List[Dict]:
search_body = self.build_search_query(query) # 构建查询
response = self.es.search(index=self.index_name, body=search_body, size=size)
```
3. **构建搜索查询 - `route_search.py:102-169`**
```python
def build_search_query(self, query: str) -> Dict:
# 3.1 提取目的地信息
destination_name, destination_code = self.extract_destination_from_query(query)
# 返回: ("兰州", "620100")
```
4. **目的地提取 - `route_search.py:95-100`**
```python
def extract_destination_from_query(self, query: str) -> tuple:
for city_name, city_code in self.config.city_codes.items():
if city_name in query: # "兰州" in "兰州路线"
return city_name, city_code
return None, None
```
5. **构建多字段查询 - `route_search.py:106-128`**
```python
# 对每个字段构建查询
for field, weight in self.config.field_weights.items():
# 字段权重配置:
# 'destinations.abstract': 3.0
# 'destinations.city.name': 2.5
# 'route_name': 2.0
# ...
```
6. **添加专注度加分 - `route_search.py:146-167`**
```python
if destination_code: # 识别到"兰州",添加专注度加分
# 6.1 专注度字段加分
search_body["query"]["function_score"]["functions"].append({
"field_value_factor": {
"field": f"focus_scores.{destination_code}", # 兰州的专注度分数
"factor": 2.0,
"modifier": "sqrt"
}
})
# 6.2 短途路线加分
search_body["query"]["function_score"]["functions"].append({
"exp": {
"metrics.duration_days": {
"origin": 2, # 2天是理想值
"scale": 3, # 衰减范围
"decay": 0.5
}
}
})
```
7. **专注度预计算 - `route_search.py:48-71`**
```python
def calculate_focus_score(self, route: Dict, target_city_code: str) -> float:
# 计算POI占比
lanzhou_pois = sum(1 for poi in route['destinations']['poi']
if poi.get('city_code') == '620100')
poi_ratio = lanzhou_pois / total_pois
# 计算天数占比
lanzhou_days = sum(1 for day in route['itinerary']
if day.get('overnight_city_code') == '620100')
day_ratio = lanzhou_days / total_days
return poi_ratio * 0.6 + day_ratio * 0.4
```
8. **发送到 Elasticsearch**
最终构建的查询体会发送到 ES,大致结构如下:
```json
{
"query": {
"function_score": {
"query": {
"bool": {
"should": [
{"match": {"destinations.abstract": {"query": "兰州路线", "boost": 3.0}}},
{"nested": {"path": "destinations.city",
"query": {"match": {"destinations.city.name": {"query": "兰州路线", "boost": 2.5}}}}},
{"match": {"route_name": {"query": "兰州路线", "boost": 2.0}}}
]
}
},
"functions": [
{
"field_value_factor": {
"field": "focus_scores.620100",
"factor": 2.0,
"modifier": "sqrt"
}
},
{
"exp": {
"metrics.duration_days": {
"origin": 2,
"scale": 3,
"decay": 0.5
}
}
}
]
}
}
}
```
## 评分计算示例
**兰州城市经典2日游:**
- **BM25 基础分**:~10分(路线名、抽象目的地、城市都匹配)
- **专注度加分**:`sqrt(1.0) * 2.0 = 2.0`
- **短途加分**:`exp(0) * 1.5 = 1.5`(2天正好是期望值)
- **最终得分**:`10 * (1 + 2.0 + 1.5) = 15.81`
**甘青大环线:**
- **BM25 基础分**:~2分(仅城市字段部分匹配)
- **专注度加分**:`sqrt(0.19) * 2.0 = 0.87`
- **短途减分**:`exp(-2.33) * 1.5 = 0.15`(10天远离期望值)
- **最终得分**:`2 * (1 + 0.87 + 0.15) = 2.44`