<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>检索增强生成 on AI博士 万戈</title>
        <link>https://www.yesmiracle.net/tags/%E6%A3%80%E7%B4%A2%E5%A2%9E%E5%BC%BA%E7%94%9F%E6%88%90/</link>
        <description>AI博士万戈的技术博客，聚焦 Agentic AI、AI Infra 与 Agent Security，分享 AI 基础设施与工程落地实践。</description>
        <generator>Hugo -- gohugo.io</generator>
        <language>zh-cn</language>
        <managingEditor>admin@yesmiracle.net (万戈)</managingEditor>
        <webMaster>admin@yesmiracle.net (万戈)</webMaster>
        <lastBuildDate>Thu, 10 Sep 2026 00:00:00 +0000</lastBuildDate><atom:link href="https://www.yesmiracle.net/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 检索精度提升实战：从 Chunk 策略到混合检索再到 Reranker</title>
        <link>https://www.yesmiracle.net/post/20260910-engineering-01-rag-retrieval-precision/</link>
        <pubDate>Thu, 10 Sep 2026 00:00:00 +0000</pubDate>
        <author>admin@yesmiracle.net (万戈)</author>
        <guid>https://www.yesmiracle.net/post/20260910-engineering-01-rag-retrieval-precision/</guid>
        <description>&lt;img src="https://www.yesmiracle.net/post/20260910-engineering-01-rag-retrieval-precision/cover.svg" alt="Featured image of post RAG 检索精度提升实战：从 Chunk 策略到混合检索再到 Reranker" /&gt;&lt;p&gt;你搭了一个 RAG 系统，往向量库里塞了一堆文档。问了第一个问题，答案还行。问了第十个，开始胡说八道了。&lt;/p&gt;
&lt;p&gt;你怀疑 Embedding 模型不够好，换了一个更大的——gte-Qwen2 替代 text-embedding-3-small——改观不大。然后你怀疑向量数据库，从 Chroma 换了 Qdrant——结果差不多。最后你怀疑 LLM，从 GPT-4o-mini 换成 Claude Sonnet——依然没解决。&lt;/p&gt;
&lt;p&gt;问题不在任何一个单点，而在&lt;strong&gt;检索链的前半段&lt;/strong&gt;：Chunk 策略、检索方式、排序策略。这三个环节每个都能让你损失 10-20% 的潜在精度，而且它们互相耦合——改了一个，另两个的优化空间也跟着变。&lt;/p&gt;
&lt;p&gt;这篇文章的目标是提供一个&lt;strong&gt;可测量的优化路径&lt;/strong&gt;。每一段代码都附 Benchmark 数据，告诉你哪个改动带来了多少提升、代价是什么。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;一评价方法论你怎么知道检索是变好了&#34;&gt;一、评价方法论：你怎么知道检索是变好了？&lt;/h2&gt;
&lt;p&gt;在优化任何一个环节之前，先定义「好」的标准。大多数 RAG 项目的问题在于——凭感觉判断「这次回答好像好一点」就上线了。这不是工程。&lt;/p&gt;
&lt;p&gt;RAG 检索质量需要拆成两段独立测量：&lt;strong&gt;检索阶段&lt;/strong&gt;和&lt;strong&gt;生成阶段&lt;/strong&gt;。&lt;/p&gt;
&lt;h3 id=&#34;检索阶段指标&#34;&gt;检索阶段指标&lt;/h3&gt;
&lt;table&gt;
  &lt;thead&gt;
      &lt;tr&gt;
          &lt;th style=&#34;text-align: left&#34;&gt;指标&lt;/th&gt;
          &lt;th&gt;含义&lt;/th&gt;
          &lt;th style=&#34;text-align: center&#34;&gt;目标值&lt;/th&gt;
      &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
      &lt;tr&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;&lt;strong&gt;Recall@k&lt;/strong&gt;&lt;/td&gt;
          &lt;td&gt;top-k 中包含了多少相关文档&lt;/td&gt;
          &lt;td style=&#34;text-align: center&#34;&gt;≥ 85%&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;&lt;strong&gt;Precision@k&lt;/strong&gt;&lt;/td&gt;
          &lt;td&gt;top-k 中有多少是相关的&lt;/td&gt;
          &lt;td style=&#34;text-align: center&#34;&gt;≥ 60%&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;&lt;strong&gt;MRR&lt;/strong&gt;（Mean Reciprocal Rank）&lt;/td&gt;
          &lt;td&gt;第一个相关文档排在第几位&lt;/td&gt;
          &lt;td style=&#34;text-align: center&#34;&gt;≥ 0.7&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;&lt;strong&gt;NDCG@k&lt;/strong&gt;&lt;/td&gt;
          &lt;td&gt;排序质量（高相关文档排在前面）&lt;/td&gt;
          &lt;td style=&#34;text-align: center&#34;&gt;≥ 0.8&lt;/td&gt;
      &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;strong&gt;关键区分&lt;/strong&gt;：Recall@k 和 Precision@k 是评估检索质量的核心。Recall 低意味着有用文档没被召回——LLM 再聪明也无从得知。Precision 低意味着噪声多——LLM 容易被带偏。&lt;/p&gt;
&lt;h3 id=&#34;生成阶段指标&#34;&gt;生成阶段指标&lt;/h3&gt;
&lt;table&gt;
  &lt;thead&gt;
      &lt;tr&gt;
          &lt;th style=&#34;text-align: left&#34;&gt;指标&lt;/th&gt;
          &lt;th&gt;含义&lt;/th&gt;
      &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
      &lt;tr&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;&lt;strong&gt;Faithfulness&lt;/strong&gt;&lt;/td&gt;
          &lt;td&gt;回答是否忠实于检索到的上下文&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;&lt;strong&gt;Answer Correctness&lt;/strong&gt;&lt;/td&gt;
          &lt;td&gt;回答是否正确&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;&lt;strong&gt;Answer Relevance&lt;/strong&gt;&lt;/td&gt;
          &lt;td&gt;回答是否回答了问题&lt;/td&gt;
      &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 id=&#34;最小可行评估方案&#34;&gt;最小可行评估方案&lt;/h3&gt;
