<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Retrieval on Khari Z's LogBook</title><link>https://n571e.github.io/tags/retrieval/</link><description>Recent content in Retrieval on Khari Z's LogBook</description><generator>Hugo -- gohugo.io</generator><language>zh-cn</language><lastBuildDate>Fri, 24 Jul 2026 00:00:00 +0000</lastBuildDate><atom:link href="https://n571e.github.io/tags/retrieval/index.xml" rel="self" type="application/rss+xml"/><item><title>从 Prompt 到可审计系统：结构化 LLM 决策的工程笔记</title><link>https://n571e.github.io/p/structured-llm-decision-system-review/</link><pubDate>Fri, 24 Jul 2026 00:00:00 +0000</pubDate><guid>https://n571e.github.io/p/structured-llm-decision-system-review/</guid><description>&lt;p&gt;最近一个月，我一直在整理一类结构化 LLM 决策系统。它接收自然语言和结构化字段，在有限标签中做选择；证据不足时，系统要能转人工或者明确放弃自动判断。&lt;/p&gt;&#10;&lt;p&gt;这篇只保留能公开讨论的方法，不涉及具体行业、业务字段、数据规模和实测数字。真正让我花时间的也不是某一句 Prompt，而是几个更基础的问题：候选从哪里来，怎样限制模型输出，第二次调用为什么可能和第一次不同，服务变快以后质量会不会发生变化。&lt;/p&gt;&#10;&lt;h2 id="一先把生成答案改成受限决策"&gt;&lt;a href="#%e4%b8%80%e5%85%88%e6%8a%8a%e7%94%9f%e6%88%90%e7%ad%94%e6%a1%88%e6%94%b9%e6%88%90%e5%8f%97%e9%99%90%e5%86%b3%e7%ad%96" class="header-anchor"&gt;&lt;/a&gt;一、先把“生成答案”改成“受限决策”&#10;&lt;/h2&gt;&lt;p&gt;开放式生成很灵活，直接放进业务流程却容易出问题：模型可能编出不存在的标签，也可能忽略方向、阶段之类的约束。即使返回了合法 JSON，字段值仍然可能选错。&lt;/p&gt;&#10;&lt;p&gt;我更习惯把流程拆成下面这样：&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;&#10;&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;&#10;&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1&#10;&lt;/span&gt;&lt;span class="lnt"&gt;2&#10;&lt;/span&gt;&lt;span class="lnt"&gt;3&#10;&lt;/span&gt;&lt;span class="lnt"&gt;4&#10;&lt;/span&gt;&lt;span class="lnt"&gt;5&#10;&lt;/span&gt;&lt;span class="lnt"&gt;6&#10;&lt;/span&gt;&lt;span class="lnt"&gt;7&#10;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&#10;&lt;td class="lntd"&gt;&#10;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;原始输入&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -&amp;gt; 清洗和确定性规则&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -&amp;gt; 检索候选&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -&amp;gt; LLM 提取受控事实&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -&amp;gt; 在候选 ID 中选择&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -&amp;gt; 程序检查 + 必要时语义复核&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -&amp;gt; 自动通过 / 人工复核 / 放弃自动判断&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&#10;&lt;/div&gt;&#10;&lt;/div&gt;&lt;p&gt;这类结构可以用在文档分类、工单路由、内容审核或其他高风险分类任务中。模型只负责一个边界清楚的局部判断：&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;&#10;&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;&#10;&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1&#10;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&#10;&lt;td class="lntd"&gt;&#10;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;decision = model(facts, candidates, policy)&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&#10;&lt;/div&gt;&#10;&lt;/div&gt;&lt;p&gt;&lt;code&gt;facts&lt;/code&gt; 是当前样本中可验证的事实，&lt;code&gt;candidates&lt;/code&gt; 是允许选择的集合，&lt;code&gt;policy&lt;/code&gt; 描述决策边界。三者最好分开组织。全塞进一段自然语言里，后面很难知道模型到底忽略了哪一部分。&lt;/p&gt;&#10;&lt;h3 id="json-合法只解决了第一层问题"&gt;&lt;a href="#json-%e5%90%88%e6%b3%95%e5%8f%aa%e8%a7%a3%e5%86%b3%e4%ba%86%e7%ac%ac%e4%b8%80%e5%b1%82%e9%97%ae%e9%a2%98" class="header-anchor"&gt;&lt;/a&gt;JSON 合法，只解决了第一层问题&#10;&lt;/h3&gt;&lt;p&gt;结构化输出有两种常见做法。一种是在 Prompt 里要求返回 JSON，解析失败后重试；另一种是在解码阶段按照 JSON Schema 屏蔽非法 token，也就是 constrained decoding。&lt;/p&gt;&#10;&lt;p&gt;&lt;a class="link" href="https://openai.com/index/introducing-structured-outputs-in-the-api/" target="_blank" rel="noopener"&#10; &gt;OpenAI 对 Structured Outputs 的说明&lt;/a&gt;提到，约束解码可以保证输出符合 schema，却不能保证对象里的值正确。一个答案可以格式完美，同时语义完全错误。&lt;/p&gt;&#10;&lt;p&gt;所以校验至少要分成两层：&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;&#10;&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;&#10;&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1&#10;&lt;/span&gt;&lt;span class="lnt"&gt;2&#10;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&#10;&lt;td class="lntd"&gt;&#10;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;结构校验：字段是否齐全、类型是否正确、枚举是否合法&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;领域校验：候选是否存在、事实是否冲突、决策是否越过业务边界&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&#10;&lt;/div&gt;&#10;&lt;/div&gt;&lt;p&gt;模型最好返回候选里的稳定 ID，不要重新生成标签名称。解释可以是一小段文本，真正参与程序路由的状态尽量使用枚举、布尔值和受控数组。&lt;/p&gt;&#10;&lt;p&gt;还有一个常被漏掉的分支：模型拒绝回答，或者因为长度限制没有生成完整对象。结构化输出接口通常会单独暴露 refusal 和 finish reason，程序必须处理，不能把缺失字段补成一个看似正常的结果。&lt;/p&gt;&#10;&lt;h2 id="二检索的价值是把问题变小"&gt;&lt;a href="#%e4%ba%8c%e6%a3%80%e7%b4%a2%e7%9a%84%e4%bb%b7%e5%80%bc%e6%98%af%e6%8a%8a%e9%97%ae%e9%a2%98%e5%8f%98%e5%b0%8f" class="header-anchor"&gt;&lt;/a&gt;二、检索的价值，是把问题变小&#10;&lt;/h2&gt;&lt;p&gt;在分类系统里，检索的任务不只是补充知识。它还负责缩小模型的选择空间。&lt;/p&gt;&#10;&lt;table&gt;&#10;&#9;&lt;thead&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;环节&lt;/th&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;目标&lt;/th&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;常用观察指标&lt;/th&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&lt;/thead&gt;&#10;&#9;&lt;tbody&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;确定性规则&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;接住边界清楚的输入&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;覆盖率、冲突率&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;候选召回&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;让正确标签进入 top-k&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Recall@K&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;候选排序&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;把更相关的候选排在前面&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;MRR、NDCG&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;LLM 决策&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;结合事实做最终选择&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;自动决策精度、放弃率&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&lt;/tbody&gt;&#10;&lt;/table&gt;&#10;&lt;p&gt;召回决定了系统的理论上限。正确答案没有进入候选池，后面的 LLM 再强也选不到。反过来，Recall@K 很高也不代表最终结果一定更好。候选太多会增加 token，相近标签之间也会互相干扰。&lt;/p&gt;&#10;&lt;p&gt;候选数量还会影响上下文利用率。&lt;a class="link" href="https://arxiv.org/abs/2307.03172" target="_blank" rel="noopener"&#10; &gt;Lost in the Middle&lt;/a&gt; 发现，相关信息位于长上下文中部时，模型表现可能明显下降。“窗口装得下”和“模型用得好”并不是一回事。&lt;/p&gt;&#10;&lt;p&gt;因此 top-k 应该被当作需要评测的超参数：&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;&#10;&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;&#10;&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1&#10;&lt;/span&gt;&lt;span class="lnt"&gt;2&#10;&lt;/span&gt;&lt;span class="lnt"&gt;3&#10;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&#10;&lt;td class="lntd"&gt;&#10;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;k 太小：正确答案可能没有被召回&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;k 太大：上下文变长，候选彼此干扰&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;合适的 k：兼顾 Recall@K 和最终决策质量&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&#10;&lt;/div&gt;&#10;&lt;/div&gt;&lt;p&gt;BM25、Embedding 和 Reranker 适合解决不同的问题。BM25 对关键词、编号和专有名词敏感；Embedding 能处理同义表达；Reranker 在较小的候选集上做更细的 query-document 交互。常见做法是先用便宜的检索器召回，再把少量候选交给 Reranker 或 LLM。&lt;/p&gt;&#10;&lt;p&gt;候选来源也值得保留。某个候选来自规则、稀疏检索还是向量检索，应该跟着结果进入 trace。误判发生时，这能帮助判断该调整召回还是决策节点。&lt;/p&gt;&#10;&lt;h2 id="三中间状态也是接口"&gt;&lt;a href="#%e4%b8%89%e4%b8%ad%e9%97%b4%e7%8a%b6%e6%80%81%e4%b9%9f%e6%98%af%e6%8e%a5%e5%8f%a3" class="header-anchor"&gt;&lt;/a&gt;三、中间状态也是接口&#10;&lt;/h2&gt;&lt;p&gt;多节点流程里，一个事实分析节点可能先把原文压缩成摘要，后续节点再根据摘要做判断。这样很方便，也很容易积累漂移。&lt;/p&gt;&#10;&lt;p&gt;自然语言摘要是一种有损压缩。它可能省略否定和范围，也可能把推断写成事实。后续模型并不知道这句话来自原始输入还是上一个模型的猜测，只会把它当作新的上下文继续推理。&lt;/p&gt;&#10;&lt;p&gt;更稳的中间协议可以写成：&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;&#10;&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;&#10;&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1&#10;&lt;/span&gt;&lt;span class="lnt"&gt;2&#10;&lt;/span&gt;&lt;span class="lnt"&gt;3&#10;&lt;/span&gt;&lt;span class="lnt"&gt;4&#10;&lt;/span&gt;&lt;span class="lnt"&gt;5&#10;&lt;/span&gt;&lt;span class="lnt"&gt;6&#10;&lt;/span&gt;&lt;span class="lnt"&gt;7&#10;&lt;/span&gt;&lt;span class="lnt"&gt;8&#10;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&#10;&lt;td class="lntd"&gt;&#10;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-json" data-lang="json"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;{&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;direction&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;outbound&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;entity_type&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;organization&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;stage&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;execution&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;has_supporting_document&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;evidence&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;原文片段 A&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;输入字段 B&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;uncertain_fields&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&#10;&lt;/div&gt;&#10;&lt;/div&gt;&lt;p&gt;这里最重要的不是字段叫什么，而是把“输入事实”“模型推断”和“不确定项”分开。自由文本仍然可以留在 &lt;code&gt;reason&lt;/code&gt; 中供人阅读，但不要让它悄悄控制程序分支。&lt;/p&gt;&#10;&lt;p&gt;从工程角度看，LLM 节点和普通服务没有区别：输入、输出、版本和失败行为都需要契约。模型生成的文字很顺，所以接口漂移有时比普通类型错误更难发现。&lt;/p&gt;&#10;&lt;h2 id="四critic-能用但要先告诉它查什么"&gt;&lt;a href="#%e5%9b%9bcritic-%e8%83%bd%e7%94%a8%e4%bd%86%e8%a6%81%e5%85%88%e5%91%8a%e8%af%89%e5%ae%83%e6%9f%a5%e4%bb%80%e4%b9%88" class="header-anchor"&gt;&lt;/a&gt;四、Critic 能用，但要先告诉它查什么&#10;&lt;/h2&gt;&lt;p&gt;多轮反思看起来很自然：先生成答案，再让模型审查，发现问题后重新生成。研究对这种方法给出了不同结果。&lt;/p&gt;&#10;&lt;p&gt;&lt;a class="link" href="https://arxiv.org/abs/2303.17651" target="_blank" rel="noopener"&#10; &gt;Self-Refine&lt;/a&gt; 在多种任务上观察到自反馈和迭代改写带来的提升；&lt;a class="link" href="https://arxiv.org/abs/2310.01798" target="_blank" rel="noopener"&#10; &gt;Large Language Models Cannot Self-Correct Reasoning Yet&lt;/a&gt; 则发现，没有外部反馈时，模型可能找不到自己的推理错误，修改后表现甚至会下降。&lt;/p&gt;&#10;&lt;p&gt;这两个结论并不冲突。润色文本、补充遗漏，和定位一个未知的逻辑错误不是同一道题。Critic 如果只收到一句“请严格审查”，很可能重复第一次判断，也可能为了表现得更严格而修改原本正确的答案。&lt;/p&gt;&#10;&lt;p&gt;我更认可有边界的审查：&lt;/p&gt;&#10;&lt;ol&gt;&#10;&lt;li&gt;程序先检查 ID、枚举、互斥条件等硬约束。&lt;/li&gt;&#10;&lt;li&gt;规则无法判断时，再调用一次语义 Critic。&lt;/li&gt;&#10;&lt;li&gt;Critic 只检查预先定义的错误类型。&lt;/li&gt;&#10;&lt;li&gt;高风险状态变化需要独立证据或短确认。&lt;/li&gt;&#10;&lt;li&gt;达到调用上限后转人工，不允许无限反思。&lt;/li&gt;&#10;&lt;/ol&gt;&#10;&lt;p&gt;Critic 真正需要的是外部锚点。程序规则、候选约束、错误位置和独立检索证据都算锚点。什么都不给，只让模型“再想想”，效果很难预测。&lt;/p&gt;&#10;&lt;p&gt;还可以把“发现错误”和“修正错误”拆成两个节点。已有研究表明，模型可能具备修正已定位错误的能力，却不擅长自己找到错误位置。拆开后，每个节点的评测也更清楚。&lt;/p&gt;&#10;&lt;h2 id="五评测不能只留一个-accuracy"&gt;&lt;a href="#%e4%ba%94%e8%af%84%e6%b5%8b%e4%b8%8d%e8%83%bd%e5%8f%aa%e7%95%99%e4%b8%80%e4%b8%aa-accuracy" class="header-anchor"&gt;&lt;/a&gt;五、评测不能只留一个 accuracy&#10;&lt;/h2&gt;&lt;p&gt;高风险分类中，不同错误的代价差很多。自动给出错误结果和把正确样本转给人工，不能算成同一种失败。&lt;/p&gt;&#10;&lt;p&gt;一组更实用的指标包括：&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;总体准确率；&lt;/li&gt;&#10;&lt;li&gt;自动决策精度和错误自动放行数；&lt;/li&gt;&#10;&lt;li&gt;人工复核率与错误放弃数；&lt;/li&gt;&#10;&lt;li&gt;最终状态分布；&lt;/li&gt;&#10;&lt;li&gt;各节点调用数、P50/P95 延迟、输入与输出 token；&lt;/li&gt;&#10;&lt;li&gt;schema retry、服务 retry、超时和错误类型。&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p&gt;如果业务更重视避免错误自动决策，就应该给自动决策精度设置独立门槛，而不是让总体 accuracy 把它平均掉。可以把目标写成简单的成本函数：&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;&#10;&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;&#10;&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1&#10;&lt;/span&gt;&lt;span class="lnt"&gt;2&#10;&lt;/span&gt;&lt;span class="lnt"&gt;3&#10;&lt;/span&gt;&lt;span class="lnt"&gt;4&#10;&lt;/span&gt;&lt;span class="lnt"&gt;5&#10;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&#10;&lt;td class="lntd"&gt;&#10;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;total_cost&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; = false_accept * 高风险成本&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; + false_abstain * 机会成本&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; + manual_review * 人工成本&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; + latency * 服务成本&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&#10;&lt;/div&gt;&#10;&lt;/div&gt;&lt;p&gt;这些权重来自业务风险，不应由模型指标替代。&lt;/p&gt;&#10;&lt;h3 id="全链路评测和冻结回放回答不同问题"&gt;&lt;a href="#%e5%85%a8%e9%93%be%e8%b7%af%e8%af%84%e6%b5%8b%e5%92%8c%e5%86%bb%e7%bb%93%e5%9b%9e%e6%94%be%e5%9b%9e%e7%ad%94%e4%b8%8d%e5%90%8c%e9%97%ae%e9%a2%98" class="header-anchor"&gt;&lt;/a&gt;全链路评测和冻结回放回答不同问题&#10;&lt;/h3&gt;&lt;p&gt;全链路评测回答“这个版本能不能用”。冻结回放回答“变化从哪里产生”。&lt;/p&gt;&#10;&lt;p&gt;冻结回放会保存某个节点的完整输入，固定 Prompt、候选和依赖，只替换模型或推理参数。如果冻结输入仍然漂移，问题更可能在模型或运行时；如果冻结后稳定、全链路却不稳定，就应检查上游状态。&lt;/p&gt;&#10;&lt;p&gt;同一实验也需要重复运行。除了均值，还应观察最差轮次、方差和变化样本交集。一直失败的样本容易定位；偶尔正确、偶尔自信做错的样本更值得警惕。&lt;/p&gt;&#10;&lt;h2 id="六推理优化要分清-prefill-和-decode"&gt;&lt;a href="#%e5%85%ad%e6%8e%a8%e7%90%86%e4%bc%98%e5%8c%96%e8%a6%81%e5%88%86%e6%b8%85-prefill-%e5%92%8c-decode" class="header-anchor"&gt;&lt;/a&gt;六、推理优化要分清 prefill 和 decode&#10;&lt;/h2&gt;&lt;p&gt;一个 LLM 请求大致有两个阶段：&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;&#10;&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;&#10;&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1&#10;&lt;/span&gt;&lt;span class="lnt"&gt;2&#10;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&#10;&lt;td class="lntd"&gt;&#10;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;prefill：处理整段输入，建立 KV cache 或模型状态&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;decode：逐 token 生成输出，过程更串行&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&#10;&lt;/div&gt;&#10;&lt;/div&gt;&lt;p&gt;上下文、候选和示例过长，主要增加 prefill；输出理由太长、反复 repair，主要增加 decode 和调用次数。优化前先拆开这些指标，不然很容易在错误的阶段花时间。&lt;/p&gt;&#10;&lt;h3 id="prefix-cache-只节省共享前缀的-prefill"&gt;&lt;a href="#prefix-cache-%e5%8f%aa%e8%8a%82%e7%9c%81%e5%85%b1%e4%ba%ab%e5%89%8d%e7%bc%80%e7%9a%84-prefill" class="header-anchor"&gt;&lt;/a&gt;Prefix cache 只节省共享前缀的 prefill&#10;&lt;/h3&gt;&lt;p&gt;&lt;a class="link" href="https://docs.vllm.ai/en/v0.15.0/features/automatic_prefix_caching/" target="_blank" rel="noopener"&#10; &gt;vLLM 的 Automatic Prefix Caching 文档&lt;/a&gt;明确说明，APC 会复用相同前缀的 KV cache，从而跳过共享部分的 prefill，但不会减少新 token 的 decode 时间。&lt;/p&gt;&#10;&lt;p&gt;它更适合长公共前缀、多轮对话，或者对同一文档的重复查询。如果瓶颈是长输出或重复调用，prefix cache 的帮助有限。&lt;/p&gt;&#10;&lt;p&gt;Prompt 的排列也会影响缓存命中。稳定的系统指令、schema 和公共规则可以放在前面，动态事实与候选放在后面。不过这个建议仍需在具体运行时上验证。Prefix cache、推测解码、模型架构和并发调度可能互相影响，任何组合都不能只凭单项基准上线。&lt;/p&gt;&#10;&lt;h3 id="mtp-和推测解码"&gt;&lt;a href="#mtp-%e5%92%8c%e6%8e%a8%e6%b5%8b%e8%a7%a3%e7%a0%81" class="header-anchor"&gt;&lt;/a&gt;MTP 和推测解码&#10;&lt;/h3&gt;&lt;p&gt;普通自回归解码一次只确定一个 token。推测解码先草拟多个 token，再由目标模型一次验证。经典的 &lt;a class="link" href="https://proceedings.mlr.press/v202/leviathan23a.html" target="_blank" rel="noopener"&#10; &gt;Speculative Decoding&lt;/a&gt; 使用接受/拒绝采样保持目标模型的输出分布，因此草拟模型不会直接替代目标模型。&lt;/p&gt;&#10;&lt;p&gt;MTP 使用模型自带的 multi-token prediction head 预测后续 token。vLLM 的 &lt;a class="link" href="https://docs.vllm.ai/projects/speculators/en/stable/user_guide/algorithms/mtp/" target="_blank" rel="noopener"&#10; &gt;MTP 文档&lt;/a&gt;也把它归在 speculative decoding 中。&lt;/p&gt;&#10;&lt;p&gt;评估 MTP 时至少要记录：&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;&#10;&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;&#10;&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1&#10;&lt;/span&gt;&lt;span class="lnt"&gt;2&#10;&lt;/span&gt;&lt;span class="lnt"&gt;3&#10;&lt;/span&gt;&lt;span class="lnt"&gt;4&#10;&lt;/span&gt;&lt;span class="lnt"&gt;5&#10;&lt;/span&gt;&lt;span class="lnt"&gt;6&#10;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&#10;&lt;td class="lntd"&gt;&#10;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;draft acceptance rate&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;mean accepted length&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;inter-token latency&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;request throughput&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;P50 / P95 latency&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;端到端质量指标&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&#10;&lt;/div&gt;&#10;&lt;/div&gt;&lt;p&gt;接受率高不等于服务一定更快。草拟和验证本身也有计算成本；高并发下，普通 decode 已经能组成较大的 batch，MTP 的相对收益可能缩小。&lt;/p&gt;&#10;&lt;h3 id="并发不是越大越好"&gt;&lt;a href="#%e5%b9%b6%e5%8f%91%e4%b8%8d%e6%98%af%e8%b6%8a%e5%a4%a7%e8%b6%8a%e5%a5%bd" class="header-anchor"&gt;&lt;/a&gt;并发不是越大越好&#10;&lt;/h3&gt;&lt;p&gt;vLLM 使用 PagedAttention 和连续批处理提高显存利用率与吞吐。&lt;a class="link" href="https://arxiv.org/abs/2309.06180" target="_blank" rel="noopener"&#10; &gt;PagedAttention 论文&lt;/a&gt; 讨论了如何减少动态 KV cache 的碎片和重复保存，从而容纳更有效的 batch。&lt;/p&gt;&#10;&lt;p&gt;但 batch 增大后，每一步 decode 会有更多序列竞争计算和内存带宽。&lt;code&gt;max-num-seqs&lt;/code&gt;、客户端并发和吞吐之间通常不是单调关系，需要逐档压测。&lt;/p&gt;&#10;&lt;p&gt;排查服务瓶颈时，可以先做这样的判断：&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;&#10;&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;&#10;&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1&#10;&lt;/span&gt;&lt;span class="lnt"&gt;2&#10;&lt;/span&gt;&lt;span class="lnt"&gt;3&#10;&lt;/span&gt;&lt;span class="lnt"&gt;4&#10;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&#10;&lt;td class="lntd"&gt;&#10;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;队列高：检查调度容量、限流或服务副本&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;prefill 高：检查上下文、候选数量和前缀复用&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;decode 高：检查输出长度、MTP 和重复调用&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;GPU 不忙但流程慢：检查网络、串行节点和任务编排&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&#10;&lt;/div&gt;&#10;&lt;/div&gt;&lt;p&gt;GPU 利用率本身说明不了请求是否高效。它需要和队列、延迟、吞吐、KV cache 使用量一起看。&lt;/p&gt;&#10;&lt;h2 id="七一次结果需要保存哪些版本"&gt;&lt;a href="#%e4%b8%83%e4%b8%80%e6%ac%a1%e7%bb%93%e6%9e%9c%e9%9c%80%e8%a6%81%e4%bf%9d%e5%ad%98%e5%93%aa%e4%ba%9b%e7%89%88%e6%9c%ac" class="header-anchor"&gt;&lt;/a&gt;七、一次结果需要保存哪些版本&#10;&lt;/h2&gt;&lt;p&gt;模型版本只是复现条件的一部分。结果还会受到检索索引、标签集合、参考数据、策略配置、Prompt、采样参数和运行时版本影响。&lt;/p&gt;&#10;&lt;p&gt;对外部依赖，可以先规范化 JSON，再计算内容哈希；一次任务只引用不可变的快照 ID 和哈希，不在执行中途重新获取可能变化的数据。&lt;/p&gt;&#10;&lt;p&gt;我现在会把一次 LLM 实验看成下面这个元组：&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;&#10;&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;&#10;&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1&#10;&lt;/span&gt;&lt;span class="lnt"&gt;2&#10;&lt;/span&gt;&lt;span class="lnt"&gt;3&#10;&lt;/span&gt;&lt;span class="lnt"&gt;4&#10;&lt;/span&gt;&lt;span class="lnt"&gt;5&#10;&lt;/span&gt;&lt;span class="lnt"&gt;6&#10;&lt;/span&gt;&lt;span class="lnt"&gt;7&#10;&lt;/span&gt;&lt;span class="lnt"&gt;8&#10;&lt;/span&gt;&lt;span class="lnt"&gt;9&#10;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&#10;&lt;td class="lntd"&gt;&#10;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;run = (&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; code_commit,&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; model_and_runtime,&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; prompt_version,&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; decoding_config,&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; dependency_snapshot,&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; evaluation_dataset,&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; random_and_request_metadata&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;)&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&#10;&lt;/div&gt;&#10;&lt;/div&gt;&lt;p&gt;少了其中一项，复现失败时都可能说不清原因。即使温度设为 0，也不要假设系统绝对确定。并行归约、批处理顺序、服务版本和上游自然语言状态仍可能产生差异。&lt;/p&gt;&#10;&lt;p&gt;运行配置可以进入版本控制，密钥不可以。可复现实验和 secret 管理是两件事。&lt;/p&gt;&#10;&lt;h2 id="八通用-prompt-不应该保存本地政策"&gt;&lt;a href="#%e5%85%ab%e9%80%9a%e7%94%a8-prompt-%e4%b8%8d%e5%ba%94%e8%af%a5%e4%bf%9d%e5%ad%98%e6%9c%ac%e5%9c%b0%e6%94%bf%e7%ad%96" class="header-anchor"&gt;&lt;/a&gt;八、通用 Prompt 不应该保存本地政策&#10;&lt;/h2&gt;&lt;p&gt;领域示例经常不只是在教模型怎样推理，还可能暗中携带业务政策。直接删掉示例，模型失去的也许不是语言能力，而是决策边界。&lt;/p&gt;&#10;&lt;p&gt;更清楚的拆法是：&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;&#10;&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;&#10;&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1&#10;&lt;/span&gt;&lt;span class="lnt"&gt;2&#10;&lt;/span&gt;&lt;span class="lnt"&gt;3&#10;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&#10;&lt;td class="lntd"&gt;&#10;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;通用推理层：怎样阅读事实、比较候选、处理冲突&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;领域知识层：常见概念、关系和表达方式&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;本地政策层：允许的边界、例外规则和风险阈值&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&#10;&lt;/div&gt;&#10;&lt;/div&gt;&lt;p&gt;推理方法可以复用，政策应显式配置和版本化。所谓泛化，也需要在新的标签集合、独立标注和不同输入分布上验证。把特定词汇从 Prompt 里删掉，只能说明文本变得更抽象，不能说明系统更通用。&lt;/p&gt;&#10;&lt;h2 id="以后再做类似系统我会先检查这些"&gt;&lt;a href="#%e4%bb%a5%e5%90%8e%e5%86%8d%e5%81%9a%e7%b1%bb%e4%bc%bc%e7%b3%bb%e7%bb%9f%e6%88%91%e4%bc%9a%e5%85%88%e6%a3%80%e6%9f%a5%e8%bf%99%e4%ba%9b" class="header-anchor"&gt;&lt;/a&gt;以后再做类似系统，我会先检查这些&#10;&lt;/h2&gt;&lt;ul&gt;&#10;&lt;li&gt;正确答案不在候选里时，系统是否会明确停止？&lt;/li&gt;&#10;&lt;li&gt;模型返回的是稳定 ID，还是重新生成标签名称？&lt;/li&gt;&#10;&lt;li&gt;schema 合法后，是否还有领域校验？&lt;/li&gt;&#10;&lt;li&gt;中间节点传递的是受控状态，还是会漂移的摘要？&lt;/li&gt;&#10;&lt;li&gt;Critic 是否知道具体要检查哪类错误？&lt;/li&gt;&#10;&lt;li&gt;自动做错和转人工是否使用不同门槛？&lt;/li&gt;&#10;&lt;li&gt;能否冻结任意节点输入，单独回放模型与运行时？&lt;/li&gt;&#10;&lt;li&gt;性能数据是否拆开 queue、prefill、decode 和调用次数？&lt;/li&gt;&#10;&lt;li&gt;每次实验能否还原代码、模型、Prompt、依赖和评测集？&lt;/li&gt;&#10;&lt;li&gt;通用方案是否在新的数据分布上验证过？&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p&gt;一个月前，我更关注模型能不能给出正确答案。现在我更在意，当模型给出答案时，系统有没有足够证据决定自动接受、转给人工，或者承认信息不足。这个问题没什么神秘的，却决定了 LLM 能不能进入真实流程。&lt;/p&gt;&#10;&lt;h2 id="延伸阅读"&gt;&lt;a href="#%e5%bb%b6%e4%bc%b8%e9%98%85%e8%af%bb" class="header-anchor"&gt;&lt;/a&gt;延伸阅读&#10;&lt;/h2&gt;&lt;ul&gt;&#10;&lt;li&gt;&lt;a class="link" href="https://openai.com/index/introducing-structured-outputs-in-the-api/" target="_blank" rel="noopener"&#10; &gt;Introducing Structured Outputs in the API&lt;/a&gt;&lt;/li&gt;&#10;&lt;li&gt;&lt;a class="link" href="https://arxiv.org/abs/2307.03172" target="_blank" rel="noopener"&#10; &gt;Lost in the Middle: How Language Models Use Long Contexts&lt;/a&gt;&lt;/li&gt;&#10;&lt;li&gt;&lt;a class="link" href="https://arxiv.org/abs/2303.17651" target="_blank" rel="noopener"&#10; &gt;Self-Refine: Iterative Refinement with Self-Feedback&lt;/a&gt;&lt;/li&gt;&#10;&lt;li&gt;&lt;a class="link" href="https://arxiv.org/abs/2310.01798" target="_blank" rel="noopener"&#10; &gt;Large Language Models Cannot Self-Correct Reasoning Yet&lt;/a&gt;&lt;/li&gt;&#10;&lt;li&gt;&lt;a class="link" href="https://proceedings.mlr.press/v202/leviathan23a.html" target="_blank" rel="noopener"&#10; &gt;Fast Inference from Transformers via Speculative Decoding&lt;/a&gt;&lt;/li&gt;&#10;&lt;li&gt;&lt;a class="link" href="https://docs.vllm.ai/en/v0.15.0/features/automatic_prefix_caching/" target="_blank" rel="noopener"&#10; &gt;vLLM Automatic Prefix Caching&lt;/a&gt;&lt;/li&gt;&#10;&lt;li&gt;&lt;a class="link" href="https://arxiv.org/abs/2309.06180" target="_blank" rel="noopener"&#10; &gt;Efficient Memory Management for Large Language Model Serving with PagedAttention&lt;/a&gt;&lt;/li&gt;&#10;&lt;/ul&gt;&#10;</description></item><item><title>多路召回学习笔记：BM25、Embedding、SPLADE 和 Reranker 各在解决什么</title><link>https://n571e.github.io/p/multi-recall-retrieval-notes/</link><pubDate>Fri, 15 May 2026 00:00:00 +0000</pubDate><guid>https://n571e.github.io/p/multi-recall-retrieval-notes/</guid><description>&lt;p&gt;最近在补 RAG 和检索系统的基础。刚开始看资料时，最容易混在一起的几个词就是：&lt;code&gt;BM25&lt;/code&gt;、&lt;code&gt;Embedding&lt;/code&gt;、&lt;code&gt;SPLADE&lt;/code&gt;、&lt;code&gt;Reranker&lt;/code&gt;，再加一个听起来很工程的词：多路召回。&lt;/p&gt;&#10;&lt;p&gt;单独看定义不难，难点在于把它们放进同一条检索链路里。复习时重点回答这些问题：&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;哪些方法负责“先找一批候选”？&lt;/li&gt;&#10;&lt;li&gt;哪些方法负责“把候选重新排好”？&lt;/li&gt;&#10;&lt;li&gt;BM25 和 Embedding 明明都能搜，为什么还要一起用？&lt;/li&gt;&#10;&lt;li&gt;Reranker 听起来更准，为什么不直接全库跑？&lt;/li&gt;&#10;&lt;li&gt;SPLADE 到底是关键词检索，还是语义检索？&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p&gt;这篇笔记不铺满公式，重点讲清每种方法在检索系统里的位置、优势和边界。&lt;/p&gt;&#10;&lt;hr&gt;&#10;&lt;h2 id="速读版"&gt;&lt;a href="#%e9%80%9f%e8%af%bb%e7%89%88" class="header-anchor"&gt;&lt;/a&gt;速读版&#10;&lt;/h2&gt;&lt;p&gt;先给一个压缩版结论：&lt;/p&gt;&#10;&lt;table&gt;&#10;&#9;&lt;thead&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;方法&lt;/th&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;系统位置&lt;/th&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;主要解决&lt;/th&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;优点&lt;/th&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;短板&lt;/th&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&lt;/thead&gt;&#10;&#9;&lt;tbody&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;BM25&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;关键词搜索&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;字面精确命中&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;快、可解释、对术语敏感&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;不懂同义表达&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Embedding&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;语义近邻搜索&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;意思接近但字面不同&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;泛化强，适合自然语言问题&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;细粒度容易混&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;SPLADE&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;神经稀疏搜索&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;语义扩展后的关键词匹配&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;兼顾稀疏结构和语义扩展&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;模型和索引更复杂&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Reranker&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;精排裁判&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;少量候选谁最相关&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;判断细，通常更准&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;慢，不适合全库扫描&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&lt;/tbody&gt;&#10;&lt;/table&gt;&#10;&lt;p&gt;一条典型链路大概是：&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;&#10;&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;&#10;&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1&#10;&lt;/span&gt;&lt;span class="lnt"&gt;2&#10;&lt;/span&gt;&lt;span class="lnt"&gt;3&#10;&lt;/span&gt;&lt;span class="lnt"&gt;4&#10;&lt;/span&gt;&lt;span class="lnt"&gt;5&#10;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&#10;&lt;td class="lntd"&gt;&#10;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;用户 query&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -&amp;gt; BM25 / Embedding / SPLADE 多路召回&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -&amp;gt; 候选合并、去重、分数融合&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -&amp;gt; Reranker 精排&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -&amp;gt; 返回 top-k 或交给下游生成模型&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&#10;&lt;/div&gt;&#10;&lt;/div&gt;&lt;p&gt;多路召回要解决的是：&lt;strong&gt;不要让单一检索方法承担所有召回责任，而是让不同方法互相补盲区。&lt;/strong&gt;&lt;/p&gt;&#10;&lt;hr&gt;&#10;&lt;h2 id="一先分清召回和精排"&gt;&lt;a href="#%e4%b8%80%e5%85%88%e5%88%86%e6%b8%85%e5%8f%ac%e5%9b%9e%e5%92%8c%e7%b2%be%e6%8e%92" class="header-anchor"&gt;&lt;/a&gt;一、先分清召回和精排&#10;&lt;/h2&gt;&lt;p&gt;搜索系统一般不会一开始就让最复杂的模型扫描整个文档库。文档量一大，成本会很高。&lt;/p&gt;&#10;&lt;p&gt;所以它会分成两步：&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;&#10;&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;&#10;&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1&#10;&lt;/span&gt;&lt;span class="lnt"&gt;2&#10;&lt;/span&gt;&lt;span class="lnt"&gt;3&#10;&lt;/span&gt;&lt;span class="lnt"&gt;4&#10;&lt;/span&gt;&lt;span class="lnt"&gt;5&#10;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&#10;&lt;td class="lntd"&gt;&#10;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Recall 召回&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; 从大量文档里快速捞出一批“可能相关”的候选。&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Ranking / Reranking 排序或精排&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; 在候选集合变小之后，再认真比较谁更相关。&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&#10;&lt;/div&gt;&#10;&lt;/div&gt;&lt;p&gt;召回阶段的目标是“尽量别漏掉相关候选”；精排阶段的目标是“在候选里排得更准”。&lt;/p&gt;&#10;&lt;p&gt;BM25、Embedding、SPLADE 通常更适合放在召回阶段；Reranker 通常更适合放在精排阶段。&lt;/p&gt;&#10;&lt;hr&gt;&#10;&lt;h2 id="二bm25最经典的关键词检索"&gt;&lt;a href="#%e4%ba%8cbm25%e6%9c%80%e7%bb%8f%e5%85%b8%e7%9a%84%e5%85%b3%e9%94%ae%e8%af%8d%e6%a3%80%e7%b4%a2" class="header-anchor"&gt;&lt;/a&gt;二、BM25：最经典的关键词检索&#10;&lt;/h2&gt;&lt;p&gt;BM25 是传统搜索里很经典的方法。它不理解深层语义，也不做推理，主要看一个问题：&lt;/p&gt;&#10;&#10; &lt;blockquote&gt;&#10; &lt;p&gt;query 里的词，在文档里有没有出现？出现的词有没有区分度？&lt;/p&gt;&#10;&#10; &lt;/blockquote&gt;&#10;&lt;p&gt;比如用户搜：&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;&#10;&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;&#10;&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1&#10;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&#10;&lt;td class="lntd"&gt;&#10;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;酒店住宿报销&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&#10;&lt;/div&gt;&#10;&lt;/div&gt;&lt;p&gt;如果一篇文档里出现了“酒店”“住宿”“报销”，它就应该比完全没出现这些词的文档更相关。&lt;/p&gt;&#10;&lt;p&gt;BM25 可以先记成：&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;&#10;&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;&#10;&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1&#10;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&#10;&lt;td class="lntd"&gt;&#10;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;BM25 分数 ≈ 词频 TF × 逆文档频率 IDF × 文档长度修正&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&#10;&lt;/div&gt;&#10;&lt;/div&gt;&lt;p&gt;复习时先理解这三个部分，再回到完整公式。&lt;/p&gt;&#10;&lt;h3 id="21-词频命中-query-词说明可能相关"&gt;&lt;a href="#21-%e8%af%8d%e9%a2%91%e5%91%bd%e4%b8%ad-query-%e8%af%8d%e8%af%b4%e6%98%8e%e5%8f%af%e8%83%bd%e7%9b%b8%e5%85%b3" class="header-anchor"&gt;&lt;/a&gt;2.1 词频：命中 query 词，说明可能相关&#10;&lt;/h3&gt;&lt;p&gt;如果 query 里有“住宿”，文档 A 出现了“住宿”，文档 B 没出现，那文档 A 大概率更相关。&lt;/p&gt;&#10;&lt;p&gt;但 BM25 不会让词频无限加分。一个词重复出现很多次，并不代表相关性可以无限升高。所以 BM25 会对词频做饱和处理。&lt;/p&gt;&#10;&lt;h3 id="22-idf越少见的词越有区分度"&gt;&lt;a href="#22-idf%e8%b6%8a%e5%b0%91%e8%a7%81%e7%9a%84%e8%af%8d%e8%b6%8a%e6%9c%89%e5%8c%ba%e5%88%86%e5%ba%a6" class="header-anchor"&gt;&lt;/a&gt;2.2 IDF：越少见的词，越有区分度&#10;&lt;/h3&gt;&lt;p&gt;有些词太常见：&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;&#10;&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;&#10;&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1&#10;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&#10;&lt;td class="lntd"&gt;&#10;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;申请、说明、费用、管理、流程&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&#10;&lt;/div&gt;&#10;&lt;/div&gt;&lt;p&gt;它们出现了当然有一点信息，但区分度不强。&lt;/p&gt;&#10;&lt;p&gt;有些词更具体：&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;&#10;&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;&#10;&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1&#10;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&#10;&lt;td class="lntd"&gt;&#10;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;住宿、发票、差旅、押金、手续费&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&#10;&lt;/div&gt;&#10;&lt;/div&gt;&lt;p&gt;这些词更能帮助判断文档主题。&lt;/p&gt;&#10;&lt;p&gt;这就是 IDF 的直觉：一个词在全库里越少见，它越能区分文档。&lt;/p&gt;&#10;&lt;h3 id="23-文档长度修正长文档不能天然占便宜"&gt;&lt;a href="#23-%e6%96%87%e6%a1%a3%e9%95%bf%e5%ba%a6%e4%bf%ae%e6%ad%a3%e9%95%bf%e6%96%87%e6%a1%a3%e4%b8%8d%e8%83%bd%e5%a4%a9%e7%84%b6%e5%8d%a0%e4%be%bf%e5%ae%9c" class="header-anchor"&gt;&lt;/a&gt;2.3 文档长度修正：长文档不能天然占便宜&#10;&lt;/h3&gt;&lt;p&gt;长文档词多，命中 query 词的概率天然更高。如果不修正，长文档会占便宜。&lt;/p&gt;&#10;&lt;p&gt;BM25 会考虑文档长度，让“短但精准命中”的文档也有机会排在前面。&lt;/p&gt;&#10;&lt;h3 id="24-bm25-的位置"&gt;&lt;a href="#24-bm25-%e7%9a%84%e4%bd%8d%e7%bd%ae" class="header-anchor"&gt;&lt;/a&gt;2.4 BM25 的位置&#10;&lt;/h3&gt;&lt;p&gt;BM25 特别适合这些场景：&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;搜索专有名词&lt;/li&gt;&#10;&lt;li&gt;搜索编号、代码、实体名&lt;/li&gt;&#10;&lt;li&gt;搜索业务词或固定术语&lt;/li&gt;&#10;&lt;li&gt;需要可解释的检索结果&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p&gt;比如：&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;&#10;&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;&#10;&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1&#10;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&#10;&lt;td class="lntd"&gt;&#10;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;增值税专用发票&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&#10;&lt;/div&gt;&#10;&lt;/div&gt;&lt;p&gt;如果文档里明确出现这个词，BM25 的表现通常很稳。&lt;/p&gt;&#10;&lt;p&gt;它的短板也很明显：不太懂同义词。&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;&#10;&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;&#10;&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1&#10;&lt;/span&gt;&lt;span class="lnt"&gt;2&#10;&lt;/span&gt;&lt;span class="lnt"&gt;3&#10;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&#10;&lt;td class="lntd"&gt;&#10;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;住酒店&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;住宿费&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;差旅住宿支出&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&#10;&lt;/div&gt;&#10;&lt;/div&gt;&lt;p&gt;这些表达意思接近，但字面不完全一样。如果没有共同词，BM25 可能就会漏。&lt;/p&gt;&#10;&lt;p&gt;复习锚点：BM25 适合作为基础召回，尤其适合术语、编号、实体名；短板是同义表达和口语化改写。&lt;/p&gt;&#10;&lt;hr&gt;&#10;&lt;h2 id="三embedding把文本放进语义空间"&gt;&lt;a href="#%e4%b8%89embedding%e6%8a%8a%e6%96%87%e6%9c%ac%e6%94%be%e8%bf%9b%e8%af%ad%e4%b9%89%e7%a9%ba%e9%97%b4" class="header-anchor"&gt;&lt;/a&gt;三、Embedding：把文本放进语义空间&#10;&lt;/h2&gt;&lt;p&gt;Embedding 的思路和 BM25 不同。BM25 看词有没有命中；Embedding 把文本编码成向量。&lt;/p&gt;&#10;&lt;p&gt;语义相近的文本，在向量空间里应该更接近。&lt;/p&gt;&#10;&lt;p&gt;比如：&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;&#10;&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;&#10;&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1&#10;&lt;/span&gt;&lt;span class="lnt"&gt;2&#10;&lt;/span&gt;&lt;span class="lnt"&gt;3&#10;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&#10;&lt;td class="lntd"&gt;&#10;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;员工出差住酒店&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;差旅住宿费用&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;酒店房费报销&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&#10;&lt;/div&gt;&#10;&lt;/div&gt;&lt;p&gt;这些句子字面不同，但语义接近。好的 Embedding 模型会尽量把它们编码到相近的位置。&lt;/p&gt;&#10;&lt;p&gt;检索时通常是这样：&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;&#10;&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;&#10;&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1&#10;&lt;/span&gt;&lt;span class="lnt"&gt;2&#10;&lt;/span&gt;&lt;span class="lnt"&gt;3&#10;&lt;/span&gt;&lt;span class="lnt"&gt;4&#10;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&#10;&lt;td class="lntd"&gt;&#10;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;文档库里的文档 -&amp;gt; 提前编码成向量&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;用户 query -&amp;gt; 编码成向量&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;计算 query 向量和文档向量的相似度&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;返回最相似的 top-k 文档&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&#10;&lt;/div&gt;&#10;&lt;/div&gt;&lt;p&gt;最常见的相似度是余弦相似度：&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;&#10;&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;&#10;&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1&#10;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&#10;&lt;td class="lntd"&gt;&#10;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;cosine similarity = 两个向量方向的接近程度&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&#10;&lt;/div&gt;&#10;&lt;/div&gt;&lt;p&gt;它关心的是方向，而不是文本长度。两个文本语义越接近，向量夹角通常越小，相似度越高。&lt;/p&gt;&#10;&lt;h3 id="31-embedding-解决了-bm25-的一部分盲区"&gt;&lt;a href="#31-embedding-%e8%a7%a3%e5%86%b3%e4%ba%86-bm25-%e7%9a%84%e4%b8%80%e9%83%a8%e5%88%86%e7%9b%b2%e5%8c%ba" class="header-anchor"&gt;&lt;/a&gt;3.1 Embedding 解决了 BM25 的一部分盲区&#10;&lt;/h3&gt;&lt;p&gt;用户的问题往往很口语化：&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;&#10;&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;&#10;&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1&#10;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&#10;&lt;td class="lntd"&gt;&#10;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;出差住酒店怎么报销&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&#10;&lt;/div&gt;&#10;&lt;/div&gt;&lt;p&gt;文档标题可能是：&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;&#10;&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;&#10;&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1&#10;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&#10;&lt;td class="lntd"&gt;&#10;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;差旅住宿费用管理办法&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&#10;&lt;/div&gt;&#10;&lt;/div&gt;&lt;p&gt;BM25 如果没有命中关键分词，可能不够稳；Embedding 更可能从整体意思上判断它们相关。&lt;/p&gt;&#10;&lt;p&gt;Embedding 适合：&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;同义表达&lt;/li&gt;&#10;&lt;li&gt;口语化 query&lt;/li&gt;&#10;&lt;li&gt;标题和问题表达方式不一致&lt;/li&gt;&#10;&lt;li&gt;需要整体语义理解的场景&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;h3 id="32-embedding-也会犯语义太近的错"&gt;&lt;a href="#32-embedding-%e4%b9%9f%e4%bc%9a%e7%8a%af%e8%af%ad%e4%b9%89%e5%a4%aa%e8%bf%91%e7%9a%84%e9%94%99" class="header-anchor"&gt;&lt;/a&gt;3.2 Embedding 也会犯“语义太近”的错&#10;&lt;/h3&gt;&lt;p&gt;Embedding 的问题是，它有时会太模糊。&lt;/p&gt;&#10;&lt;p&gt;比如：&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;&#10;&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;&#10;&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1&#10;&lt;/span&gt;&lt;span class="lnt"&gt;2&#10;&lt;/span&gt;&lt;span class="lnt"&gt;3&#10;&lt;/span&gt;&lt;span class="lnt"&gt;4&#10;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&#10;&lt;td class="lntd"&gt;&#10;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;技术服务费&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;咨询服务费&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;软件服务费&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;会议服务费&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&#10;&lt;/div&gt;&#10;&lt;/div&gt;&lt;p&gt;从语义上看都和“服务”有关，向量空间里可能靠得很近。但在实际业务里，它们可能需要被分到不同类别。&lt;/p&gt;&#10;&lt;p&gt;复习锚点：Embedding 擅长找“语义相近”的内容，但在术语边界、细分类别、编号实体上可能不够精确。因此它常和 BM25 搭配使用。&lt;/p&gt;&#10;&lt;hr&gt;&#10;&lt;h2 id="四为什么需要多路召回"&gt;&lt;a href="#%e5%9b%9b%e4%b8%ba%e4%bb%80%e4%b9%88%e9%9c%80%e8%a6%81%e5%a4%9a%e8%b7%af%e5%8f%ac%e5%9b%9e" class="header-anchor"&gt;&lt;/a&gt;四、为什么需要多路召回&#10;&lt;/h2&gt;&lt;p&gt;BM25 和 Embedding 的优缺点互补。BM25 负责词面命中，Embedding 负责语义相近；SPLADE 可以补充神经稀疏召回。多路召回就是同时跑多种召回方法，再合并候选：&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;&#10;&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;&#10;&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1&#10;&lt;/span&gt;&lt;span class="lnt"&gt;2&#10;&lt;/span&gt;&lt;span class="lnt"&gt;3&#10;&lt;/span&gt;&lt;span class="lnt"&gt;4&#10;&lt;/span&gt;&lt;span class="lnt"&gt;5&#10;&lt;/span&gt;&lt;span class="lnt"&gt;6&#10;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&#10;&lt;td class="lntd"&gt;&#10;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;BM25 召回一批&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Embedding 召回一批&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;SPLADE 也可以召回一批&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -&amp;gt; 合并候选&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -&amp;gt; 去重&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -&amp;gt; 融合或重排&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&#10;&lt;/div&gt;&#10;&lt;/div&gt;&lt;p&gt;举个简单例子。&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;&#10;&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;&#10;&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1&#10;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&#10;&lt;td class="lntd"&gt;&#10;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;query: 出差住酒店怎么报销&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&#10;&lt;/div&gt;&#10;&lt;/div&gt;&lt;p&gt;BM25 可能找到：&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;&#10;&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;&#10;&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1&#10;&lt;/span&gt;&lt;span class="lnt"&gt;2&#10;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&#10;&lt;td class="lntd"&gt;&#10;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;酒店发票填写要求&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;住宿费报销单填写说明&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&#10;&lt;/div&gt;&#10;&lt;/div&gt;&lt;p&gt;Embedding 可能找到：&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;&#10;&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;&#10;&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1&#10;&lt;/span&gt;&lt;span class="lnt"&gt;2&#10;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&#10;&lt;td class="lntd"&gt;&#10;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;差旅费用管理办法&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;员工出差报销流程&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&#10;&lt;/div&gt;&#10;&lt;/div&gt;&lt;p&gt;两路结果合起来，比单独用一路更稳。&lt;/p&gt;&#10;&lt;h3 id="41-分数不能直接相加"&gt;&lt;a href="#41-%e5%88%86%e6%95%b0%e4%b8%8d%e8%83%bd%e7%9b%b4%e6%8e%a5%e7%9b%b8%e5%8a%a0" class="header-anchor"&gt;&lt;/a&gt;4.1 分数不能直接相加&#10;&lt;/h3&gt;&lt;p&gt;多路召回带来一个麻烦：不同方法的分数不是一个尺度。&lt;/p&gt;&#10;&lt;p&gt;BM25 可能返回：&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;&#10;&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;&#10;&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1&#10;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&#10;&lt;td class="lntd"&gt;&#10;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;12.7, 8.3, 3.1&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&#10;&lt;/div&gt;&#10;&lt;/div&gt;&lt;p&gt;Embedding 的余弦相似度可能是：&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;&#10;&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;&#10;&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1&#10;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&#10;&lt;td class="lntd"&gt;&#10;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;0.82, 0.77, 0.65&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&#10;&lt;/div&gt;&#10;&lt;/div&gt;&lt;p&gt;这些分数不能直接相加。&lt;/p&gt;&#10;&lt;p&gt;常见处理方法有两类。&lt;/p&gt;&#10;&lt;p&gt;第一类是分数归一化，例如 min-max normalization，把不同来源的分数压到相近区间，再加权融合：&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;&#10;&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;&#10;&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1&#10;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&#10;&lt;td class="lntd"&gt;&#10;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;final_score = 0.5 * norm(BM25) + 0.5 * norm(Embedding)&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&#10;&lt;/div&gt;&#10;&lt;/div&gt;&lt;p&gt;第二类是只看排名，比如 Reciprocal Rank Fusion。它不太关心原始分数，而是看一个文档在每一路里排第几：&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;&#10;&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;&#10;&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1&#10;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&#10;&lt;td class="lntd"&gt;&#10;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;RRF(d) = sum(1 / (k + rank_i(d)))&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&#10;&lt;/div&gt;&#10;&lt;/div&gt;&lt;p&gt;如果一个文档在多路召回里都排得靠前，它的融合分数就会更高。&lt;/p&gt;&#10;&lt;p&gt;RRF 的复习锚点：&lt;strong&gt;多路召回都排得靠前的文档，融合后应该获得更高优先级。&lt;/strong&gt;&lt;/p&gt;&#10;&lt;hr&gt;&#10;&lt;h2 id="五reranker候选少了以后再认真判断"&gt;&lt;a href="#%e4%ba%94reranker%e5%80%99%e9%80%89%e5%b0%91%e4%ba%86%e4%bb%a5%e5%90%8e%e5%86%8d%e8%ae%a4%e7%9c%9f%e5%88%a4%e6%96%ad" class="header-anchor"&gt;&lt;/a&gt;五、Reranker：候选少了以后，再认真判断&#10;&lt;/h2&gt;&lt;p&gt;如果 BM25、Embedding、SPLADE 负责先找候选，Reranker 负责在候选变少后做细粒度比较。&lt;/p&gt;&#10;&lt;p&gt;Embedding 通常是 bi-encoder：&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;&#10;&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;&#10;&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1&#10;&lt;/span&gt;&lt;span class="lnt"&gt;2&#10;&lt;/span&gt;&lt;span class="lnt"&gt;3&#10;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&#10;&lt;td class="lntd"&gt;&#10;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;query 单独编码&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;doc 单独编码&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;然后算向量相似度&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&#10;&lt;/div&gt;&#10;&lt;/div&gt;&lt;p&gt;这种方式快，因为文档向量可以提前算好。&lt;/p&gt;&#10;&lt;p&gt;Reranker 通常接近 cross-encoder 思路：&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;&#10;&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;&#10;&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1&#10;&lt;/span&gt;&lt;span class="lnt"&gt;2&#10;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&#10;&lt;td class="lntd"&gt;&#10;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;把 query 和 doc 放在一起&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;让模型直接判断相关性&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&#10;&lt;/div&gt;&#10;&lt;/div&gt;&lt;p&gt;例如：&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;&#10;&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;&#10;&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1&#10;&lt;/span&gt;&lt;span class="lnt"&gt;2&#10;&lt;/span&gt;&lt;span class="lnt"&gt;3&#10;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&#10;&lt;td class="lntd"&gt;&#10;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;query: 出差住酒店怎么报销&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;doc: 差旅住宿费用管理办法&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;score: 0.93&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&#10;&lt;/div&gt;&#10;&lt;/div&gt;&lt;p&gt;模型可以同时看到 query 和 doc，细粒度判断它们是否匹配。&lt;/p&gt;&#10;&lt;h3 id="51-为什么-reranker-不直接全库跑"&gt;&lt;a href="#51-%e4%b8%ba%e4%bb%80%e4%b9%88-reranker-%e4%b8%8d%e7%9b%b4%e6%8e%a5%e5%85%a8%e5%ba%93%e8%b7%91" class="header-anchor"&gt;&lt;/a&gt;5.1 为什么 Reranker 不直接全库跑&#10;&lt;/h3&gt;&lt;p&gt;Reranker 通常更准，但也更慢。&lt;/p&gt;&#10;&lt;p&gt;如果文档库有 100 万篇，让 Reranker 对每篇都判断一次：&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;&#10;&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;&#10;&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1&#10;&lt;/span&gt;&lt;span class="lnt"&gt;2&#10;&lt;/span&gt;&lt;span class="lnt"&gt;3&#10;&lt;/span&gt;&lt;span class="lnt"&gt;4&#10;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&#10;&lt;td class="lntd"&gt;&#10;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;query-doc1&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;query-doc2&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;...&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;query-doc1000000&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&#10;&lt;/div&gt;&#10;&lt;/div&gt;&lt;p&gt;成本太高。&lt;/p&gt;&#10;&lt;p&gt;所以它一般放在召回之后：&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;&#10;&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;&#10;&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1&#10;&lt;/span&gt;&lt;span class="lnt"&gt;2&#10;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&#10;&lt;td class="lntd"&gt;&#10;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;先用 BM25 / Embedding / SPLADE 召回 top50&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;再用 Reranker 对这 50 个候选重新排序&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&#10;&lt;/div&gt;&#10;&lt;/div&gt;&lt;p&gt;复习锚点：召回阶段追求快和覆盖，Reranker 阶段追求细粒度相关性判断。&lt;/p&gt;&#10;&lt;h3 id="52-reranker-适合处理候选之间的细微差别"&gt;&lt;a href="#52-reranker-%e9%80%82%e5%90%88%e5%a4%84%e7%90%86%e5%80%99%e9%80%89%e4%b9%8b%e9%97%b4%e7%9a%84%e7%bb%86%e5%be%ae%e5%b7%ae%e5%88%ab" class="header-anchor"&gt;&lt;/a&gt;5.2 Reranker 适合处理候选之间的细微差别&#10;&lt;/h3&gt;&lt;p&gt;比如 query 是：&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;&#10;&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;&#10;&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1&#10;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&#10;&lt;td class="lntd"&gt;&#10;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;酒店发票抬头填错了怎么办&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&#10;&lt;/div&gt;&#10;&lt;/div&gt;&lt;p&gt;召回阶段可能找出：&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;&#10;&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;&#10;&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1&#10;&lt;/span&gt;&lt;span class="lnt"&gt;2&#10;&lt;/span&gt;&lt;span class="lnt"&gt;3&#10;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&#10;&lt;td class="lntd"&gt;&#10;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;酒店发票填写要求&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;发票作废与重开流程&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;差旅住宿费报销规则&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&#10;&lt;/div&gt;&#10;&lt;/div&gt;&lt;p&gt;这些都相关，但最该排前面的可能是“发票作废与重开流程”。Reranker 就是在这种时候发挥价值。&lt;/p&gt;&#10;&lt;hr&gt;&#10;&lt;h2 id="六splade神经网络版的关键词检索"&gt;&lt;a href="#%e5%85%adsplade%e7%a5%9e%e7%bb%8f%e7%bd%91%e7%bb%9c%e7%89%88%e7%9a%84%e5%85%b3%e9%94%ae%e8%af%8d%e6%a3%80%e7%b4%a2" class="header-anchor"&gt;&lt;/a&gt;六、SPLADE：神经网络版的关键词检索&#10;&lt;/h2&gt;&lt;p&gt;SPLADE 是这几个方法里最容易混淆的一块，可以先这样理解：&lt;/p&gt;&#10;&#10; &lt;blockquote&gt;&#10; &lt;p&gt;SPLADE 试图把 BM25 的稀疏可解释性，和神经模型的语义扩展能力接起来。&lt;/p&gt;&#10;&#10; &lt;/blockquote&gt;&#10;&lt;p&gt;BM25 在词项空间工作。文档里有哪些词，就在哪些词上有统计权重。&lt;/p&gt;&#10;&lt;p&gt;Embedding 在稠密向量空间工作。每个维度不一定有明确含义，但整体能表达语义。&lt;/p&gt;&#10;&lt;p&gt;SPLADE 也在词表空间里工作。假设模型词表有 30,000 个 token，那么一段文本会被表示成一个 30,000 维向量。&lt;/p&gt;&#10;&lt;p&gt;不同的是，这个向量很稀疏：&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;&#10;&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;&#10;&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1&#10;&lt;/span&gt;&lt;span class="lnt"&gt;2&#10;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&#10;&lt;td class="lntd"&gt;&#10;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;大部分维度是 0&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;少数相关 token 维度有正权重&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&#10;&lt;/div&gt;&#10;&lt;/div&gt;&lt;p&gt;比如用户输入：&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;&#10;&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;&#10;&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1&#10;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&#10;&lt;td class="lntd"&gt;&#10;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;住酒店&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&#10;&lt;/div&gt;&#10;&lt;/div&gt;&lt;p&gt;SPLADE 可能不只激活“酒店”，还会激活：&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;&#10;&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;&#10;&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1&#10;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&#10;&lt;td class="lntd"&gt;&#10;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;住宿、旅馆、房费、差旅&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&#10;&lt;/div&gt;&#10;&lt;/div&gt;&lt;p&gt;这相当于模型自动帮 query 做语义扩展，但结果仍然落在稀疏词项空间里。&lt;/p&gt;&#10;&lt;h3 id="61-splade-的激活直觉"&gt;&lt;a href="#61-splade-%e7%9a%84%e6%bf%80%e6%b4%bb%e7%9b%b4%e8%a7%89" class="header-anchor"&gt;&lt;/a&gt;6.1 SPLADE 的激活直觉&#10;&lt;/h3&gt;&lt;p&gt;很多 SPLADE 实现会对模型输出做类似这样的变换：&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;&#10;&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;&#10;&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1&#10;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&#10;&lt;td class="lntd"&gt;&#10;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;log(1 + ReLU(x))&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&#10;&lt;/div&gt;&#10;&lt;/div&gt;&lt;p&gt;拆开看：&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;&lt;code&gt;ReLU(x)&lt;/code&gt;：负数清零，只保留正向激活。&lt;/li&gt;&#10;&lt;li&gt;&lt;code&gt;log(1 + x)&lt;/code&gt;：压缩过大的权重，让数值更稳定。&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p&gt;这样得到的是非负稀疏权重。&lt;/p&gt;&#10;&lt;p&gt;检索时可以直接做点积：&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;&#10;&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;&#10;&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1&#10;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&#10;&lt;td class="lntd"&gt;&#10;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;score(query, doc) = sparse_query_vector · sparse_doc_vector&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&#10;&lt;/div&gt;&#10;&lt;/div&gt;&lt;p&gt;如果 query 和 doc 在相同或相关 token 上都有权重，分数就会变高。&lt;/p&gt;&#10;&lt;h3 id="62-splade-放在哪个位置"&gt;&lt;a href="#62-splade-%e6%94%be%e5%9c%a8%e5%93%aa%e4%b8%aa%e4%bd%8d%e7%bd%ae" class="header-anchor"&gt;&lt;/a&gt;6.2 SPLADE 放在哪个位置&#10;&lt;/h3&gt;&lt;p&gt;可以把它放在 BM25 和 Embedding 中间理解：&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;&#10;&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;&#10;&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1&#10;&lt;/span&gt;&lt;span class="lnt"&gt;2&#10;&lt;/span&gt;&lt;span class="lnt"&gt;3&#10;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&#10;&lt;td class="lntd"&gt;&#10;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;BM25 ：词面匹配，稀疏，可解释&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Embedding ：语义匹配，稠密，不太可解释&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;SPLADE ：神经稀疏匹配，有语义扩展，也保留词项空间&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&#10;&lt;/div&gt;&#10;&lt;/div&gt;&lt;p&gt;SPLADE 吸引人的地方在于，它不像普通 Embedding 那样完全进入黑盒稠密空间，也不像 BM25 那样只依赖原始词面。它可以学习到一些扩展关系。&lt;/p&gt;&#10;&lt;p&gt;它的成本也更高：需要专门模型，向量维度很高，索引和部署比 BM25 更复杂。&lt;/p&gt;&#10;&lt;hr&gt;&#10;&lt;h2 id="七一个完整检索链路可以怎么想"&gt;&lt;a href="#%e4%b8%83%e4%b8%80%e4%b8%aa%e5%ae%8c%e6%95%b4%e6%a3%80%e7%b4%a2%e9%93%be%e8%b7%af%e5%8f%af%e4%bb%a5%e6%80%8e%e4%b9%88%e6%83%b3" class="header-anchor"&gt;&lt;/a&gt;七、一个完整检索链路可以怎么想&#10;&lt;/h2&gt;&lt;p&gt;假设用户问：&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;&#10;&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;&#10;&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1&#10;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&#10;&lt;td class="lntd"&gt;&#10;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;差旅住宿发票丢了还能报销吗&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&#10;&lt;/div&gt;&#10;&lt;/div&gt;&lt;p&gt;一条比较完整的检索链路可以这样组织：&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;&#10;&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;&#10;&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1&#10;&lt;/span&gt;&lt;span class="lnt"&gt;2&#10;&lt;/span&gt;&lt;span class="lnt"&gt;3&#10;&lt;/span&gt;&lt;span class="lnt"&gt;4&#10;&lt;/span&gt;&lt;span class="lnt"&gt;5&#10;&lt;/span&gt;&lt;span class="lnt"&gt;6&#10;&lt;/span&gt;&lt;span class="lnt"&gt;7&#10;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&#10;&lt;td class="lntd"&gt;&#10;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;1. BM25 找到包含“差旅”“住宿”“发票”“报销”的文档&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;2. Embedding 找到语义上接近“发票遗失”“报销材料补充”的文档&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;3. SPLADE 如果可用，再补一批神经稀疏召回结果&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;4. 合并候选，去掉重复文档&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;5. 用归一化加权或 RRF 做候选融合&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;6. 用 Reranker 对候选重新排序&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;7. 把 top 文档交给下游问答模型或直接返回&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&#10;&lt;/div&gt;&#10;&lt;/div&gt;&lt;p&gt;每一步对应一个具体问题：&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;BM25：有没有明确命中关键词？&lt;/li&gt;&#10;&lt;li&gt;Embedding：语义上像不像？&lt;/li&gt;&#10;&lt;li&gt;SPLADE：能不能做更聪明的词项扩展？&lt;/li&gt;&#10;&lt;li&gt;Reranker：候选里谁最贴合用户问题？&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;hr&gt;&#10;&lt;h2 id="八选型建议"&gt;&lt;a href="#%e5%85%ab%e9%80%89%e5%9e%8b%e5%bb%ba%e8%ae%ae" class="header-anchor"&gt;&lt;/a&gt;八、选型建议&#10;&lt;/h2&gt;&lt;p&gt;如果只是做一个小型知识库，最简单的起点通常是：&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;&#10;&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;&#10;&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1&#10;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&#10;&lt;td class="lntd"&gt;&#10;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Embedding 召回 + Reranker 精排&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&#10;&lt;/div&gt;&#10;&lt;/div&gt;&lt;p&gt;如果业务词很强，比如法规条款、财务科目、商品型号、错误代码，可以加上 BM25：&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;&#10;&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;&#10;&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1&#10;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&#10;&lt;td class="lntd"&gt;&#10;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;BM25 + Embedding 多路召回 + Reranker 精排&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&#10;&lt;/div&gt;&#10;&lt;/div&gt;&lt;p&gt;如果发现 BM25 词面太死、Embedding 又容易把细分类别混在一起，再考虑 SPLADE：&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;&#10;&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;&#10;&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1&#10;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&#10;&lt;td class="lntd"&gt;&#10;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;BM25 + Embedding + SPLADE 多路召回 + Reranker 精排&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&#10;&lt;/div&gt;&#10;&lt;/div&gt;&lt;p&gt;不要一开始就把所有东西都加上。检索系统越堆越复杂，调试成本也越高。更稳的做法是先跑通 baseline，再根据错误样本决定补哪一路。&lt;/p&gt;&#10;&lt;hr&gt;&#10;&lt;h2 id="小结"&gt;&lt;a href="#%e5%b0%8f%e7%bb%93" class="header-anchor"&gt;&lt;/a&gt;小结&#10;&lt;/h2&gt;&lt;p&gt;多路召回不是一个单独算法，而是一种系统设计思路：让不同检索方法从不同角度找候选，再用排序模型做最终筛选。&lt;/p&gt;&#10;&lt;p&gt;BM25 解决精确词面匹配，Embedding 解决语义泛化，SPLADE 尝试在稀疏词项空间里加入神经语义扩展，Reranker 负责最后的细粒度判断。&lt;/p&gt;&#10;&lt;p&gt;以后复习或设计检索链路时，优先问这些问题：&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;&#10;&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;&#10;&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1&#10;&lt;/span&gt;&lt;span class="lnt"&gt;2&#10;&lt;/span&gt;&lt;span class="lnt"&gt;3&#10;&lt;/span&gt;&lt;span class="lnt"&gt;4&#10;&lt;/span&gt;&lt;span class="lnt"&gt;5&#10;&lt;/span&gt;&lt;span class="lnt"&gt;6&#10;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&#10;&lt;td class="lntd"&gt;&#10;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;它用了哪些召回路？&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;每一路分别解决什么盲区？&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;候选是取并集，还是只在某一路结果里重打分？&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;不同分数怎么融合？&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Reranker 放在多少候选之后？&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;最终阈值有没有单独校准？&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&#10;&lt;/div&gt;&#10;&lt;/div&gt;&lt;p&gt;这些问题比单独背概念更有用。检索效果通常不是由某个单点算法决定的，而是由整条链路的召回覆盖、候选融合、精排质量和阈值校准共同决定的。&lt;/p&gt;&#10;</description></item></channel></rss>