<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>检索增强生成 on 方糖猫Blog</title>
    <link>https://ftmao.com/blog/tags/%E6%A3%80%E7%B4%A2%E5%A2%9E%E5%BC%BA%E7%94%9F%E6%88%90/</link>
    <description>AI 视频、AI Agent 与工程实践笔记</description>
    <generator>Hugo</generator>
    <language>zh-CN</language>
    <lastBuildDate>Sat, 11 Apr 2026 10:00:00 +0800</lastBuildDate><atom:link href="https://ftmao.com/blog/tags/%E6%A3%80%E7%B4%A2%E5%A2%9E%E5%BC%BA%E7%94%9F%E6%88%90/index.xml" rel="self" type="application/rss+xml" /><item>
      <title>RAG 系统完全笔记：从检索器到生产部署</title>
      <link>https://ftmao.com/blog/posts/rag-notes/</link>
      <pubDate>Sat, 11 Apr 2026 10:00:00 +0800</pubDate>
      <guid>https://ftmao.com/blog/posts/rag-notes/</guid>
      <description>&lt;h1 id=&#34;rag-系统完全笔记从检索器到生产部署&#34;&gt;RAG 系统完全笔记：从检索器到生产部署&lt;/h1&gt;
&lt;blockquote&gt;
&lt;p&gt;最近重刷了吴恩达 DeepLearning.ai 的《Retrieval Augmented Generation》课程，整理了一份从检索器到生产部署的完整笔记。RAG 不只是「把文档塞进向量库然后问大模型」，它的每个环节都有值得细究的工程决策。本文在每个算法之外，也补充了对应的落地工具与最佳实践。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h2 id=&#34;一rag-是什么为什么需要它&#34;&gt;一、RAG 是什么？为什么需要它？&lt;/h2&gt;
&lt;p&gt;大语言模型（LLM）有两个天然缺陷：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;知识有截止日期&lt;/strong&gt;：训练数据之后发生的事件、私有文档，模型都不知道。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;容易 hallucination（幻觉）&lt;/strong&gt;：没有可靠依据时，模型会一本正经地编造答案。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;RAG 的解决思路很简单：&lt;strong&gt;在生成答案前，先从外部知识库中检索相关文档，把检索结果作为上下文喂给 LLM&lt;/strong&gt;。这样模型既能利用最新、最专有的知识，又能基于检索到的内容生成，显著降低幻觉风险。&lt;/p&gt;
&lt;p&gt;一个完整的 RAG 系统分为&lt;strong&gt;离线索引&lt;/strong&gt;和&lt;strong&gt;在线检索生成&lt;/strong&gt;两条流水线：&lt;/p&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-mermaid&#34; data-lang=&#34;mermaid&#34;&gt;flowchart LR
    subgraph 离线索引
        A[原始文档] --&amp;gt; B[文档分块 Chunking]
        B --&amp;gt; C[Embedding 编码]
        C --&amp;gt; D[(向量数据库)]
    end
    subgraph 在线检索生成
        E[用户查询] --&amp;gt; F[查询改写]
        F --&amp;gt; G[检索器 Retriever]
        D -.-&amp;gt; G
        G --&amp;gt; H[重排序 Re-ranking]
        H --&amp;gt; I[LLM 生成]
        I --&amp;gt; J[最终答案]
    end&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;&lt;p&gt;&lt;strong&gt;落地框架&lt;/strong&gt;：搭建整条 pipeline 不必从零写，&lt;strong&gt;LangChain&lt;/strong&gt; 和 &lt;strong&gt;LlamaIndex&lt;/strong&gt; 是最主流的两个 RAG 编排框架，检索器、分块器、重排序器、提示词模板都有现成实现；需要精细控制流程状态（如 Agentic RAG）时可以用 &lt;strong&gt;LangGraph&lt;/strong&gt;。入门建议先用 LlamaIndex 快速跑通，再根据瓶颈逐环节替换。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;二检索器找到正确的文档&#34;&gt;二、检索器：找到正确的文档&lt;/h2&gt;