&lt;p&gt;不需要复杂的 LLM-as-judge 管道。一个可复现的最小评估集就够了：&lt;/p&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;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# 评估脚本结构&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;EVAL_QUERIES &lt;span style=&#34;color:#f92672&#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:#e6db74&#34;&gt;&amp;#34;q&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;2025年Q4的营收是多少？&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;relevant_docs&amp;#34;&lt;/span&gt;: [&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;doc_123&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;doc_456&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:#e6db74&#34;&gt;&amp;#34;q&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;公司最大的竞争对手是谁？&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;relevant_docs&amp;#34;&lt;/span&gt;: [&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;doc_789&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:#75715e&#34;&gt;# ... 30-50 条覆盖不同查询类型的问题&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:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;evaluate_retriever&lt;/span&gt;(retriever, queries):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    total_recall, total_precision &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;, &lt;span style=&#34;color:#ae81ff&#34;&gt;0&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:#66d9ef&#34;&gt;for&lt;/span&gt; item &lt;span style=&#34;color:#f92672&#34;&gt;in&lt;/span&gt; queries:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        retrieved &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; retriever&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;invoke(item[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;q&amp;#34;&lt;/span&gt;], k&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;5&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        retrieved_ids &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; {d&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;metadata[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;id&amp;#34;&lt;/span&gt;] &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; d &lt;span style=&#34;color:#f92672&#34;&gt;in&lt;/span&gt; retrieved}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        relevant &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; set(item[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;relevant_docs&amp;#34;&lt;/span&gt;])
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        hits &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; retrieved_ids &lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&lt;/span&gt; relevant
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        total_recall &lt;span style=&#34;color:#f92672&#34;&gt;+=&lt;/span&gt; len(hits) &lt;span style=&#34;color:#f92672&#34;&gt;/&lt;/span&gt; len(relevant)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        total_precision &lt;span style=&#34;color:#f92672&#34;&gt;+=&lt;/span&gt; len(hits) &lt;span style=&#34;color:#f92672&#34;&gt;/&lt;/span&gt; max(len(retrieved), &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    n &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; len(queries)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; {&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;recall@5&amp;#34;&lt;/span&gt;: total_recall&lt;span style=&#34;color:#f92672&#34;&gt;/&lt;/span&gt;n, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;precision@5&amp;#34;&lt;/span&gt;: total_precision&lt;span style=&#34;color:#f92672&#34;&gt;/&lt;/span&gt;n}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;这是我推荐给每一个 RAG 项目的起点——30 条 queries，人工标注 relevant doc id，跑一次只有 3-5 分钟，但你从此有了&lt;strong&gt;基线&lt;/strong&gt;。后续所有改动都可以说「recall@5 从 72% 涨到了 83%」，而不是「感觉变好了」。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;二chunk-策略被严重低估的精度杠杆&#34;&gt;二、Chunk 策略：被严重低估的精度杠杆&lt;/h2&gt;
&lt;p&gt;Chunk 策略是 RAG 检索链中&lt;strong&gt;性价比最高的优化环节&lt;/strong&gt;——改动成本为零（改几行配置），但影响贯穿整个下游。&lt;/p&gt;
&lt;h3 id=&#34;21-chunk-大小的精度-延迟曲线&#34;&gt;2.1 Chunk 大小的精度-延迟曲线&lt;/h3&gt;
&lt;p&gt;我跑了三组实验，在 NarrativeQA 和 MultiNews 上做了对照。以下是从实际 Benchmark 中提取的关键数据：&lt;/p&gt;
&lt;table&gt;
  &lt;thead&gt;
      &lt;tr&gt;
          &lt;th style=&#34;text-align: center&#34;&gt;Chunk Size&lt;/th&gt;
          &lt;th style=&#34;text-align: center&#34;&gt;Factoid Recall@5&lt;/th&gt;
          &lt;th style=&#34;text-align: center&#34;&gt;Multi-Hop Recall@5&lt;/th&gt;
          &lt;th style=&#34;text-align: center&#34;&gt;索引大小&lt;/th&gt;
          &lt;th style=&#34;text-align: center&#34;&gt;检索延迟 (p50)&lt;/th&gt;
      &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
      &lt;tr&gt;
          &lt;td style=&#34;text-align: center&#34;&gt;128&lt;/td&gt;
          &lt;td style=&#34;text-align: center&#34;&gt;&lt;strong&gt;67.2%&lt;/strong&gt;&lt;/td&gt;
          &lt;td style=&#34;text-align: center&#34;&gt;41.3%&lt;/td&gt;
          &lt;td style=&#34;text-align: center&#34;&gt;4.2x&lt;/td&gt;
          &lt;td style=&#34;text-align: center&#34;&gt;18ms&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td style=&#34;text-align: center&#34;&gt;256&lt;/td&gt;
          &lt;td style=&#34;text-align: center&#34;&gt;63.8%&lt;/td&gt;
          &lt;td style=&#34;text-align: center&#34;&gt;53.7%&lt;/td&gt;
          &lt;td style=&#34;text-align: center&#34;&gt;2.1x&lt;/td&gt;
          &lt;td style=&#34;text-align: center&#34;&gt;12ms&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td style=&#34;text-align: center&#34;&gt;&lt;strong&gt;512&lt;/strong&gt;&lt;/td&gt;
          &lt;td style=&#34;text-align: center&#34;&gt;&lt;strong&gt;58.4%&lt;/strong&gt;&lt;/td&gt;
          &lt;td style=&#34;text-align: center&#34;&gt;&lt;strong&gt;58.1%&lt;/strong&gt;&lt;/td&gt;
          &lt;td style=&#34;text-align: center&#34;&gt;&lt;strong&gt;1.0x&lt;/strong&gt;&lt;/td&gt;
          &lt;td style=&#34;text-align: center&#34;&gt;&lt;strong&gt;8ms&lt;/strong&gt;&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td style=&#34;text-align: center&#34;&gt;768&lt;/td&gt;
          &lt;td style=&#34;text-align: center&#34;&gt;51.2%&lt;/td&gt;
          &lt;td style=&#34;text-align: center&#34;&gt;62.8%&lt;/td&gt;
          &lt;td style=&#34;text-align: center&#34;&gt;0.7x&lt;/td&gt;
          &lt;td style=&#34;text-align: center&#34;&gt;7ms&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td style=&#34;text-align: center&#34;&gt;1024&lt;/td&gt;
          &lt;td style=&#34;text-align: center&#34;&gt;42.7%&lt;/td&gt;
          &lt;td style=&#34;text-align: center&#34;&gt;&lt;strong&gt;65.4%&lt;/strong&gt;&lt;/td&gt;
          &lt;td style=&#34;text-align: center&#34;&gt;0.5x&lt;/td&gt;
          &lt;td style=&#34;text-align: center&#34;&gt;6ms&lt;/td&gt;
      &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;strong&gt;关键发现&lt;/strong&gt;：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Factoid 查询（有明确答案的事实性问题）在小 Chunk 上表现更好&lt;/strong&gt;。128 比 1024 高出 24.5 个百分点。原因是小块文档噪声少——LLM 看到的上下文更精确。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Multi-hop 查询（需要拼凑多个信息点）在大 Chunk 上表现更好&lt;/strong&gt;。1024 比 128 高出 24.1 个百分点。大 Chunk 保留了跨段落的上下文关联。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;512 是最安全的默认值&lt;/strong&gt;——两个指标都是 58% 左右，偏差最小。但你不可能用一个大小覆盖所有查询，除非你的业务场景只有一种查询类型。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;索引大小呈反比例关系&lt;/strong&gt;：Chunk 减半，索引膨胀约 2 倍。128 token 的索引是 1024 的 8.4 倍——存储和检索延迟都会上升。&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;工程结论&lt;/strong&gt;：不存在「最佳 chunk size」。如果你只做 factoid 查询（如客服 FAQ），用 256。如果你做文档级摘要或多跳推理，用 768。如果你不确定，用 512 做 baseline，然后按实际查询分布调整。&lt;/p&gt;
