下面给你一张“放大镜”式的路线图,把**搜(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`