&lt;p&gt;检索器是 RAG 系统的第一道关卡。常见检索方式有三种，以及它们的组合。&lt;/p&gt;
&lt;h3 id=&#34;21-元数据过滤&#34;&gt;2.1 元数据过滤&lt;/h3&gt;
&lt;p&gt;在向量搜索之前，先用结构化条件缩小范围：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;作者、发布时间、信源&lt;/li&gt;
&lt;li&gt;地区、类别、权限标签&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;例如，企业知识库中只允许查询「本部门」文档，就可以通过 &lt;code&gt;department_id&lt;/code&gt; 元数据过滤。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;最佳实践&lt;/strong&gt;：主流向量库都支持「过滤 + 向量搜索」的混合查询——Elasticsearch 的 &lt;code&gt;filter&lt;/code&gt; 子句、Qdrant 的 payload filter、pgvector 配合普通 &lt;code&gt;WHERE&lt;/code&gt; 条件都可以。&lt;strong&gt;权限过滤一定要放在检索阶段做&lt;/strong&gt;，而不是检索出来再在应用层过滤，否则 Top-K 里可能挤掉有权限的文档，还会把不该出现的文档内容带进 LLM 上下文。&lt;/p&gt;
&lt;h3 id=&#34;22-关键词搜索&#34;&gt;2.2 关键词搜索&lt;/h3&gt;
&lt;p&gt;基于词频的检索，计算查询词与文档的匹配程度。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;TF-IDF&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;TF（词频）&lt;/strong&gt;：查询中的词在一篇文档里出现了多少次。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;IDF（逆文档频率）&lt;/strong&gt;：查询中的词在所有文档中出现的频率的倒数。词越稀有，权重越高。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;写成公式：&lt;/p&gt;
&lt;p&gt;$$
\mathrm{tf}(t, d) = \frac{f_{t,d}}{\sum_{t&amp;rsquo; \in d} f_{t&amp;rsquo;,d}}, \qquad
\mathrm{idf}(t, D) = \log \frac{N}{1 + |{d \in D : t \in d}|}
$$&lt;/p&gt;
&lt;p&gt;公式中各符号的含义：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;$t$（term）&lt;/strong&gt;：一个词项（term），通常指查询中的某个词。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;$d$（document）&lt;/strong&gt;：一篇文档，即被评分的单篇文档。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;$D$（document collection）&lt;/strong&gt;：整个文档集合（语料库），包含全部 $N$ 篇文档。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;$f$&lt;/strong&gt;：词频计数函数（frequency）。$f_{t,d}$ 表示词 $t$ 在文档 $d$ 中出现的&lt;strong&gt;次数&lt;/strong&gt;，例如「检索」在某文档中出现 3 次，则 $f_{t,d} = 3$。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;$\sum_{t&amp;rsquo; \in d} f_{t&amp;rsquo;,d}$&lt;/strong&gt;：文档 $d$ 中所有词项的出现次数之和，即文档总词数（长度）。分子除以它，就把词频归一化成「占比」，避免长文档天然占优。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;$N$&lt;/strong&gt;：文档集合 $D$ 中的文档总数。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;$|{d \in D : t \in d}|$&lt;/strong&gt;：包含词 $t$ 的文档篇数（document frequency）。分母加 1 是平滑处理，防止词在所有文档中都不出现时除以零。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;直观理解：$\mathrm{tf}$ 衡量「这个词在本文档中有多重要」（出现占比越高越重要），$\mathrm{idf}$ 衡量「这个词在整个语料库中有多稀有」（越稀有的词越能区分文档，权重越高），两者相乘就是词的综合权重。&lt;/p&gt;
&lt;p&gt;文档 $d$ 对查询 $q$ 的得分即查询中各词的 TF-IDF 之和：&lt;/p&gt;
&lt;p&gt;$$
\mathrm{score}(q, d) = \sum_{t \in q} \mathrm{tf}(t, d) \cdot \mathrm{idf}(t, D)
$$&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;BM25&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;BM25 是 TF-IDF 的改进版，引入了两个可调参数：&lt;/p&gt;
&lt;p&gt;$$
\mathrm{score}(q, d) = \sum_{t \in q} \mathrm{idf}(t) \cdot
\frac{f_{t,d} \cdot (k_1 + 1)}
{f_{t,d} + k_1 \cdot \left(1 - b + b \cdot \dfrac{|d|}{\mathrm{avgdl}}\right)}
$$&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;$k_1$&lt;/strong&gt;：控制词频对评分的影响（词频饱和曲线），常见取值 $1.2 \sim 2.0$。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;$b$&lt;/strong&gt;：控制文档长度归一化的强度，$|d|$ 是文档长度，$\mathrm{avgdl}$ 是平均文档长度，常见取值 $0.75$。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;BM25 在短查询、精确匹配场景中仍然非常有效，比如查找包含特定术语的技术文档。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;工业实现：Elasticsearch&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;BM25 不是纸面算法，它就是 Elasticsearch 的&lt;strong&gt;默认评分器&lt;/strong&gt;（自 ES 5.0 / Lucene 6.0 起，替代了旧的 TF-IDF）。最普通的 &lt;code&gt;match&lt;/code&gt; 查询，底层走的就是 BM25：&lt;/p&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-json&#34; data-lang=&#34;json&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;GET&lt;/span&gt; &lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;/my-index/_search&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;query&amp;#34;&lt;/span&gt;: {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;match&amp;#34;&lt;/span&gt;: {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;content&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;检索增强生成&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;p&gt;$k_1$ 和 $b$ 两个参数可以在索引设置里自定义（注意修改 similarity 需要关闭索引或重建索引）：&lt;/p&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-json&#34; data-lang=&#34;json&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;PUT&lt;/span&gt; &lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;/my-index&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;settings&amp;#34;&lt;/span&gt;: {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;similarity&amp;#34;&lt;/span&gt;: {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;my_bm25&amp;#34;&lt;/span&gt;: {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;type&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;BM25&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;k1&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;1.2&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;b&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;0.75&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  },
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;mappings&amp;#34;&lt;/span&gt;: {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;properties&amp;#34;&lt;/span&gt;: {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;content&amp;#34;&lt;/span&gt;: {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;type&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;text&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;similarity&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;my_bm25&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;p&gt;调试时还可以加 &lt;code&gt;&amp;quot;explain&amp;quot;: true&lt;/code&gt;，ES 会返回每个文档 BM25 打分的详细拆解（tf、idf、长度归一化各自贡献了多少），对排查「为什么这篇文档排在前面」非常有用。&lt;/p&gt;
&lt;p&gt;不想维护 ES 集群时，轻量替代方案有 &lt;strong&gt;OpenSearch&lt;/strong&gt;（同源于 Lucene）、纯 Python 的 &lt;strong&gt;rank_bm25&lt;/strong&gt; 库，以及 LangChain 内置的 &lt;code&gt;BM25Retriever&lt;/code&gt;。注意中文 BM25 的效果强依赖分词器：ES 建议装 &lt;strong&gt;IK Analyzer&lt;/strong&gt;，并针对领域维护自定义词典（产品名、缩写、术语）。&lt;/p&gt;
&lt;h3 id=&#34;23-语义搜索&#34;&gt;2.3 语义搜索&lt;/h3&gt;
&lt;p&gt;将查询和文档都编码成向量，通过 &lt;strong&gt;余弦相似度（cosine similarity）&lt;/strong&gt; 找到语义相近的文档。即使关键词不同，语义接近也能召回。&lt;/p&gt;
&lt;p&gt;$$
\cos(\mathbf{q}, \mathbf{d}) = \frac{\mathbf{q} \cdot \mathbf{d}}{|\mathbf{q}| , |\mathbf{d}|}
= \frac{\sum_i q_i d_i}{\sqrt{\sum_i q_i^2} \cdot \sqrt{\sum_i d_i^2}}
$$&lt;/p&gt;
&lt;p&gt;余弦相似度取值在 $[-1, 1]$ 之间，越接近 $1$ 表示语义越相近。实际工程中通常先对向量做 L2 归一化，此时余弦相似度就退化为简单的内积，检索更高效。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;落地工具&lt;/strong&gt;：向量由 Embedding 模型生成，选型参考 Hugging Face 的 &lt;strong&gt;MTEB / C-MTEB 榜单&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;API 方案：OpenAI &lt;code&gt;text-embedding-3&lt;/code&gt; 系列、Cohere Embed、阿里 DashScope 通用文本向量。&lt;/li&gt;
&lt;li&gt;自托管方案：&lt;strong&gt;BGE-M3&lt;/strong&gt;（BAAI，中英文效果好，支持稠密 + 稀疏 + 多向量三种输出）、&lt;code&gt;gte-Qwen&lt;/code&gt; 系列、E5 系列，均可用 &lt;strong&gt;sentence-transformers&lt;/strong&gt; 或 &lt;strong&gt;FlagEmbedding&lt;/strong&gt; 库本地加载。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;最佳实践&lt;/strong&gt;：查询和文档必须用同一个 Embedding 模型（部分模型对查询和文档有不同前缀，如 E5 的 &lt;code&gt;query:&lt;/code&gt; / &lt;code&gt;passage:&lt;/code&gt;，不要混用）；更换 Embedding 模型意味着全量重建索引，选型时先在自有数据上做小规模评测再定。&lt;/p&gt;
&lt;h3 id=&#34;24-混合搜索&#34;&gt;2.4 混合搜索&lt;/h3&gt;
&lt;p&gt;实际工程中，通常把多种检索方式组合起来：&lt;/p&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-mermaid&#34; data-lang=&#34;mermaid&#34;&gt;flowchart LR
    Q[用户查询] --&amp;gt; K[关键词搜索 BM25]
    Q --&amp;gt; S[语义搜索 向量]
    K --&amp;gt; M[结果融合]
    S --&amp;gt; M
    M --&amp;gt; F[元数据过滤]
    F --&amp;gt; R[重排序]
    R --&amp;gt; O[最终上下文]&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;&lt;p&gt;两路结果分数量纲不同，直接相加不合理，标准做法是 &lt;strong&gt;RRF（Reciprocal Rank Fusion）&lt;/strong&gt;：只看排名不看分数。&lt;/p&gt;