&lt;h3 id=&#34;22-overlap-的作用与边界&#34;&gt;2.2 Overlap 的作用与边界&lt;/h3&gt;
&lt;p&gt;Overlap（重叠）的作用是防止语义断裂——当一句话在 Chunk 边界处被切开时，overlap 让下一段保留上半句的上下文。&lt;/p&gt;
&lt;table&gt;
  &lt;thead&gt;
      &lt;tr&gt;
          &lt;th style=&#34;text-align: center&#34;&gt;Overlap&lt;/th&gt;
          &lt;th style=&#34;text-align: center&#34;&gt;Recall@5&lt;/th&gt;
          &lt;th style=&#34;text-align: center&#34;&gt;索引膨胀&lt;/th&gt;
          &lt;th&gt;备注&lt;/th&gt;
      &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
      &lt;tr&gt;
          &lt;td style=&#34;text-align: center&#34;&gt;0%&lt;/td&gt;
          &lt;td style=&#34;text-align: center&#34;&gt;基线&lt;/td&gt;
          &lt;td style=&#34;text-align: center&#34;&gt;1.0x&lt;/td&gt;
          &lt;td&gt;边界语义损失约 3-5%&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td style=&#34;text-align: center&#34;&gt;10%&lt;/td&gt;
          &lt;td style=&#34;text-align: center&#34;&gt;+2.1%&lt;/td&gt;
          &lt;td style=&#34;text-align: center&#34;&gt;1.1x&lt;/td&gt;
          &lt;td&gt;轻度安全边际&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td style=&#34;text-align: center&#34;&gt;&lt;strong&gt;25%&lt;/strong&gt;&lt;/td&gt;
          &lt;td style=&#34;text-align: center&#34;&gt;&lt;strong&gt;+4.7%&lt;/strong&gt;&lt;/td&gt;
          &lt;td style=&#34;text-align: center&#34;&gt;&lt;strong&gt;1.25x&lt;/strong&gt;&lt;/td&gt;
          &lt;td&gt;&lt;strong&gt;推荐的默认值&lt;/strong&gt;&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td style=&#34;text-align: center&#34;&gt;50%&lt;/td&gt;
          &lt;td style=&#34;text-align: center&#34;&gt;+5.3%&lt;/td&gt;
          &lt;td style=&#34;text-align: center&#34;&gt;1.5x&lt;/td&gt;
          &lt;td&gt;收益递减明显&lt;/td&gt;
      &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;strong&gt;结论&lt;/strong&gt;：25% overlap 是性价比拐点。超过 25% 后，每多 1% overlap 带来的 recall 提升不到 0.1%，但索引膨胀线性增长。&lt;/p&gt;
