<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>RAG on Khari Z's LogBook</title><link>https://n571e.github.io/tags/rag/</link><description>Recent content in RAG on Khari Z's LogBook</description><generator>Hugo -- gohugo.io</generator><language>zh-cn</language><lastBuildDate>Fri, 15 May 2026 00:00:00 +0000</lastBuildDate><atom:link href="https://n571e.github.io/tags/rag/index.xml" rel="self" type="application/rss+xml"/><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><item><title>大模型应用这一层：从评测、RAG 到 Agent</title><link>https://n571e.github.io/p/happy-llm-llm-applications-rag-agent/</link><pubDate>Sat, 02 May 2026 00:00:00 +0000</pubDate><guid>https://n571e.github.io/p/happy-llm-llm-applications-rag-agent/</guid><description>&lt;img src="https://n571e.github.io/" alt="Featured image of post 大模型应用这一层：从评测、RAG 到 Agent" /&gt;&lt;p&gt;第七章的主题很集中：大模型进入应用场景后，先要评估能力边界，再接入外部知识，最后才谈工具调用和任务执行。&lt;/p&gt;&#10;&lt;p&gt;这一章可以按三个模块复习：评测、RAG 和 Agent。评测判断模型在哪些任务上可靠；RAG 把外部文档接入生成过程；Agent 让模型通过工具参与行动。&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-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;评测 Evaluation&#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;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;RAG Retrieval-Augmented Generation&#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;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Agent&#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;hr&gt;&#10;&lt;h2 id="一先从评测开始不要只问模型强不强"&gt;&lt;a href="#%e4%b8%80%e5%85%88%e4%bb%8e%e8%af%84%e6%b5%8b%e5%bc%80%e5%a7%8b%e4%b8%8d%e8%a6%81%e5%8f%aa%e9%97%ae%e6%a8%a1%e5%9e%8b%e5%bc%ba%e4%b8%8d%e5%bc%ba" 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;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;它是否会正确使用工具&lt;/li&gt;&#10;&lt;li&gt;它在金融、法律、医疗等垂直领域是否可靠&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p&gt;所以第七章把评测集按能力拆开看，比如 &lt;code&gt;MMLU&lt;/code&gt; 偏通用多任务理解，&lt;code&gt;GSM8K/MATH&lt;/code&gt; 偏数学推理，&lt;code&gt;ARC/GPQA/HellaSwag&lt;/code&gt; 偏推理和常识，&lt;code&gt;InfiniteBench/Multi-needle&lt;/code&gt; 偏长文本，&lt;code&gt;MGSM&lt;/code&gt; 偏多语言数学。&lt;/p&gt;&#10;&lt;p&gt;&lt;strong&gt;评测是在给应用选择画边界。&lt;/strong&gt;&lt;/p&gt;&#10;&lt;p&gt;比如我要做知识问答系统，就不能只看聊天榜单，还要看事实性、长文档理解和检索后回答是否忠实。我要做工具型助手，就不能只看语言流畅度，还要关心工具调用正确率、参数是否填对、失败时能不能恢复。&lt;/p&gt;&#10;&lt;p&gt;复习锚点：评测不是为了得到一个总分，而是为了判断模型适合放进哪个场景，以及哪些能力不能靠感觉判断。&lt;/p&gt;&#10;&lt;hr&gt;&#10;&lt;h2 id="二rag把模型从只靠参数记忆拉回到外部知识"&gt;&lt;a href="#%e4%ba%8crag%e6%8a%8a%e6%a8%a1%e5%9e%8b%e4%bb%8e%e5%8f%aa%e9%9d%a0%e5%8f%82%e6%95%b0%e8%ae%b0%e5%bf%86%e6%8b%89%e5%9b%9e%e5%88%b0%e5%a4%96%e9%83%a8%e7%9f%a5%e8%af%86" class="header-anchor"&gt;&lt;/a&gt;二、RAG：把模型从“只靠参数记忆”拉回到外部知识&#10;&lt;/h2&gt;&lt;p&gt;第七章第二部分进入 RAG。这里的背景很直接：LLM 虽然会生成，但它有几个天然问题：&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;RAG 的思路很直接：生成之前先检索。用户提出问题后，系统先从文档库里找相关片段，再把这些片段作为上下文交给模型，让模型基于外部材料回答。&lt;/p&gt;&#10;&lt;p&gt;RAG 的主链路可以写成：&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;文档 -&amp;gt; 切分 -&amp;gt; 向量化 -&amp;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;用户问题 -&amp;gt; 向量化 -&amp;gt; 相似度检索 -&amp;gt; 上下文拼接 -&amp;gt; LLM 生成回答&#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;RAG 把知识系统拆成两个部分：&lt;/p&gt;&#10;&lt;ul&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 align="center"&gt;&#10; &lt;img src="https://raw.githubusercontent.com/datawhalechina/happy-llm/main/docs/images/7-images/7-2-rag.png" alt="RAG 流程图" width="90%" /&gt;&#10; &lt;p&gt;图：RAG 先检索相关上下文，再让模型基于上下文生成回答&lt;/p&gt;&#10;&lt;/div&gt;&#10;&lt;hr&gt;&#10;&lt;h2 id="三tiny-rag最小实现里要盯住的几个模块"&gt;&lt;a href="#%e4%b8%89tiny-rag%e6%9c%80%e5%b0%8f%e5%ae%9e%e7%8e%b0%e9%87%8c%e8%a6%81%e7%9b%af%e4%bd%8f%e7%9a%84%e5%87%a0%e4%b8%aa%e6%a8%a1%e5%9d%97" class="header-anchor"&gt;&lt;/a&gt;三、Tiny-RAG：最小实现里要盯住的几个模块&#10;&lt;/h2&gt;&lt;p&gt;第七章还写了一个 Tiny-RAG。这个实现很适合学习，因为它把 RAG 系统拆成了几个足够小的部件：&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;文档加载与切分：&lt;code&gt;ReadFiles&lt;/code&gt;&lt;/li&gt;&#10;&lt;li&gt;向量化：&lt;code&gt;BaseEmbeddings&lt;/code&gt; / &lt;code&gt;OpenAIEmbedding&lt;/code&gt;&lt;/li&gt;&#10;&lt;li&gt;向量存储与检索：&lt;code&gt;VectorStore&lt;/code&gt;&lt;/li&gt;&#10;&lt;li&gt;生成模型封装：&lt;code&gt;OpenAIChat&lt;/code&gt;&lt;/li&gt;&#10;&lt;li&gt;演示入口：&lt;code&gt;demo.py&lt;/code&gt;&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;div align="center"&gt;&#10; &lt;img src="https://raw.githubusercontent.com/datawhalechina/happy-llm/main/docs/images/7-images/7-2-tinyrag.png" alt="TinyRAG 项目结构" width="90%" /&gt;&#10; &lt;p&gt;图：Tiny-RAG 把 RAG 拆成文档、向量、检索和模型回答几个最小模块&lt;/p&gt;&#10;&lt;/div&gt;&#10;&lt;h3 id="31-文档切分决定证据粒度"&gt;&lt;a href="#31-%e6%96%87%e6%a1%a3%e5%88%87%e5%88%86%e5%86%b3%e5%ae%9a%e8%af%81%e6%8d%ae%e7%b2%92%e5%ba%a6" class="header-anchor"&gt;&lt;/a&gt;3.1 文档切分决定证据粒度&#10;&lt;/h3&gt;&lt;p&gt;文档加载部分支持 &lt;code&gt;pdf/md/txt&lt;/code&gt;，读出来之后会按 token 长度切成 chunk。这里要记两个参数：&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-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;max_token_len&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;600&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;cover_content&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;150&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;max_token_len&lt;/code&gt; 控制每个片段不要太长，&lt;code&gt;cover_content&lt;/code&gt; 则给相邻片段保留重叠内容。这个重叠很重要，因为很多语义边界不会刚好落在切分点上。如果完全硬切，检索时很容易丢掉上下文。&lt;/p&gt;&#10;&lt;p&gt;所以 chunk 策略本质上是在做取舍：&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;chunk 太短，语义不完整&lt;/li&gt;&#10;&lt;li&gt;chunk 太长，检索不精确，而且占上下文&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;复习锚点：chunk 策略表面上是数据预处理，实际是在决定模型后面能看到什么证据。&lt;/p&gt;&#10;&lt;h3 id="32-向量化层要抽象出来"&gt;&lt;a href="#32-%e5%90%91%e9%87%8f%e5%8c%96%e5%b1%82%e8%a6%81%e6%8a%bd%e8%b1%a1%e5%87%ba%e6%9d%a5" class="header-anchor"&gt;&lt;/a&gt;3.2 向量化层要抽象出来&#10;&lt;/h3&gt;&lt;p&gt;第七章先定义 &lt;code&gt;BaseEmbeddings&lt;/code&gt;，再写 &lt;code&gt;OpenAIEmbedding&lt;/code&gt;。这个设计很朴素，但教学价值很清楚：embedding 模型应该作为可替换模块接入 RAG。&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-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;BaseEmbeddings&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="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_embedding&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&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="k"&gt;raise&lt;/span&gt; &lt;span class="ne"&gt;NotImplementedError&lt;/span&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; &lt;span class="nd"&gt;@classmethod&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;cosine_similarity&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;cls&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;vector1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;vector2&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="o"&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;这里的重点在于接口抽象：embedding 模型被包装成了可替换模块。以后如果从 API embedding 换成本地 embedding，RAG 主流程不用大改，只要新的类实现 &lt;code&gt;get_embedding&lt;/code&gt; 就行。&lt;/p&gt;&#10;&lt;p&gt;复习锚点：RAG 里的模型不止最终生成回答的 LLM，还有负责把文本映射到向量空间的 embedding 模型。embedding 质量不好，LLM 再强也可能拿不到正确上下文。&lt;/p&gt;&#10;&lt;h3 id="33-向量库的最小检索逻辑就是相似度排序"&gt;&lt;a href="#33-%e5%90%91%e9%87%8f%e5%ba%93%e7%9a%84%e6%9c%80%e5%b0%8f%e6%a3%80%e7%b4%a2%e9%80%bb%e8%be%91%e5%b0%b1%e6%98%af%e7%9b%b8%e4%bc%bc%e5%ba%a6%e6%8e%92%e5%ba%8f" class="header-anchor"&gt;&lt;/a&gt;3.3 向量库的最小检索逻辑就是相似度排序&#10;&lt;/h3&gt;&lt;p&gt;Tiny-RAG 的 &lt;code&gt;VectorStore&lt;/code&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;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-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;EmbeddingModel&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1&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="n"&gt;query_vector&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;EmbeddingModel&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get_embedding&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;query&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="n"&gt;scores&lt;/span&gt; &lt;span class="o"&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="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get_similarity&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;query_vector&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;vector&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="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;vector&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;vectors&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;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;document&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;argsort&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;scores&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="p"&gt;:][::&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]]&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tolist&lt;/span&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;/p&gt;&#10;&lt;ol&gt;&#10;&lt;li&gt;问题也要向量化&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;后续做项目时，可以把这里替换成 FAISS、Milvus、Qdrant、Chroma 之类的向量库，也可以补 reranker。但无论外壳怎么换，目标都还是“找到和问题最相关的上下文”。&lt;/p&gt;&#10;&lt;h3 id="34-rag-prompt-要约束模型只基于上下文回答"&gt;&lt;a href="#34-rag-prompt-%e8%a6%81%e7%ba%a6%e6%9d%9f%e6%a8%a1%e5%9e%8b%e5%8f%aa%e5%9f%ba%e4%ba%8e%e4%b8%8a%e4%b8%8b%e6%96%87%e5%9b%9e%e7%ad%94" class="header-anchor"&gt;&lt;/a&gt;3.4 RAG prompt 要约束模型只基于上下文回答&#10;&lt;/h3&gt;&lt;p&gt;最后，&lt;code&gt;OpenAIChat&lt;/code&gt; 会把检索到的内容放进一个 RAG 专用 prompt。这个 prompt 要明确告诉模型：&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;这个 prompt 给模型增加“证据约束”：答案应建立在可追溯材料上。RAG 做得好不好，要回到评测中看事实一致性、引用准确性和答案忠实性。&lt;/p&gt;&#10;&lt;hr&gt;&#10;&lt;h2 id="四rag-的学习收获系统质量不只取决于-llm"&gt;&lt;a href="#%e5%9b%9brag-%e7%9a%84%e5%ad%a6%e4%b9%a0%e6%94%b6%e8%8e%b7%e7%b3%bb%e7%bb%9f%e8%b4%a8%e9%87%8f%e4%b8%8d%e5%8f%aa%e5%8f%96%e5%86%b3%e4%ba%8e-llm" class="header-anchor"&gt;&lt;/a&gt;四、RAG 的学习收获：系统质量不只取决于 LLM&#10;&lt;/h2&gt;&lt;p&gt;RAG 系统的质量，很大一部分取决于生成模型之外的环节。&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;embedding 模型不匹配&lt;/li&gt;&#10;&lt;li&gt;Top-k 召回错了&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 很不一样。直接问模型时，问题通常被归因到“模型会不会”。但到了 RAG，系统多了检索层、索引层、上下文构造层，错误也会分散到更多地方。&lt;/p&gt;&#10;&lt;p&gt;所以看 RAG 系统时，不要只问“接了什么大模型”，还要问：&lt;/p&gt;&#10;&lt;ol&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;回答怎么评&lt;/li&gt;&#10;&lt;/ol&gt;&#10;&lt;p&gt;这几个问题答清楚，才算开始理解 RAG 的系统质量。&lt;/p&gt;&#10;&lt;hr&gt;&#10;&lt;h2 id="五agent从会回答走向会调用工具"&gt;&lt;a href="#%e4%ba%94agent%e4%bb%8e%e4%bc%9a%e5%9b%9e%e7%ad%94%e8%b5%b0%e5%90%91%e4%bc%9a%e8%b0%83%e7%94%a8%e5%b7%a5%e5%85%b7" class="header-anchor"&gt;&lt;/a&gt;五、Agent：从“会回答”走向“会调用工具”&#10;&lt;/h2&gt;&lt;p&gt;第七章最后讲 Agent。它先给了一个比较完整的定义：LLM Agent 以 LLM 为中心，结合目标理解、规划、记忆、工具使用、反思迭代等能力，去完成更复杂的任务。&lt;/p&gt;&#10;&lt;p&gt;Agent 可以整理成一句话：&lt;/p&gt;&#10;&#10; &lt;blockquote&gt;&#10; &lt;p&gt;&lt;strong&gt;Agent 是把 LLM 放进一个可行动的任务系统里。&lt;/strong&gt;&lt;/p&gt;&#10;&#10; &lt;/blockquote&gt;&#10;&lt;p&gt;普通聊天模型的输出主要是文字。Agent 的变化在于，模型可以在合适的时候选择工具，比如查时间、搜索百科、查询天气、做计算，然后把工具结果继续放回对话，让模型生成最终回答。&lt;/p&gt;&#10;&lt;p&gt;Agent 改变了 LLM 的角色：模型不只是生成文本，还参与调度工具。它需要承担这些职责：&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;读取工具返回结果&lt;/li&gt;&#10;&lt;li&gt;继续生成自然语言回复&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p&gt;第七章也把 Agent 粗略分成任务导向型、规划推理型、多 Agent 系统、探索学习型几类。当前复习可以先抓任务导向型，因为 Tiny-Agent 主要演示的就是这条最小链路。&lt;/p&gt;&#10;&lt;hr&gt;&#10;&lt;h2 id="六tiny-agenttool-calling-的最小闭环"&gt;&lt;a href="#%e5%85%adtiny-agenttool-calling-%e7%9a%84%e6%9c%80%e5%b0%8f%e9%97%ad%e7%8e%af" class="header-anchor"&gt;&lt;/a&gt;六、Tiny-Agent：tool calling 的最小闭环&#10;&lt;/h2&gt;&lt;p&gt;Tiny-Agent 的实现围绕 OpenAI 兼容接口里的 &lt;code&gt;tool_calls&lt;/code&gt; 展开。它有三个部件：&lt;/p&gt;&#10;&lt;ol&gt;&#10;&lt;li&gt;工具函数&lt;/li&gt;&#10;&lt;li&gt;工具 schema 转换&lt;/li&gt;&#10;&lt;li&gt;Agent 对话与工具调用循环&lt;/li&gt;&#10;&lt;/ol&gt;&#10;&lt;p&gt;工具函数本身很普通，比如获取时间、搜索 Wikipedia、查询天气。但在 Agent 里，普通函数要变成模型可理解的工具，就必须有清晰的函数名、参数类型和 docstring。&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-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_current_datetime&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;str&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="s2"&gt;&amp;#34;&amp;#34;&amp;#34;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt; 获取真实的当前日期和时间。&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt; &amp;#34;&amp;#34;&amp;#34;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="o"&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;function_to_json&lt;/code&gt; 会读取函数签名，把它转换成 tool schema：&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;span class="lnt"&gt;10&#10;&lt;/span&gt;&lt;span class="lnt"&gt;11&#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-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;function_to_json&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;func&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;dict&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="n"&gt;signature&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;inspect&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;signature&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;func&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="o"&gt;...&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&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="s2"&gt;&amp;#34;type&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;function&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="s2"&gt;&amp;#34;function&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="s2"&gt;&amp;#34;name&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;func&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="vm"&gt;__name__&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="s2"&gt;&amp;#34;description&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;func&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="vm"&gt;__doc__&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&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="s2"&gt;&amp;#34;parameters&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&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;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;模型看到的是结构化 schema。工具描述不清楚、参数设计不合理，模型就更容易选错工具或填错参数。&lt;/p&gt;&#10;&lt;div align="center"&gt;&#10; &lt;img src="https://raw.githubusercontent.com/datawhalechina/happy-llm/main/docs/images/7-images/7-3-Tiny_Agent.jpg" alt="Tiny-Agent 工具调用流程" width="88%" /&gt;&#10; &lt;p&gt;图：Tiny-Agent 的流程是模型提出工具调用，程序执行工具，再把结果交回模型&lt;/p&gt;&#10;&lt;/div&gt;&#10;&lt;h3 id="61-agent-类负责维护调用循环"&gt;&lt;a href="#61-agent-%e7%b1%bb%e8%b4%9f%e8%b4%a3%e7%bb%b4%e6%8a%a4%e8%b0%83%e7%94%a8%e5%be%aa%e7%8e%af" class="header-anchor"&gt;&lt;/a&gt;6.1 Agent 类负责维护调用循环&#10;&lt;/h3&gt;&lt;p&gt;&lt;code&gt;Agent.get_completion&lt;/code&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;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;用户输入&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -&amp;gt; 追加到 messages&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -&amp;gt; 调用模型并传入 tools&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -&amp;gt; 如果模型返回 tool_calls&#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; 把工具结果追加为 tool message&#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; 保存 assistant 回复&#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;这个循环说明了 tool calling 的边界：模型输出结构化调用请求，外层程序执行工具，工具结果再回到模型上下文中。&lt;/p&gt;&#10;&lt;p&gt;所以 Agent 的可靠性同时取决于模型和外层控制逻辑：&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;历史消息是否会越积越乱&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p&gt;这些问题在纯聊天模型里不太显眼，但一旦模型开始调用工具，就必须认真处理。&lt;/p&gt;&#10;&lt;h3 id="62-tiny-agent-很适合作为-agent-入门"&gt;&lt;a href="#62-tiny-agent-%e5%be%88%e9%80%82%e5%90%88%e4%bd%9c%e4%b8%ba-agent-%e5%85%a5%e9%97%a8" class="header-anchor"&gt;&lt;/a&gt;6.2 Tiny-Agent 很适合作为 Agent 入门&#10;&lt;/h3&gt;&lt;p&gt;第七章前面介绍了规划、记忆、反思等能力，但配套 Tiny-Agent 主要还是一个 tool calling demo。它有工具、有消息历史、有二次模型调用，但还没有实现复杂规划、长期记忆或反思循环。&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;/ul&gt;&#10;&lt;p&gt;等这一层明白之后，再看 ReAct、多 Agent、长期记忆、任务规划，才不会只停在概念名词上。&lt;/p&gt;&#10;&lt;p&gt;工程边界也要记住：Tiny-Agent 中用 &lt;code&gt;eval(...)&lt;/code&gt; 执行工具调用，适合教学演示；真实系统应该换成更安全的白名单分发。模型一旦能触发工具，权限和安全边界就必须认真处理。&lt;/p&gt;&#10;&lt;hr&gt;&#10;&lt;h2 id="七这一章留给我的应用层框架"&gt;&lt;a href="#%e4%b8%83%e8%bf%99%e4%b8%80%e7%ab%a0%e7%95%99%e7%bb%99%e6%88%91%e7%9a%84%e5%ba%94%e7%94%a8%e5%b1%82%e6%a1%86%e6%9e%b6" 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;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-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;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;RAG&#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;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Agent&#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;ol&gt;&#10;&lt;li&gt;&#10;&lt;p&gt;评测要看具体任务边界&lt;br&gt;&#10;榜单分数只能提供参考，应用还要关心场景、数据、语言、工具和安全边界。&lt;/p&gt;&#10;&lt;/li&gt;&#10;&lt;li&gt;&#10;&lt;p&gt;RAG 要把知识放到可更新的位置&lt;br&gt;&#10;外部文档、向量索引和检索结果共同决定模型能看到哪些依据。&lt;/p&gt;&#10;&lt;/li&gt;&#10;&lt;li&gt;&#10;&lt;p&gt;Agent 要把工具调用纳入控制流程&lt;br&gt;&#10;Agent 让模型不仅能回答，还能通过工具查询、计算、搜索和执行任务。&lt;/p&gt;&#10;&lt;/li&gt;&#10;&lt;/ol&gt;&#10;&lt;p&gt;第七章适合作为应用入口。Tiny-RAG 和 Tiny-Agent 给出了最小可运行骨架，帮助先看清系统边界。&lt;/p&gt;&#10;&lt;hr&gt;&#10;&lt;h2 id="八写在最后应用层要把不可靠变得可控"&gt;&lt;a href="#%e5%85%ab%e5%86%99%e5%9c%a8%e6%9c%80%e5%90%8e%e5%ba%94%e7%94%a8%e5%b1%82%e8%a6%81%e6%8a%8a%e4%b8%8d%e5%8f%af%e9%9d%a0%e5%8f%98%e5%be%97%e5%8f%af%e6%8e%a7" class="header-anchor"&gt;&lt;/a&gt;八、写在最后：应用层要把不可靠变得可控&#10;&lt;/h2&gt;&#10; &lt;blockquote&gt;&#10; &lt;p&gt;&lt;strong&gt;大模型应用不能只盯着模型本身，还要补上评测、检索、工具、日志和控制边界。&lt;/strong&gt;&lt;/p&gt;&#10;&#10; &lt;/blockquote&gt;&#10;&lt;p&gt;评测告诉我模型哪里能用，RAG 让回答尽量有依据，Agent 让模型可以接入行动接口。目标很实际：让模型在真实场景里更可控。&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;RAG 负责外部知识接口。&lt;/li&gt;&#10;&lt;li&gt;Agent 负责工具调度。&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="#%e5%8f%82%e8%80%83%e8%b5%84%e6%96%99" class="header-anchor"&gt;&lt;/a&gt;参考资料&#10;&lt;/h2&gt;&lt;ul&gt;&#10;&lt;li&gt;&lt;a class="link" href="https://github.com/datawhalechina/happy-llm/blob/main/docs/chapter7/%E7%AC%AC%E4%B8%83%E7%AB%A0%20%E5%A4%A7%E6%A8%A1%E5%9E%8B%E5%BA%94%E7%94%A8.md" target="_blank" rel="noopener"&#10; &gt;Datawhale. Happy-LLM：第七章《大模型应用》&lt;/a&gt;&lt;/li&gt;&#10;&lt;li&gt;&lt;a class="link" href="https://github.com/datawhalechina/happy-llm" target="_blank" rel="noopener"&#10; &gt;Datawhale Happy-LLM 仓库&lt;/a&gt;&lt;/li&gt;&#10;&lt;li&gt;&lt;a class="link" href="https://arxiv.org/pdf/2312.10997.pdf" target="_blank" rel="noopener"&#10; &gt;Retrieval-Augmented Generation for Large Language Models: A Survey&lt;/a&gt;&lt;/li&gt;&#10;&lt;/ul&gt;&#10;</description></item></channel></rss>