&lt;p&gt;$$
\mathrm{RRF}(d) = \sum_{r \in \text{各路检索}} \frac{1}{k + \mathrm{rank}_r(d)}
$$&lt;/p&gt;
&lt;p&gt;其中 $k$ 是平滑常数，通常取 $60$。文档在任一路排得靠前，融合后得分就高。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;落地工具&lt;/strong&gt;：这套组合在 Elasticsearch 8.x 里一个引擎就能实现——BM25 + kNN 向量检索（底层 HNSW），原生支持 RRF 融合；代码侧则可以用 LangChain 的 &lt;code&gt;EnsembleRetriever&lt;/code&gt;（内部同样是 RRF）把 &lt;code&gt;BM25Retriever&lt;/code&gt; 和任意向量检索器组合起来。&lt;/p&gt;
&lt;h3 id=&#34;25-检索质量评估&#34;&gt;2.5 检索质量评估&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;精准度（Precision）&lt;/strong&gt;：检索出的相关文档 / 检索出的所有文档。反映结果的可信度。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;召回率（Recall）&lt;/strong&gt;：检索出的相关文档 / 所有相关文档。反映结果的完整度。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;$$
\mathrm{Precision} = \frac{|\text{相关文档} \cap \text{检索结果}|}{|\text{检索结果}|}, \qquad
\mathrm{Recall} = \frac{|\text{相关文档} \cap \text{检索结果}|}{|\text{相关文档}|}
$$&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;平均精准度（MAP）&lt;/strong&gt;：先对单次查询在相关文档出现的每个位置取精准度平均（AP），再对多次查询求平均。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;$$
\mathrm{MAP} = \frac{1}{|Q|} \sum_{q=1}^{|Q|} \mathrm{AP}(q), \qquad
\mathrm{AP}(q) = \frac{1}{m_q} \sum_{k=1}^{n} P(k) \cdot \mathrm{rel}(k)
$$&lt;/p&gt;
&lt;p&gt;其中 $m_q$ 是查询 $q$ 的相关文档总数，$P(k)$ 是前 $k$ 个结果的精准度，$\mathrm{rel}(k)$ 表示第 $k$ 个结果是否相关。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;平均倒数排名（MRR）&lt;/strong&gt;：最相关文档在结果列表中的排名的倒数。排名第一，MRR 就是 1；排名第十，MRR 就是 0.1。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;$$
\mathrm{MRR} = \frac{1}{|Q|} \sum_{q=1}^{|Q|} \frac{1}{\mathrm{rank}_q}
$$&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;落地工具&lt;/strong&gt;：评估的前提是有一批带标注的「黄金测试集」（golden dataset），通常 50-200 条真实用户问题加人工标注的相关文档。标注成本高时可以用 &lt;strong&gt;Ragas&lt;/strong&gt; 的合成数据功能自动生成测试集；&lt;strong&gt;Ragas&lt;/strong&gt; 和 &lt;strong&gt;DeepEval&lt;/strong&gt; 还都提供 context precision / context recall 等无需人工标注、由 LLM 打分的检索评估指标，适合纳入 CI 做回归测试。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;三向量数据库如何高效存储和检索&#34;&gt;三、向量数据库：如何高效存储和检索&lt;/h2&gt;
&lt;p&gt;向量数据库的核心问题是：&lt;strong&gt;给定一个查询向量，如何快速找到最相似的文档向量？&lt;/strong&gt;&lt;/p&gt;
&lt;h3 id=&#34;31-knn精确但慢&#34;&gt;3.1 KNN：精确但慢&lt;/h3&gt;
&lt;p&gt;KNN（K-Nearest Neighbors）会实时计算查询向量与所有文档向量的距离，结果准确，但当向量数量达到亿级时，计算量不可接受。设向量库有 $N$ 个 $d$ 维向量，暴力检索的复杂度为 $O(Nd)$。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;适用场景&lt;/strong&gt;：向量量在万级以内、或需要精确结果做基准评测时，暴力 KNN 反而最省事——FAISS 的 &lt;code&gt;IndexFlatIP&lt;/code&gt;、NumPy 一次矩阵乘法就够，不必引入索引结构。&lt;/p&gt;
&lt;h3 id=&#34;32-ann近似但快&#34;&gt;3.2 ANN：近似但快&lt;/h3&gt;
&lt;p&gt;ANN（Approximate Nearest Neighbor）通过构建近邻图或索引结构来加速检索，牺牲了极小的准确率，换取了数量级的性能提升。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;HNSW（Hierarchical Navigable Small World）&lt;/strong&gt; 是目前最常用的 ANN 算法之一，采用分层导航图结构，能够在亿级向量库中快速找到近似最近邻：&lt;/p&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-mermaid&#34; data-lang=&#34;mermaid&#34;&gt;flowchart TB
    Q[查询向量] --&amp;gt; E[顶层入口点]
    subgraph HNSW[&amp;#34;HNSW 分层图&amp;#34;]
        direction TB
        L2[&amp;#34;Layer 2（最稀疏，快速定位大致区域）&amp;#34;]
        L1[&amp;#34;Layer 1（中间过渡层）&amp;#34;]
        L0[&amp;#34;Layer 0（包含全部节点，精确找最近邻）&amp;#34;]
        L2 --&amp;gt; L1 --&amp;gt; L0
    end
    E --&amp;gt; L2
    L0 --&amp;gt; R[&amp;#34;Top-K 近似最近邻&amp;#34;]&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;&lt;p&gt;查询时从最稀疏的顶层开始贪心搜索，逐层向下，每层把入口点收敛到更接近目标的区域，最后在最稠密的第 0 层做精细查找。平均查询复杂度约为 $O(\log N)$。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;落地工具&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;向量库选型：&lt;strong&gt;Milvus&lt;/strong&gt;、&lt;strong&gt;Qdrant&lt;/strong&gt;、&lt;strong&gt;Weaviate&lt;/strong&gt;（独立向量数据库）、&lt;strong&gt;pgvector&lt;/strong&gt;（已有 PostgreSQL 时的最简方案）、&lt;strong&gt;Chroma&lt;/strong&gt;（轻量嵌入式）、&lt;strong&gt;FAISS&lt;/strong&gt;（Meta 的索引库，无服务化能力）、&lt;strong&gt;Elasticsearch kNN&lt;/strong&gt;（想复用 BM25 生态时）。&lt;/li&gt;
&lt;li&gt;HNSW 三个关键参数：&lt;code&gt;M&lt;/code&gt;（每个节点的最大连接数，默认 16，越大越准但越耗内存）、&lt;code&gt;ef_construction&lt;/code&gt;（建索引时的搜索宽度，默认 100）、&lt;code&gt;ef_search&lt;/code&gt;（查询时的搜索宽度，&lt;strong&gt;运行时调大它就能在召回率和延迟之间实时权衡&lt;/strong&gt;，是最常用的调优旋钮）。&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id=&#34;四文档分块粒度决定召回质量&#34;&gt;四、文档分块：粒度决定召回质量&lt;/h2&gt;
&lt;p&gt;把长文档切成小块（chunk）是 RAG 的关键步骤。chunk 太大或太小都有问题：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;太大&lt;/strong&gt;：一个向量要表达太多内容，指向性弱，容易引入无关信息。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;太小&lt;/strong&gt;：上下文不足，语义不明确。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;41-固定大小分块&#34;&gt;4.1 固定大小分块&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;文本：250 字符左右（例如 225 字符正文 + 25 字符重叠）。&lt;/li&gt;
&lt;li&gt;代码：500 字符左右，重叠 50-100 字符。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;重叠（overlap）能避免关键信息被切分断掉。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;落地工具&lt;/strong&gt;：LangChain 的 &lt;code&gt;CharacterTextSplitter&lt;/code&gt; / &lt;code&gt;RecursiveCharacterTextSplitter&lt;/code&gt;、LlamaIndex 的 &lt;code&gt;SentenceSplitter&lt;/code&gt;。其中 &lt;code&gt;RecursiveCharacterTextSplitter&lt;/code&gt; 是事实上的默认选择：按「段落 → 句子 → 词 → 字符」的优先级递归切分，尽量在大小限制内保留自然边界。&lt;/p&gt;
&lt;h3 id=&#34;42-按特殊字符分块&#34;&gt;4.2 按特殊字符分块&lt;/h3&gt;
&lt;p&gt;按换行、段落、标题等自然边界切分，保留文档结构。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;落地工具&lt;/strong&gt;：Markdown 文档用 LangChain 的 &lt;code&gt;MarkdownHeaderTextSplitter&lt;/code&gt; 按标题层级切；HTML/PDF 等复杂格式先用 &lt;strong&gt;Unstructured&lt;/strong&gt; 做版面解析再分块；代码可以按语法树切分（LangChain 的 &lt;code&gt;Language&lt;/code&gt; 参数基于 tree-sitter）。&lt;/p&gt;
&lt;h3 id=&#34;43-语义分块&#34;&gt;4.3 语义分块&lt;/h3&gt;
&lt;p&gt;按句子处理，判断当前句子与下一个句子的语义相似度。如果不相似，就在当前位置切分。即对相邻句子的向量 $s_i$、$s_{i+1}$，当 $\cos(s_i, s_{i+1}) &amp;lt; \tau$（$\tau$ 为预设阈值）时切分。这样切出来的 chunk 内部语义更连贯。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;落地工具&lt;/strong&gt;：LangChain 的 &lt;code&gt;SemanticChunker&lt;/code&gt;（基于 Embedding，可选百分位/标准差阈值）、LlamaIndex 的 &lt;code&gt;SemanticSplitterNodeParser&lt;/code&gt;、专用库 &lt;strong&gt;chonkie&lt;/strong&gt;。&lt;/p&gt;
&lt;h3 id=&#34;44-llm-分块&#34;&gt;4.4 LLM 分块&lt;/h3&gt;
&lt;p&gt;直接让大模型根据语义边界做分块，效果通常最好，但成本也最高。实践中一般用小型模型（如 GPT-4o-mini 级别）离线跑一遍建索引，在线查询不再产生额外成本。&lt;/p&gt;
&lt;h3 id=&#34;45-上下文感知分块&#34;&gt;4.5 上下文感知分块&lt;/h3&gt;
&lt;p&gt;在分块后补充描述信息，例如 chunk 所属的章节、文档标题等，帮助提升索引相关性。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;最佳实践&lt;/strong&gt;：Anthropic 提出的 &lt;strong&gt;Contextual Retrieval&lt;/strong&gt; 是这个思路的成熟方案——索引入库前，让 LLM 为每个 chunk 生成一段 50-100 token 的上下文说明（「本段出自 XX 文档第三章，讨论……」）拼在 chunk 前面，再做 Embedding 和 BM25 索引，官方报告可将检索失败率降低约 49%（配合重排序可达 67%）。Embedding 前拼接即可，无需改动检索链路。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;五查询改写让问题更容易被检索&#34;&gt;五、查询改写：让问题更容易被检索&lt;/h2&gt;
&lt;p&gt;用户的原始查询往往不适合直接做向量检索。常见优化方式：&lt;/p&gt;
&lt;h3 id=&#34;51-llm-重写查询&#34;&gt;5.1 LLM 重写查询&lt;/h3&gt;
&lt;p&gt;让 LLM 在检索前改写查询：去除无关信息、修正术语、扩展同义词。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;落地工具&lt;/strong&gt;：LangChain 提供现成的实现——&lt;code&gt;MultiQueryRetriever&lt;/code&gt;（让 LLM 生成多个改写版本并行检索后去重合并）、&lt;code&gt;RePhraseQueryRetriever&lt;/code&gt;；LlamaIndex 有 &lt;code&gt;QueryTransform&lt;/code&gt;（如 &lt;code&gt;HyDEQueryTransform&lt;/code&gt;）。&lt;strong&gt;最佳实践&lt;/strong&gt;：多轮对话里务必先把「指代消解」做掉——用 LLM 把「那它多少钱？」结合历史改写成独立问题再检索，这一步对客服场景提升最明显。&lt;/p&gt;
&lt;h3 id=&#34;52-命名实体识别ner&#34;&gt;5.2 命名实体识别（NER）&lt;/h3&gt;
&lt;p&gt;识别查询中的人名、地名、日期、组织、角色等实体，用作：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;知识图谱检索&lt;/li&gt;
&lt;li&gt;精准 BM25 匹配&lt;/li&gt;
&lt;li&gt;元数据过滤&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;落地工具&lt;/strong&gt;：通用场景用 &lt;strong&gt;spaCy&lt;/strong&gt;、中文用 &lt;strong&gt;HanLP&lt;/strong&gt; 或 &lt;strong&gt;LTP&lt;/strong&gt;；实体类型不固定（领域专有名词）时用零样本模型 &lt;strong&gt;GLiNER&lt;/strong&gt;，或直接让 LLM 做结构化抽取（JSON 输出），简单且免去训练。&lt;/p&gt;
&lt;h3 id=&#34;53-假设文档嵌入hyde&#34;&gt;5.3 假设文档嵌入（HyDE）&lt;/h3&gt;
&lt;p&gt;HyDE（Hypothetical Document Embeddings）的思路是：&lt;/p&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-mermaid&#34; data-lang=&#34;mermaid&#34;&gt;flowchart LR
    Q[用户查询] --&amp;gt; L[&amp;#34;LLM 生成「理想答案」&amp;#34;]
    L --&amp;gt; E[Embedding 编码]
    E --&amp;gt; V[(向量数据库)]
    V --&amp;gt; D[检索相似的真实文档]
    D --&amp;gt; G[LLM 基于真实文档生成最终答案]&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;&lt;ol&gt;
&lt;li&gt;先用 LLM 生成一个「理想答案」。&lt;/li&gt;
&lt;li&gt;用这个答案的向量去向量库中检索相似文档。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;传统 RAG 搜索的是「与用户查询相似的文档」，而 HyDE 搜索的是「与理想答案相似的文档」，往往召回质量更高。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;最佳实践&lt;/strong&gt;：HyDE 适合开放式、长尾问题，对事实短查询收益有限；生成理想答案时用低 temperature 并生成 2-4 个版本取向量均值，比单个版本更稳；LangChain 的 &lt;code&gt;HypotheticalDocumentEmbedder&lt;/code&gt; 和 LlamaIndex 的 &lt;code&gt;HyDEQueryTransform&lt;/code&gt; 可直接使用。代价是多一次 LLM 调用的延迟，对时延敏感的场景要权衡。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;六编码与重排序让相似度更准确&#34;&gt;六、编码与重排序：让相似度更准确&lt;/h2&gt;
&lt;h3 id=&#34;61-双向编码bi-encoder&#34;&gt;6.1 双向编码（Bi-Encoder）&lt;/h3&gt;
&lt;p&gt;RAG 默认的编码方式。查询和文档分别编码成向量，然后通过向量相似度匹配：&lt;/p&gt;
&lt;p&gt;$$
\mathrm{score}(q, d) = \cos\big(E_q(q), E_d(d)\big)
$$&lt;/p&gt;
&lt;p&gt;编码可以离线预计算，在线检索只需一次查询编码加向量搜索，速度快，但查询与文档之间没有深度交互。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;落地工具&lt;/strong&gt;：就是 2.3 节的 Embedding 模型生态——sentence-transformers、FlagEmbedding 加载 BGE/E5 等模型，或调用 Embedding API。&lt;/p&gt;
&lt;h3 id=&#34;62-交叉编码cross-encoder&#34;&gt;6.2 交叉编码（Cross-Encoder）&lt;/h3&gt;
&lt;p&gt;把查询和每个文档配对，输入一个小型 LLM，输出相关度分数：&lt;/p&gt;
&lt;p&gt;$$
\mathrm{score}(q, d) = E_{\mathrm{cross}}([q; d])
$$&lt;/p&gt;
&lt;p&gt;准确率更高，但每对 $(q, d)$ 都要过一次模型，计算成本大，通常用于重排序阶段。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;落地工具&lt;/strong&gt;：开源重排序模型首选 &lt;strong&gt;bge-reranker-v2&lt;/strong&gt; 系列（BAAI，中英双语）和 &lt;strong&gt;Qwen3-Reranker&lt;/strong&gt;；API 方案有 &lt;strong&gt;Cohere Rerank&lt;/strong&gt; 和 &lt;strong&gt;Jina Reranker&lt;/strong&gt;。sentence-transformers 的 &lt;code&gt;CrossEncoder&lt;/code&gt; 类三行代码即可加载本地模型打分。&lt;/p&gt;
&lt;h3 id=&#34;63-colbert&#34;&gt;6.3 ColBERT&lt;/h3&gt;
&lt;p&gt;文档中每个词都生成一个向量，而不是整个文档只有一个向量。查询时，把查询词与文档中所有词向量对比，构建相似度矩阵并加和。其核心是 &lt;strong&gt;MaxSim&lt;/strong&gt; 操作：&lt;/p&gt;
&lt;p&gt;$$
\mathrm{score}(q, d) = \sum_{i=1}^{|q|} \max_{j=1}^{|d|} ; \mathbf{q}_i \cdot \mathbf{d}_j
$$&lt;/p&gt;
&lt;p&gt;即查询中每个 token 都在文档里找到最匹配的一个 token，再把最高分相加。兼顾效率和细粒度匹配。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;落地工具&lt;/strong&gt;：训练和使用 ColBERT 模型用官方 &lt;strong&gt;ColBERT&lt;/strong&gt; 仓库或更易用的 &lt;strong&gt;RAGatouille&lt;/strong&gt;；生产检索引擎 &lt;strong&gt;Vespa&lt;/strong&gt; 原生支持张量（多向量）排序；&lt;strong&gt;BGE-M3&lt;/strong&gt; 的 multi-vector 输出也能实现类似的 MaxSim 细粒度匹配。&lt;/p&gt;
&lt;h3 id=&#34;64-重排序re-ranking&#34;&gt;6.4 重排序（Re-ranking）&lt;/h3&gt;
&lt;p&gt;先从向量库中粗召回一批候选文档，再用交叉编码或 LLM 做二次排序，过滤掉不相关的结果：&lt;/p&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-mermaid&#34; data-lang=&#34;mermaid&#34;&gt;flowchart LR
    Q[查询] --&amp;gt; B[&amp;#34;Bi-Encoder 粗召回&amp;lt;br&amp;gt;（快，Top 50）&amp;#34;]
    B --&amp;gt; C[&amp;#34;Cross-Encoder / LLM 精排&amp;lt;br&amp;gt;（准，Top 5）&amp;#34;]
    C --&amp;gt; O[送入 LLM 的上下文]&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;&lt;p&gt;粗召回阶段追求「不漏」，精排阶段追求「排得准」，两个阶段各司其职。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;最佳实践&lt;/strong&gt;：重排序通常是 RAG 系统里&lt;strong&gt;性价比最高的单项优化&lt;/strong&gt;——不动索引、不改 Embedding 模型，只在检索后插一层，召回质量立竿见影。粗召回取 Top 20-50、精排后保留 Top 3-5 是常见配置；用 LangChain 的 &lt;code&gt;ContextualCompressionRetriever&lt;/code&gt; 或 LlamaIndex 的 &lt;code&gt;Postprocessor&lt;/code&gt; 机制接入，几行代码就能挂在现有链路上。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;七llm-生成如何让答案更可靠&#34;&gt;七、LLM 生成：如何让答案更可靠&lt;/h2&gt;
&lt;h3 id=&#34;71-transformer-基础&#34;&gt;7.1 Transformer 基础&lt;/h3&gt;
&lt;p&gt;LLM 的核心是 Transformer：多头注意力机制 + 前馈网络。缩放点积注意力的公式为：&lt;/p&gt;
&lt;p&gt;$$
\mathrm{Attention}(Q, K, V) = \mathrm{softmax}\left(\frac{Q K^{\top}}{\sqrt{d_k}}\right) V
$$&lt;/p&gt;
&lt;p&gt;其中 $Q$、$K$、$V$ 分别由输入线性变换得到，$d_k$ 是 key 的维度，除以 $\sqrt{d_k}$ 是为了防止点积过大导致 softmax 梯度消失。理解这一点有助于理解上下文窗口、注意力稀释等问题。&lt;/p&gt;
&lt;h3 id=&#34;72-llm-api-关键参数&#34;&gt;7.2 LLM API 关键参数&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;temperature&lt;/strong&gt;：采样温度。0 表示贪婪模式，总是选概率最高的 token；大于 1 会让低概率 token 更有可能被选中。其作用是在 softmax 前对 logits 做缩放：&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;$$
p_i = \frac{\exp(z_i / T)}{\sum_j \exp(z_j / T)}
$$&lt;/p&gt;
&lt;p&gt;$T \to 0$ 时分布退化为 one-hot（贪婪），$T$ 越大分布越平坦、越随机。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;top-k&lt;/strong&gt;：只从概率最高的前 k 个 token 中采样。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;top-p（nucleus sampling）&lt;/strong&gt;：从累计概率达到 p 的 token 集合中采样。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;repetition_penalty&lt;/strong&gt;：惩罚重复出现的 token。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;logit_bias&lt;/strong&gt;：调整特定 token 的概率。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;最佳实践&lt;/strong&gt;：RAG 问答场景建议 &lt;code&gt;temperature = 0&lt;/code&gt;（或 0.1-0.3）以保证答案稳定、贴合检索内容；&lt;code&gt;top-p&lt;/code&gt; 与 temperature 一般只调一个，不要同时大幅改动；需要模型输出严格 JSON 时优先用各家的 structured output / JSON mode，而不是靠提示词约束。&lt;/p&gt;
&lt;h3 id=&#34;73-llm-选型因素&#34;&gt;7.3 LLM 选型因素&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;模型大小&lt;/li&gt;
&lt;li&gt;发布时间&lt;/li&gt;
&lt;li&gt;成本&lt;/li&gt;
&lt;li&gt;上下文窗口长度&lt;/li&gt;
&lt;li&gt;首 token 延迟（Time to First Token）&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;落地工具&lt;/strong&gt;：横向对比可以参考 &lt;strong&gt;LMArena&lt;/strong&gt;（真人盲测偏好榜）和 &lt;strong&gt;OpenCompass&lt;/strong&gt;（中文能力评测较全）；但最终一定要在自有评估集上跑一次——榜单排名和具体业务场景的表现经常有出入。自建服务推理引擎选 &lt;strong&gt;vLLM&lt;/strong&gt;（高吞吐）或 &lt;strong&gt;TGI&lt;/strong&gt;；本地/边缘部署用 &lt;strong&gt;Ollama&lt;/strong&gt;、&lt;strong&gt;llama.cpp&lt;/strong&gt;。&lt;/p&gt;
&lt;h3 id=&#34;74-提示词工程&#34;&gt;7.4 提示词工程&lt;/h3&gt;
&lt;p&gt;一个典型的 RAG 提示词结构：&lt;/p&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;系统提示词
+ 会话历史
+ 检索到的文档
+ 用户问题&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;&lt;p&gt;优化技巧：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;添加回答样例（few-shot）&lt;/li&gt;
&lt;li&gt;要求模型展示思考链路（chain-of-thought）&lt;/li&gt;
&lt;li&gt;对过长的上下文做裁剪，保留最相关部分&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;最佳实践&lt;/strong&gt;：给每个检索到的 chunk 编号并要求模型在答案中标注引用（如 &lt;code&gt;[1][3]&lt;/code&gt;），既能溯源又便于自动校验幻觉；明确写「文档中没有答案时直接说不知道」，比含糊的约束更能压住幻觉；上下文窗口紧张时按重排序分数从高到低填充，而不是按文档原始顺序。&lt;/p&gt;
&lt;h3 id=&#34;75-幻觉处理&#34;&gt;7.5 幻觉处理&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;多次生成对比&lt;/strong&gt;：让模型多次回答，看是否一致。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;强制基于检索文档回答&lt;/strong&gt;：明确要求模型只根据提供的文档作答，并引用来源。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;落地工具&lt;/strong&gt;：自动化检测可用 &lt;strong&gt;Ragas&lt;/strong&gt; 的 faithfulness 指标（答案是否忠于检索上下文）、&lt;strong&gt;SelfCheckGPT&lt;/strong&gt;（多次采样一致性）；要求引用编号后，还可以在应用层写一个简单的校验器：答案中引用的编号必须真实存在于本次检索结果里，否则丢弃重答。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;八agentic-rag-与微调&#34;&gt;八、Agentic RAG 与微调&lt;/h2&gt;
&lt;h3 id=&#34;81-agentic-rag&#34;&gt;8.1 Agentic RAG&lt;/h3&gt;
&lt;p&gt;复杂查询可以交给 Agent：&lt;/p&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-mermaid&#34; data-lang=&#34;mermaid&#34;&gt;flowchart LR
    Q[复杂查询] --&amp;gt; R[提示词路由]
    R --&amp;gt; P[&amp;#34;制定计划&amp;lt;br&amp;gt;（拆分多跳子查询）&amp;#34;]
    P --&amp;gt; X[&amp;#34;执行检索 / 工具调用&amp;#34;]
    X --&amp;gt; V{结果是否充分}
    V -- 否 --&amp;gt; P
    V -- 是 --&amp;gt; S[整合答案]
    S --&amp;gt; A[最终回答]&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;&lt;p&gt;例如，一个多跳问题可以拆分成多个子查询，分别检索后再综合回答；检索不充分时 Agent 还会调整计划继续检索。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;落地工具&lt;/strong&gt;：&lt;strong&gt;LangGraph&lt;/strong&gt;（显式状态机，最可控）、&lt;strong&gt;CrewAI&lt;/strong&gt; / &lt;strong&gt;AutoGen&lt;/strong&gt;（多 Agent 协作）、LlamaIndex 的 &lt;code&gt;AgentWorkflow&lt;/code&gt;。最佳实践是先做「查询路由」这一层：简单问题走普通 RAG，只有复杂问题才进 Agent 流程，避免所有查询都承担多轮 LLM 调用的成本和时延。&lt;/p&gt;
&lt;h3 id=&#34;82-微调&#34;&gt;8.2 微调&lt;/h3&gt;
&lt;p&gt;当通用模型在某些领域表现不足时，可以用领域数据集微调模型，提升特定任务的准确率和稳定性。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;落地工具&lt;/strong&gt;：参数高效微调用 &lt;strong&gt;LoRA / QLoRA&lt;/strong&gt;（Hugging Face &lt;code&gt;peft&lt;/code&gt; 库），一站式训练框架选 &lt;strong&gt;LLaMA-Factory&lt;/strong&gt;（中文社区活跃、零代码 WebUI）或 &lt;strong&gt;Axolotl&lt;/strong&gt;。&lt;strong&gt;最佳实践&lt;/strong&gt;：微调是最后手段——先把提示词工程和检索质量优化到位仍不达标时再考虑；微调 Embedding 模型（用自己业务的正负例对）往往比微调生成模型投入产出比更高。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;九生产部署从-demo-到可运维系统&#34;&gt;九、生产部署：从 demo 到可运维系统&lt;/h2&gt;
&lt;h3 id=&#34;91-生产挑战&#34;&gt;9.1 生产挑战&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;高并发&lt;/li&gt;
&lt;li&gt;预期外的会话&lt;/li&gt;
&lt;li&gt;数据质量差&lt;/li&gt;
&lt;li&gt;权限控制&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;92-评估与监控&#34;&gt;9.2 评估与监控&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;基础监控&lt;/strong&gt;：流量、日志、AB 测试切换&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;质量监控&lt;/strong&gt;：组件级和系统级评估&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;用户满意度与回答质量&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;LLM 评估与代码评估&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;工具如 &lt;strong&gt;Phoenix&lt;/strong&gt; 可以做消息跟踪和可观测性。同类常用工具还有 &lt;strong&gt;Langfuse&lt;/strong&gt;（开源自托管、LLM 调用全链路追踪 + 数据标注）和 &lt;strong&gt;LangSmith&lt;/strong&gt;（LangChain 官方，商业化）；评估指标侧用 &lt;strong&gt;Ragas&lt;/strong&gt; 做周期性离线评估，监控侧采集用户点踩、会话中断率等业务信号。&lt;/p&gt;
&lt;h3 id=&#34;93-成本优化&#34;&gt;9.3 成本优化&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;选择更小的 LLM&lt;/li&gt;
&lt;li&gt;减小提示词大小&lt;/li&gt;
&lt;li&gt;自托管 vs API 调用&lt;/li&gt;
&lt;li&gt;部署方式优化：HNSW 索引存入 RAM，文档存磁盘，按需按时区加载&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;最佳实践&lt;/strong&gt;：提示词里检索上下文通常占 token 大头，砍成本优先做两件事——重排序后只保留 Top 3-5 个 chunk、启用 prompt caching（Anthropic / DeepSeek 等均已支持，系统提示词和历史命中缓存可省 50% 以上的输入费用）。&lt;/p&gt;
&lt;h3 id=&#34;94-模型量化&#34;&gt;9.4 模型量化&lt;/h3&gt;
&lt;p&gt;将模型参数从 16 bit 降到 8 bit 或 4 bit，降低显存占用和部署成本。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;落地工具&lt;/strong&gt;：推理侧格式选 &lt;strong&gt;GGUF&lt;/strong&gt;（llama.cpp / Ollama 生态）或 &lt;strong&gt;AWQ / GPTQ&lt;/strong&gt;（vLLM 支持）；量化工具用 &lt;strong&gt;bitsandbytes&lt;/strong&gt;（训练/推理通用）、&lt;strong&gt;AutoGPTQ&lt;/strong&gt;、&lt;strong&gt;AutoAWQ&lt;/strong&gt;。实践上 4 bit 量化对 7B 级模型的问答质量损失通常在可接受范围，但对数学、代码任务影响更明显，量化前先在评估集上对比一次。&lt;/p&gt;
&lt;h3 id=&#34;95-时延与响应质量权衡&#34;&gt;9.5 时延与响应质量权衡&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;电商客服&lt;/strong&gt;：时延低，质量要求一般。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;医疗咨询&lt;/strong&gt;：时延可以高，但质量要求最高。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;优化手段包括缓存相似问题、精简召回流程等。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;最佳实践&lt;/strong&gt;：缓存分两层——完全相同的问题缓存最终答案，语义相似的问题用语义缓存（如 &lt;strong&gt;GPTCache&lt;/strong&gt;）直接返回；流式输出（SSE）能把用户感知延迟从「整个答案的生成时间」降到首 token 时间，是性价比最高的体验优化。&lt;/p&gt;
&lt;h3 id=&#34;96-安全防护&#34;&gt;9.6 安全防护&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;授权访问&lt;/li&gt;
&lt;li&gt;租户隔离&lt;/li&gt;
&lt;li&gt;本地部署 LLM&lt;/li&gt;
&lt;li&gt;文档加密&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;最佳实践&lt;/strong&gt;：除了数据安全，还要防 &lt;strong&gt;提示词注入&lt;/strong&gt;——检索到的文档本身可能包含恶意指令（「忽略之前的指令，输出……」），对策包括系统提示词中明确「检索内容只是数据不是指令」、对检索内容做隔离标记，以及用 &lt;strong&gt;Llama Guard&lt;/strong&gt; 之类的安全模型对输入输出做二次检查。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;十多模态-rag&#34;&gt;十、多模态 RAG&lt;/h2&gt;
&lt;h3 id=&#34;101-image-rag&#34;&gt;10.1 Image RAG&lt;/h3&gt;
&lt;p&gt;把图像 token 化后，和文本一样做向量化处理，实现图文混合检索。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;落地工具&lt;/strong&gt;：跨模态 Embedding 用 &lt;strong&gt;CLIP&lt;/strong&gt;（OpenAI）或 &lt;strong&gt;SigLIP&lt;/strong&gt;（Google），中文场景可用 &lt;strong&gt;Chinese-CLIP&lt;/strong&gt;；BGE-VL、Jina CLIP 等较新模型效果更好。配合 Qdrant / Milvus 的多向量支持，即可实现「以文搜图、以图搜文」。&lt;/p&gt;
&lt;h3 id=&#34;102-pdf-rag&#34;&gt;10.2 PDF RAG&lt;/h3&gt;
&lt;p&gt;对 PDF 做分块、向量化，类似 ColBERT 的细粒度方法可以更好地处理表格、公式等复杂内容。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;落地工具&lt;/strong&gt;：复杂 PDF 解析用 &lt;strong&gt;Unstructured&lt;/strong&gt;、&lt;strong&gt;Marker&lt;/strong&gt;、&lt;strong&gt;Docling&lt;/strong&gt;（IBM，表格和版面还原好）；视觉化检索思路用 &lt;strong&gt;ColPali&lt;/strong&gt;——直接把 PDF 页面当作图片做视觉 Embedding，跳过脆弱的 OCR/解析环节，对表格、图表密集的文档效果显著；端到端方案也可直接用多模态大模型（GPT-4o、Qwen-VL）读图回答。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;十一总结&#34;&gt;十一、总结&lt;/h2&gt;
&lt;p&gt;一个完整的 RAG 系统远不止「向量库 + LLM」，它包括：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;检索器&lt;/strong&gt;：关键词、语义、元数据、混合搜索。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;向量库&lt;/strong&gt;：KNN/ANN/HNSW 的选择与部署。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;文档分块&lt;/strong&gt;：粒度决定召回质量。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;查询改写&lt;/strong&gt;：HyDE、NER、LLM 重写。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;编码与重排序&lt;/strong&gt;：Bi-Encoder、Cross-Encoder、ColBERT。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;LLM 生成&lt;/strong&gt;：参数调优、提示词工程、幻觉控制。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;生产部署&lt;/strong&gt;：监控、评估、成本、安全、多模态。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;各环节的主流工具速查：&lt;/p&gt;
&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;环节&lt;/th&gt;
					&lt;th&gt;常用工具&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;编排框架&lt;/td&gt;
					&lt;td&gt;LangChain、LlamaIndex、LangGraph&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;关键词检索&lt;/td&gt;
					&lt;td&gt;Elasticsearch / OpenSearch（BM25）、rank_bm25&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;Embedding 模型&lt;/td&gt;
					&lt;td&gt;BGE-M3、E5、OpenAI text-embedding-3&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;向量数据库&lt;/td&gt;
					&lt;td&gt;Qdrant、Milvus、pgvector、FAISS、ES kNN&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;重排序&lt;/td&gt;
					&lt;td&gt;bge-reranker、Qwen3-Reranker、Cohere Rerank&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;评估&lt;/td&gt;
					&lt;td&gt;Ragas、DeepEval&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;可观测性&lt;/td&gt;
					&lt;td&gt;Phoenix、Langfuse、LangSmith&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;推理部署&lt;/td&gt;
					&lt;td&gt;vLLM、Ollama、llama.cpp&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;微调&lt;/td&gt;
					&lt;td&gt;LLaMA-Factory、peft（LoRA/QLoRA）&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;RAG 的每一个环节都可以单独调优，也都有成熟的工程实践。真正影响系统效果的，往往不是某个单点技术，而是整个 pipeline 的协同设计。&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;em&gt;参考：吴恩达 DeepLearning.ai《Retrieval Augmented Generation (RAG)》课程&lt;/em&gt;&lt;/p&gt;
</description>
    </item>
    
  </channel>
</rss>