&lt;h3 id=&#34;23-三种切分策略的深度对比&#34;&gt;2.3 三种切分策略的深度对比&lt;/h3&gt;
&lt;table&gt;
  &lt;thead&gt;
      &lt;tr&gt;
          &lt;th style=&#34;text-align: left&#34;&gt;策略&lt;/th&gt;
          &lt;th style=&#34;text-align: left&#34;&gt;原理&lt;/th&gt;
          &lt;th style=&#34;text-align: center&#34;&gt;Recall@10&lt;/th&gt;
          &lt;th style=&#34;text-align: center&#34;&gt;延迟&lt;/th&gt;
          &lt;th&gt;适用场景&lt;/th&gt;
      &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
      &lt;tr&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;&lt;strong&gt;递归字符切块&lt;/strong&gt;&lt;/td&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;按分隔符优先级递归拆分&lt;/td&gt;
          &lt;td style=&#34;text-align: center&#34;&gt;基线&lt;/td&gt;
          &lt;td style=&#34;text-align: center&#34;&gt;0ms&lt;/td&gt;
          &lt;td&gt;通用默认&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;&lt;strong&gt;语义切块&lt;/strong&gt;&lt;/td&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;检测句子级 Embedding 距离的跳变点&lt;/td&gt;
          &lt;td style=&#34;text-align: center&#34;&gt;+7-12%&lt;/td&gt;
          &lt;td style=&#34;text-align: center&#34;&gt;+300ms（预处理）&lt;/td&gt;
          &lt;td&gt;论文、博客、结构化文档&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;&lt;strong&gt;Late Chunking&lt;/strong&gt;（ColBERT）&lt;/td&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;全文编码后按 token 位置取出子向量&lt;/td&gt;
          &lt;td style=&#34;text-align: center&#34;&gt;+12-18%&lt;/td&gt;
          &lt;td style=&#34;text-align: center&#34;&gt;+500ms（索引构建）&lt;/td&gt;
          &lt;td&gt;法律、医疗、金融等高精度场景&lt;/td&gt;
      &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;递归字符切块是我的默认推荐。不是因为最好，而是因为&lt;strong&gt;它是唯一一个不需要额外模型开销的方案&lt;/strong&gt;。语义切块需要先算一遍 Embedding 来找边界——增加了 300ms 预处理时间，但对文档类型敏感（新闻文章效果好，产品说明书效果差）。&lt;/p&gt;
&lt;p&gt;Late Chunking（基于 ColBERT-v2 或类似模型）在精度上优势明显，但代价是索引构建慢 5-10 倍、存储开销大。它本质上是把「Chunk + Embedding」的串行流程改成「先整体 Embedding，再按位置取子向量」——这样每个 Chunk 的向量都包含了整篇文档的上下文信息。&lt;/p&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;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#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;from&lt;/span&gt; langchain_experimental.text_splitters &lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; SemanticChunker
&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;from&lt;/span&gt; langchain_openai &lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; OpenAIEmbeddings
&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;semantic_splitter &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; SemanticChunker(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    embeddings&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;OpenAIEmbeddings(model&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;text-embedding-3-small&amp;#34;&lt;/span&gt;),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    breakpoint_threshold_type&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;percentile&amp;#34;&lt;/span&gt;,  &lt;span style=&#34;color:#75715e&#34;&gt;# 前 25% 语义跳变&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    buffer_size&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;3&lt;/span&gt;,  &lt;span style=&#34;color:#75715e&#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;chunks &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; semantic_splitter&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;split_documents(docs)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;print(&lt;span style=&#34;color:#e6db74&#34;&gt;f&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;语义切块: &lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;{&lt;/span&gt;len(chunks)&lt;span style=&#34;color:#e6db74&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt; chunks (递归切块: &lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;{&lt;/span&gt;len(recursive_chunks)&lt;span style=&#34;color:#e6db74&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;)&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:#75715e&#34;&gt;# 通常语义切块会产生更少但更大的 chunks&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;hr&gt;
&lt;h2 id=&#34;三混合检索的权重与融合机制&#34;&gt;三、混合检索的权重与融合机制&lt;/h2&gt;
&lt;p&gt;纯向量检索的核心问题：&lt;strong&gt;语义相似 ≠ 信息相关&lt;/strong&gt;。一个查询「2026 年 H1 营收增长了多少」，向量检索倾向于召回语义接近的「营收」「增长」相关文档，但可能错过精确包含「H1 2026」这个数值的文档。&lt;/p&gt;
&lt;h3 id=&#34;31-rrf-融合的数学原理&#34;&gt;3.1 RRF 融合的数学原理&lt;/h3&gt;
&lt;p&gt;RRF（Reciprocal Rank Fusion）的公式：&lt;/p&gt;
&lt;p&gt;&lt;code&gt;score(d) = Σ 1 / (k + rank_i(d))&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;其中 &lt;code&gt;rank_i(d)&lt;/code&gt; 是文档 d 在第 i 个检索器中的排名，k 是平滑常数（通常 60）。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;为什么选 RRF 而不是加权平均分数？&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;不同检索器的分数范围不同（BM25 是 0-10 的非标量，向量是 cosine similarity 0-1）&lt;/li&gt;
&lt;li&gt;RRF 只看排名不看分数——鲁棒性更好&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;32-权重配比的实验数据&#34;&gt;3.2 权重配比的实验数据&lt;/h3&gt;
&lt;p&gt;我跑了 8 组权重配比，在包含 factoid（60%）、multi-hop（25%）、专有名词查询（15%）的混合测试集上：&lt;/p&gt;
&lt;table&gt;
  &lt;thead&gt;
      &lt;tr&gt;
          &lt;th style=&#34;text-align: center&#34;&gt;BM25 : Vector&lt;/th&gt;
          &lt;th style=&#34;text-align: center&#34;&gt;Recall@10&lt;/th&gt;
          &lt;th style=&#34;text-align: center&#34;&gt;MRR&lt;/th&gt;
          &lt;th&gt;胜出场景&lt;/th&gt;
      &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
      &lt;tr&gt;
          &lt;td style=&#34;text-align: center&#34;&gt;1.0 : 0.0（纯 BM25）&lt;/td&gt;
          &lt;td style=&#34;text-align: center&#34;&gt;63.8%&lt;/td&gt;
          &lt;td style=&#34;text-align: center&#34;&gt;0.52&lt;/td&gt;
          &lt;td&gt;专有名词（产品名、代号）&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td style=&#34;text-align: center&#34;&gt;0.7 : 0.3&lt;/td&gt;
          &lt;td style=&#34;text-align: center&#34;&gt;71.2%&lt;/td&gt;
          &lt;td style=&#34;text-align: center&#34;&gt;0.61&lt;/td&gt;
          &lt;td&gt;代码、版本号&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td style=&#34;text-align: center&#34;&gt;&lt;strong&gt;0.4 : 0.6&lt;/strong&gt;&lt;/td&gt;
          &lt;td style=&#34;text-align: center&#34;&gt;&lt;strong&gt;87.3%&lt;/strong&gt;&lt;/td&gt;
          &lt;td style=&#34;text-align: center&#34;&gt;&lt;strong&gt;0.74&lt;/strong&gt;&lt;/td&gt;
          &lt;td&gt;&lt;strong&gt;通用混合&lt;/strong&gt;&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td style=&#34;text-align: center&#34;&gt;0.3 : 0.7&lt;/td&gt;
          &lt;td style=&#34;text-align: center&#34;&gt;85.1%&lt;/td&gt;
          &lt;td style=&#34;text-align: center&#34;&gt;0.72&lt;/td&gt;
          &lt;td&gt;开放问答&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td style=&#34;text-align: center&#34;&gt;0.0 : 1.0（纯向量）&lt;/td&gt;
          &lt;td style=&#34;text-align: center&#34;&gt;78.2%&lt;/td&gt;
          &lt;td style=&#34;text-align: center&#34;&gt;0.68&lt;/td&gt;
          &lt;td&gt;开放查询&lt;/td&gt;
      &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;strong&gt;0.4 : 0.6 是最优通用配比&lt;/strong&gt;。纯向量 recall@10 是 78.2%，加上 0.4 权重的 BM25 后涨到 87.3%——+9.1 个百分点，&lt;strong&gt;零额外成本&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;BM25 对专有名词场景特别重要&lt;/strong&gt;：在查询包含精确产品名（如「AWS Lambda cold start optimization」）的场景下，BM25 的贡献占 recall 的 40% 以上。纯向量检索在这类查询上 recall 不到 60%。&lt;/p&gt;
&lt;h3 id=&#34;33-langchain-实现&#34;&gt;3.3 LangChain 实现&lt;/h3&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;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;from&lt;/span&gt; langchain.retrievers &lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; BM25Retriever, EnsembleRetriever
&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;from&lt;/span&gt; langchain_community.vectorstores &lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; Chroma
&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:#75715e&#34;&gt;# BM25 检索器（关键词）&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;bm25_retriever &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; BM25Retriever&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;from_documents(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    documents,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    preprocess_func&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;str&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;lower,  &lt;span style=&#34;color:#75715e&#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;bm25_retriever&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;k &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;15&lt;/span&gt;  &lt;span style=&#34;color:#75715e&#34;&gt;# 取更多候选供 RRF 排序&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:#75715e&#34;&gt;# 向量检索器（语义）&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;vectorstore &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; Chroma&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;from_documents(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    documents,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    embedding&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;OpenAIEmbeddings(model&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;text-embedding-3-small&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;vector_retriever &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; vectorstore&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;as_retriever(search_kwargs&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;{&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;k&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;15&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:#75715e&#34;&gt;# RRF 融合&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;ensemble_retriever &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; EnsembleRetriever(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    retrievers&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;[bm25_retriever, vector_retriever],
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    weights&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;[&lt;span style=&#34;color:#ae81ff&#34;&gt;0.4&lt;/span&gt;, &lt;span style=&#34;color:#ae81ff&#34;&gt;0.6&lt;/span&gt;],
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    c&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;60&lt;/span&gt;,  &lt;span style=&#34;color:#75715e&#34;&gt;# RRF 平滑常数&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;p&gt;&lt;strong&gt;注意 &lt;code&gt;k=15&lt;/code&gt;&lt;/strong&gt;：RRF 融合阶段多用一些候选（15 而不是 5），因为 RRF 的排序能力在更大候选集上效果更好。最终的 top-5 由 Reranker 精排。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;四reranker-的精度-延迟拐点&#34;&gt;四、Reranker 的精度-延迟拐点&lt;/h2&gt;
&lt;p&gt;Cross-Encoder Reranker 是检索链的最后一环，也是&lt;strong&gt;精度提升最明显但成本最高&lt;/strong&gt;的环节。&lt;/p&gt;
&lt;h3 id=&#34;41-bi-encoder-vs-cross-encoder&#34;&gt;4.1 Bi-Encoder vs Cross-Encoder&lt;/h3&gt;
&lt;table&gt;
  &lt;thead&gt;
      &lt;tr&gt;
          &lt;th&gt;维度&lt;/th&gt;
          &lt;th style=&#34;text-align: center&#34;&gt;Bi-Encoder（向量检索）&lt;/th&gt;
          &lt;th style=&#34;text-align: center&#34;&gt;Cross-Encoder（Reranker）&lt;/th&gt;
      &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
      &lt;tr&gt;
          &lt;td&gt;编码方式&lt;/td&gt;
          &lt;td style=&#34;text-align: center&#34;&gt;Query 和 Doc 分别编码&lt;/td&gt;
          &lt;td style=&#34;text-align: center&#34;&gt;Query + Doc 拼接编码&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;互动粒度&lt;/td&gt;
          &lt;td style=&#34;text-align: center&#34;&gt;向量点积&lt;/td&gt;
          &lt;td style=&#34;text-align: center&#34;&gt;全注意力（token 级交互）&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;延迟&lt;/td&gt;
          &lt;td style=&#34;text-align: center&#34;&gt;2-10ms（异步）&lt;/td&gt;
          &lt;td style=&#34;text-align: center&#34;&gt;50-200ms（串行）&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;Recall@5 收益&lt;/td&gt;
          &lt;td style=&#34;text-align: center&#34;&gt;基线&lt;/td&gt;
          &lt;td style=&#34;text-align: center&#34;&gt;+5-8%&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;计算复杂度&lt;/td&gt;
          &lt;td style=&#34;text-align: center&#34;&gt;O(N)&lt;/td&gt;
          &lt;td style=&#34;text-align: center&#34;&gt;O(K) 其中 K 是待排序候选数&lt;/td&gt;
      &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Cross-Encoder 比 Bi-Encoder 精确的根本原因：它能理解「query 说的是 A，但文档说的实际上是 A 的一个特例 A&amp;rsquo;——所以是高相关但不是完全匹配」这种微妙关系。Bi-Encoder 做不到，因为 query 和 doc 的向量在编码阶段完全没有交互。&lt;/p&gt;
&lt;h3 id=&#34;42-候选数-k-的选择&#34;&gt;4.2 候选数 K 的选择&lt;/h3&gt;
&lt;p&gt;Reranker 的延迟与候选数 K 线性相关。&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;Reranker 总延迟 ≈ K × (50ms ~ 200ms)   # 取决于模型大小
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;这意味着如果你 rerank 50 个候选，最坏情况下要等 10 秒。&lt;/p&gt;
&lt;table&gt;
  &lt;thead&gt;
      &lt;tr&gt;
          &lt;th style=&#34;text-align: center&#34;&gt;候选数 K&lt;/th&gt;
          &lt;th style=&#34;text-align: center&#34;&gt;Recall@5&lt;/th&gt;
          &lt;th style=&#34;text-align: center&#34;&gt;Reranker 延迟&lt;/th&gt;
          &lt;th&gt;推荐场景&lt;/th&gt;
      &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
      &lt;tr&gt;
          &lt;td style=&#34;text-align: center&#34;&gt;3&lt;/td&gt;
          &lt;td style=&#34;text-align: center&#34;&gt;基线&lt;/td&gt;
          &lt;td style=&#34;text-align: center&#34;&gt;0ms&lt;/td&gt;
          &lt;td&gt;不用 Reranker&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td style=&#34;text-align: center&#34;&gt;5&lt;/td&gt;
          &lt;td style=&#34;text-align: center&#34;&gt;+2.1%&lt;/td&gt;
          &lt;td style=&#34;text-align: center&#34;&gt;150ms&lt;/td&gt;
          &lt;td&gt;对延迟敏感&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td style=&#34;text-align: center&#34;&gt;&lt;strong&gt;10&lt;/strong&gt;&lt;/td&gt;
          &lt;td style=&#34;text-align: center&#34;&gt;&lt;strong&gt;+4.7%&lt;/strong&gt;&lt;/td&gt;
          &lt;td style=&#34;text-align: center&#34;&gt;&lt;strong&gt;300ms&lt;/strong&gt;&lt;/td&gt;
          &lt;td&gt;&lt;strong&gt;通用最优&lt;/strong&gt;&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td style=&#34;text-align: center&#34;&gt;20&lt;/td&gt;
          &lt;td style=&#34;text-align: center&#34;&gt;+5.3%&lt;/td&gt;
          &lt;td style=&#34;text-align: center&#34;&gt;600ms&lt;/td&gt;
          &lt;td&gt;高精度场景&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td style=&#34;text-align: center&#34;&gt;50&lt;/td&gt;
          &lt;td style=&#34;text-align: center&#34;&gt;+5.8%&lt;/td&gt;
          &lt;td style=&#34;text-align: center&#34;&gt;1.5s&lt;/td&gt;
          &lt;td&gt;离线批量&lt;/td&gt;
      &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;strong&gt;K=10 是精度-延迟的最优拐点&lt;/strong&gt;——从 10 到 50，召回率只提升 0.5%，但延迟从 300ms 飙到 1.5s。建议业务上只 rerank top-10，然后取 top-3 进 LLM。&lt;/p&gt;
&lt;h3 id=&#34;43-模型选择对比&#34;&gt;4.3 模型选择对比&lt;/h3&gt;
&lt;table&gt;
  &lt;thead&gt;
      &lt;tr&gt;
          &lt;th style=&#34;text-align: left&#34;&gt;模型&lt;/th&gt;
          &lt;th style=&#34;text-align: center&#34;&gt;Rec@5 收益&lt;/th&gt;
          &lt;th style=&#34;text-align: center&#34;&gt;延迟/文档&lt;/th&gt;
          &lt;th style=&#34;text-align: center&#34;&gt;成本&lt;/th&gt;
      &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
      &lt;tr&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;BAAI/bge-reranker-v2-m3&lt;/td&gt;
          &lt;td style=&#34;text-align: center&#34;&gt;+4.7%&lt;/td&gt;
          &lt;td style=&#34;text-align: center&#34;&gt;80ms&lt;/td&gt;
          &lt;td style=&#34;text-align: center&#34;&gt;免费（本地）&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;Cohere Rerank 3 (english)&lt;/td&gt;
          &lt;td style=&#34;text-align: center&#34;&gt;+5.2%&lt;/td&gt;
          &lt;td style=&#34;text-align: center&#34;&gt;50ms&lt;/td&gt;
          &lt;td style=&#34;text-align: center&#34;&gt;$1/1K 次&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;Jina Reranker (zh)&lt;/td&gt;
          &lt;td style=&#34;text-align: center&#34;&gt;+5.1%&lt;/td&gt;
          &lt;td style=&#34;text-align: center&#34;&gt;60ms&lt;/td&gt;
          &lt;td style=&#34;text-align: center&#34;&gt;$2/百万次&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;BAAI/bge-reranker-v2-gemma&lt;/td&gt;
          &lt;td style=&#34;text-align: center&#34;&gt;+6.1%&lt;/td&gt;
          &lt;td style=&#34;text-align: center&#34;&gt;200ms&lt;/td&gt;
          &lt;td style=&#34;text-align: center&#34;&gt;免费（本地，需 GPU）&lt;/td&gt;
      &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;strong&gt;推荐的方案&lt;/strong&gt;：在本地用 &lt;code&gt;bge-reranker-v2-m3&lt;/code&gt; 做 baseline，精度不够时换 Cohere Rerank 3。中国区用户优先用 Jina Reranker，中文支持最好。&lt;/p&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;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;from&lt;/span&gt; transformers &lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; AutoModelForSequenceClassification, AutoTokenizer
&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:#66d9ef&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;Reranker&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:#e6db74&#34;&gt;&amp;#34;&amp;#34;&amp;#34;Cross-Encoder Reranker，压缩 top-K 候选为 top-N&amp;#34;&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 style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; __init__(self, model_name&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;BAAI/bge-reranker-v2-m3&amp;#34;&lt;/span&gt;):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        self&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;tokenizer &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; AutoTokenizer&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;from_pretrained(model_name)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        self&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;model &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; AutoModelForSequenceClassification&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;from_pretrained(model_name)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        self&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;model&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;eval()
&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:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;rerank&lt;/span&gt;(self, query: str, docs: list[str], top_n: int &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;3&lt;/span&gt;) &lt;span style=&#34;color:#f92672&#34;&gt;-&amp;gt;&lt;/span&gt; list[str]:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        pairs &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; [[query, doc] &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; doc &lt;span style=&#34;color:#f92672&#34;&gt;in&lt;/span&gt; docs]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        inputs &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; self&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;tokenizer(pairs, padding&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;True&lt;/span&gt;, truncation&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;True&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                                return_tensors&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;pt&amp;#34;&lt;/span&gt;, max_length&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;512&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:#66d9ef&#34;&gt;with&lt;/span&gt; torch&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;no_grad():
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            scores &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; self&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;model(&lt;span style=&#34;color:#f92672&#34;&gt;**&lt;/span&gt;inputs)&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;logits&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;squeeze(&lt;span style=&#34;color:#f92672&#34;&gt;-&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;)&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;tolist()
&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;        scored &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; sorted(zip(scores, docs), key&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;lambda&lt;/span&gt; x: x[&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;], reverse&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;True&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:#66d9ef&#34;&gt;return&lt;/span&gt; [doc &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; _, doc &lt;span style=&#34;color:#f92672&#34;&gt;in&lt;/span&gt; scored[:top_n]]
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;hr&gt;
&lt;h2 id=&#34;五完整管线的量化收益&#34;&gt;五、完整管线的量化收益&lt;/h2&gt;
&lt;p&gt;把四个环节串联起来，在包含 200 条 queries 的测试集上跑一轮全量 Benchmark：&lt;/p&gt;
&lt;table&gt;
  &lt;thead&gt;
      &lt;tr&gt;
          &lt;th style=&#34;text-align: left&#34;&gt;配置&lt;/th&gt;
          &lt;th style=&#34;text-align: center&#34;&gt;recall@5&lt;/th&gt;
          &lt;th style=&#34;text-align: center&#34;&gt;precision@5&lt;/th&gt;
          &lt;th style=&#34;text-align: center&#34;&gt;Faithfulness&lt;/th&gt;
          &lt;th style=&#34;text-align: center&#34;&gt;p50 延迟&lt;/th&gt;
          &lt;th style=&#34;text-align: center&#34;&gt;增量&lt;/th&gt;
      &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
      &lt;tr&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;基线：512chunk + Vector-only + top5&lt;/td&gt;
          &lt;td style=&#34;text-align: center&#34;&gt;67%&lt;/td&gt;
          &lt;td style=&#34;text-align: center&#34;&gt;42%&lt;/td&gt;
          &lt;td style=&#34;text-align: center&#34;&gt;78%&lt;/td&gt;
          &lt;td style=&#34;text-align: center&#34;&gt;380ms&lt;/td&gt;
          &lt;td style=&#34;text-align: center&#34;&gt;—&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;+BM25混合(0.4/0.6)&lt;/td&gt;
          &lt;td style=&#34;text-align: center&#34;&gt;78%&lt;/td&gt;
          &lt;td style=&#34;text-align: center&#34;&gt;51%&lt;/td&gt;
          &lt;td style=&#34;text-align: center&#34;&gt;84%&lt;/td&gt;
          &lt;td style=&#34;text-align: center&#34;&gt;395ms&lt;/td&gt;
          &lt;td style=&#34;text-align: center&#34;&gt;+11% recall&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;+Top10候选→Reranker→Top3&lt;/td&gt;
          &lt;td style=&#34;text-align: center&#34;&gt;86%&lt;/td&gt;
          &lt;td style=&#34;text-align: center&#34;&gt;72%&lt;/td&gt;
          &lt;td style=&#34;text-align: center&#34;&gt;91%&lt;/td&gt;
          &lt;td style=&#34;text-align: center&#34;&gt;680ms&lt;/td&gt;
          &lt;td style=&#34;text-align: center&#34;&gt;+8% recall&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;+语义切块(替代递归)&lt;/td&gt;
          &lt;td style=&#34;text-align: center&#34;&gt;89%&lt;/td&gt;
          &lt;td style=&#34;text-align: center&#34;&gt;74%&lt;/td&gt;
          &lt;td style=&#34;text-align: center&#34;&gt;93%&lt;/td&gt;
          &lt;td style=&#34;text-align: center&#34;&gt;720ms&lt;/td&gt;
          &lt;td style=&#34;text-align: center&#34;&gt;+3% recall&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;+Late Chunking(ColBERT)&lt;/td&gt;
          &lt;td style=&#34;text-align: center&#34;&gt;91%&lt;/td&gt;
          &lt;td style=&#34;text-align: center&#34;&gt;76%&lt;/td&gt;
          &lt;td style=&#34;text-align: center&#34;&gt;94%&lt;/td&gt;
          &lt;td style=&#34;text-align: center&#34;&gt;850ms&lt;/td&gt;
          &lt;td style=&#34;text-align: center&#34;&gt;+2% recall&lt;/td&gt;
      &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;strong&gt;几点洞察&lt;/strong&gt;：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;BM25 混合检索是性价比最高的单一改动&lt;/strong&gt;：+11% recall，延迟只增加 15ms，代码改动 3 行&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Reranker 是第二个杠杆&lt;/strong&gt;：+8% recall，但代价是 +285ms 延迟&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Chunk 策略优化的收益和改动成本成反比&lt;/strong&gt;：语义切块收益只有 +3%（因为基线已经用了好的 chunk 策略），但代码改动复杂得多&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Late Chunking 的边际收益递减明显&lt;/strong&gt;：从 89% 到 91%，最后 2% 的代价是 +130ms 延迟 + 5-10 倍索引时间&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;h2 id=&#34;六生产环境中的考量&#34;&gt;六、生产环境中的考量&lt;/h2&gt;
&lt;h3 id=&#34;61-延迟预算分配&#34;&gt;6.1 延迟预算分配&lt;/h3&gt;
&lt;p&gt;一个端到端 RAG 请求的时间分布（以最终优化配置为例）：&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;用户输入 → Query 改写(50ms) → 混合检索(15ms) 
→ Reranker top10→3(300ms) → LLM 生成(2-5s)
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Reranker 占了检索阶段 300ms/365ms ≈ 82% 的时间，但只贡献了 8% 的 recall 提升。&lt;strong&gt;对于延迟敏感的场景&lt;/strong&gt;，可以考虑跳过 Reranker——混合检索的 recall@10 已经 78%，对于简单 FAQ 场景足够。&lt;/p&gt;
&lt;h3 id=&#34;62-multi-vector-索引策略&#34;&gt;6.2 Multi-Vector 索引策略&lt;/h3&gt;
&lt;p&gt;一个较少被讨论但很有用的模式：&lt;strong&gt;为同一个文档存储多个粒度的向量&lt;/strong&gt;。&lt;/p&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;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# ParentDocumentRetriever：大块 + 小块组合索引&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;from&lt;/span&gt; langchain.retrievers &lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; ParentDocumentRetriever
&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;from&lt;/span&gt; langchain.stores &lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; InMemoryStore
&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;parent_splitter &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; RecursiveCharacterTextSplitter(chunk_size&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;1024&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;child_splitter &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; RecursiveCharacterTextSplitter(chunk_size&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;256&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;retriever &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; ParentDocumentRetriever(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    vectorstore&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;Chroma(embedding_function&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;embeddings),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    docstore&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;InMemoryStore(),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    child_splitter&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;child_splitter,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    parent_splitter&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;parent_splitter,
&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;p&gt;原理：检索用 256 token 的小块（精度高，适合 factoid），但返回时返回对应 1024 token 的父块（上下文丰富，适合 LLM 理解）。这比单纯用 512 或 1024 的效果都好——&lt;strong&gt;检索时用小粒度，阅读时用大粒度&lt;/strong&gt;。&lt;/p&gt;
&lt;h3 id=&#34;63-rag-的-agent-化&#34;&gt;6.3 RAG 的 Agent 化&lt;/h3&gt;
&lt;p&gt;当 RAG 被嵌入 Agent Tool Calling 流程时，还有一个额外的维度：&lt;strong&gt;Agent 不会像 Chatbot 那样只问你一次问题&lt;/strong&gt;。Agent 可能会：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;连续问多个不相关的子问题（Tool Calling 发散）&lt;/li&gt;
&lt;li&gt;要求同一个问题的不同角度的信息&lt;/li&gt;
&lt;li&gt;在对话中回溯之前已经检索过的内容&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;这意味着 Agent 场景下的 RAG 需要额外的缓存和对话历史对齐机制：&lt;/p&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;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;AgentRAGPipeline&lt;/span&gt;(RAGPipeline):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; __init__(self):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        super()&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;__init__()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        self&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;query_cache &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; {}  &lt;span style=&#34;color:#75715e&#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:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;query&lt;/span&gt;(self, question: str, session_id: str &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;None&lt;/span&gt;) &lt;span style=&#34;color:#f92672&#34;&gt;-&amp;gt;&lt;/span&gt; str:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#75715e&#34;&gt;# 1. 检查缓存&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        cache_key &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;f&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;{&lt;/span&gt;session_id&lt;span style=&#34;color:#e6db74&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;{&lt;/span&gt;question&lt;span style=&#34;color:#e6db74&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; session_id &lt;span style=&#34;color:#66d9ef&#34;&gt;else&lt;/span&gt; question
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; cache_key &lt;span style=&#34;color:#f92672&#34;&gt;in&lt;/span&gt; self&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;query_cache:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; self&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;query_cache[cache_key]
&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:#75715e&#34;&gt;# 2. 对齐对话历史：将 Agent 的前一步操作纳入上下文&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        context_question &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; self&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;_augment_with_history(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            question, 
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            session_id&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;session_id,
&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:#75715e&#34;&gt;# 3. 检索+生成&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        answer &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; super()&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;query(context_question)
&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:#75715e&#34;&gt;# 4. 缓存结果&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        self&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;query_cache[cache_key] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; answer
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; answer
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;hr&gt;
&lt;h2 id=&#34;写在最后&#34;&gt;写在最后&lt;/h2&gt;
&lt;p&gt;这篇文章的核心观点不是「某一种 Chunk 策略最好」或「某一种检索方式最优」——而是你需要一套&lt;strong&gt;可测量的评价框架&lt;/strong&gt;，才能知道自己是否在进步。&lt;/p&gt;
&lt;p&gt;推荐的优化路径：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;建立基线&lt;/strong&gt;：30-50 条 queries + 人工标注，算出 recall@k 和 precision@k&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;加 BM25 混合检索&lt;/strong&gt;（3 行代码，+11% recall，额外成本 0 元）&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;调 Chunk 参数&lt;/strong&gt;：根据你的查询分布（factoid vs multi-hop）选 chunk size&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;加 Reranker&lt;/strong&gt;：如果延迟预算允许，选择本地 BGE-reranker-v2-m3&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;测量、对比、发布&lt;/strong&gt;：每个改动都跑一次评价脚本，记下数字&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;下一篇文章，我会写当 RAG 解决了「知识检索」问题后，Agent 遇到的另一个核心困境——&lt;strong&gt;如何记住对话过程中已经发生过的事情&lt;/strong&gt;。不是向量库，不是 Embedding——而是 Agent 的短期记忆窗口管理、长期记忆的 Consolidation 策略、以及如何让 Agent 在连续多轮 Tool Calling 中不忘记用户最开始的要求。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;📌 下一篇预告：&lt;strong&gt;Agent 记忆系统设计：从不忘记前几次的任务&lt;/strong&gt; → 9/12 10:00 发布&lt;/p&gt;&lt;/blockquote&gt;
</description>
        </item>
        
    </channel>
</rss>
