<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>RAG on Hypho - AI Agent 技术博客</title><link>https://blog.hypho.cn/tags/rag/</link><description>Recent content in RAG on Hypho - AI Agent 技术博客</description><image><title>Hypho - AI Agent 技术博客</title><url>https://blog.hypho.cn/papermod-cover.png</url><link>https://blog.hypho.cn/papermod-cover.png</link></image><generator>Hugo -- 0.148.2</generator><language>zh-cn</language><lastBuildDate>Wed, 03 Jun 2026 10:08:05 +0800</lastBuildDate><atom:link href="https://blog.hypho.cn/tags/rag/index.xml" rel="self" type="application/rss+xml"/><item><title>RAG 处理图片的正确姿势：索引时描述，而不是查询时看图</title><link>https://blog.hypho.cn/posts/rag-image-indexing-kapa-ai-multimodal/</link><pubDate>Wed, 03 Jun 2026 10:08:05 +0800</pubDate><guid>https://blog.hypho.cn/posts/rag-image-indexing-kapa-ai-multimodal/</guid><description>技术文档中的截图、架构图和表格是 RAG 系统最容易忽略的高价值内容。kapa.ai 在生产环境中验证了一套务实方案：索引时用视觉模型为图片生成文字描述，查询时走纯文本检索。本文拆解这套方案的工程细节、与 ColPali 等视觉检索方案的对比，以及你在自己的 RAG 管线中应该怎么选。</description><content:encoded><![CDATA[<p>做 RAG 的人大多有个盲区：眼睛只盯着文字。</p>
<p>你花了两周调 chunk size，换了三种 embedding 模型，reranker 也加上了——<a href="https://blog.hypho.cn/posts/rerank-bi-encoder-cross-encoder/">《Rerank 在 RAG 中的角色：Bi-Encoder vs Cross-Encoder》</a>里讲的那些优化你全做了。但回头一看，技术文档里的截图、架构图、参数表格，这些图片承载的信息，你的系统压根没碰。</p>
<p>这不是小问题。kapa.ai 的团队最近在 Hacker News 上分享了他们的生产实践（89 points），标题直白：<strong>How we index images for RAG</strong>。他们服务 200+ 客户，知识库里有数百万张图片，每天处理百万级查询。这篇文章把他们的方案拆开来看，顺便聊聊 ColPali 等另一条路线，以及你到底该选哪条。</p>
<h2 id="图片在技术文档里到底干了什么">图片在技术文档里到底干了什么</h2>
<p>kapa.ai 把文档图片分成两类，这个分类非常实用。</p>
<p><strong>说明型（illustrative）</strong>：文字说&quot;点击设置图标&quot;，旁边的截图标出了图标在哪、长什么样。图片让答案更直观，但信息本身在文字里也有——用户不看图也能做事，只是要多找一会儿。</p>
<p><strong>承重型（load-bearing）</strong>：接线图、参数规格表、认证矩阵、颜色可选表——这些图里的数值<strong>只存在于图片中</strong>。你不看图，就根本不知道答案。</p>
<p>他们跑了上千条真实客户问题验证：有图片上下文时，LLM 评委（McNemar 检验，p &lt; 0.05）显著偏好带图片的答案。效果不是微调能弥补的那种——是用户能感知到的质变。</p>
<p>但问题来了：怎么把这些图片喂给 LLM？</p>
<h2 id="查询时看图一个看起来对实际上贵到用不起的方案">查询时看图：一个看起来对、实际上贵到用不起的方案</h2>
<p>直觉反应是：检索到相关 chunk 后，把它们引用的图片一起丢给多模态模型（GPT-5.1、Claude 4.6 Sonnet 等）。kapa.ai 测试了这个方案，发现三个结构性问题：</p>
<p><strong>1. 贵得离谱。</strong> 原始图片让 GPT 单次查询成本增加 27%，Claude 增加 51%（Claude 把一张图编码成约 975 tokens，GPT 约 716）。kapa.ai 每天百万级查询，这笔钱根本花不起。</p>
<p><strong>2. 塞不进去。</strong> 一个典型问题检索 10-30 个 chunk，平均引用 20-30 张图片，长尾能到 130 张。Claude 的 payload 上限 30 MB，OpenAI 的 50 MB——超过 20 张图就撞墙了。</p>
<p><strong>3. CLIP 式嵌入在技术文档上失效。</strong> CLIP 这类视觉嵌入模型擅长的是&quot;这是一只猫&quot;还是&quot;这是一条狗&quot;，不是&quot;这个表格第三行的耐火等级是多少&quot;。短技术查询（&ldquo;how do I configure X&rdquo;）和图表细节之间，语义鸿沟太大。</p>
<p>kapa.ai 的结论很干脆：这些不是工程细节能调掉的问题，是今天多模态生态的结构性限制。于是他们换了一条路。</p>
<h2 id="索引时描述一次查询时当文字检索">索引时描述一次，查询时当文字检索</h2>
<p>核心思路反过来：<strong>重活在索引时干一次，查询时完全不碰图片。</strong></p>
<p>流程很清晰：</p>
<ol>
<li><strong>索引阶段</strong>：用视觉语言模型（VLM）给每张图片生成一段文字描述（caption）</li>
<li><strong>存储</strong>：caption 作为独立的文本 chunk 存进向量数据库</li>
<li><strong>查询阶段</strong>：检索器照常做文本检索，如果 caption chunk 被命中，就把它放进上下文</li>
</ol>
<p>这样查询时的成本几乎不增加（纯文本检索），延迟也几乎不变。&ldquo;看图&quot;这个昂贵操作只发生一次——在文档摄入的时候。</p>
<p>听起来简单，但生产环境里有三个关键细节决定了成败。</p>
<h2 id="关键细节一大多数图片是垃圾过滤是第一步">关键细节一：大多数图片是垃圾，过滤是第一步</h2>
<p>你不能给知识库里的每张图都生成描述。Logo、头像、社交预览图、装饰性横幅——这些占了大多数。</p>
<p>kapa.ai 用启发式规则做第一轮过滤（丢掉不支持的格式、过小的图片等），然后用分类器做第二轮过滤。在明确的图片上，分类器准确率 96.8%（F1 0.974），但在模糊图片上准确率暴跌到 59.8%。</p>
<p>原因很根本：一张倒计时截图，你分不清它是装饰性横幅还是功能性内容。图片分类在边界情况下就是很难。</p>
<h2 id="关键细节二上下文比模型大小更重要">关键细节二：上下文比模型大小更重要</h2>
<p>给 VLM 喂图片时，<strong>把图片前后的段落一起喂进去</strong>，caption 质量会大幅提升。没有上下文时，一个文件上传对话框的描述是&quot;一个有文件选择按钮的网页&rdquo;；有上下文后，描述变成&quot;项目配置页面中的文件上传对话框，用于导入自定义规则集&quot;。</p>
<p>另一个有意思的发现：<strong>贵的模型几乎不值得。</strong> 他们测试了五个模型，从 Claude 4.6 Sonnet 到 GPT-5.4 nano。小型模型（GPT-5.4 mini）生成的 caption 和四倍价格的大模型几乎无法区分。</p>
<p>这很符合直觉——描述一张截图不需要博士级推理，需要的是看清图片内容并用准确的语言表述。小模型就够了。</p>
<h2 id="关键细节三独立存储优于内联替换">关键细节三：独立存储优于内联替换</h2>
<p>两种存储 caption 的方式：</p>
<ul>
<li><strong>内联（inline）</strong>：替换图片的 alt text，让 caption 和原文混在一起</li>
<li><strong>独立（separate）</strong>：每个 caption 作为单独的 chunk 存储，原文 chunk 保持不变</li>
</ul>
<p>直觉告诉你会内联更好——caption 和上下文紧挨着嘛。但实际测试中，独立存储在成本和图片使用率上都赢了。</p>
<p>原因：内联 caption 会膨胀每个它所在的 chunk，而这些 chunk 每次查询都要发送。独立 chunk 只在相关时才被检索到，不相关的查询根本不会为它付 token 费。</p>
<h2 id="生产环境数据">生产环境数据</h2>
<p>端到端测试（三个客户项目，GPT-5.1 和 Claude 4.6 Sonnet）：</p>
<table>
  <thead>
      <tr>
          <th>指标</th>
          <th>纯文本基线</th>
          <th>加入图片 caption</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>图片被引用比例</td>
          <td>0%</td>
          <td>10%-64%</td>
      </tr>
      <tr>
          <td>答案质量（LLM 评委）</td>
          <td>基线</td>
          <td>显著更好（p &lt; 0.05）</td>
      </tr>
      <tr>
          <td>单次查询成本增加</td>
          <td>—</td>
          <td>1%-6%</td>
      </tr>
      <tr>
          <td>延迟（首 token 时间）</td>
          <td>—</td>
          <td>亚秒级增加</td>
      </tr>
      <tr>
          <td>图片放置正确率</td>
          <td>—</td>
          <td>94%-99%</td>
      </tr>
  </tbody>
</table>
<p>用 1%-6% 的成本换来 10%-64% 的图片引用率提升，且图片放置正确率 94%-99%。这笔账怎么算都划算。</p>
<h2 id="另一条路colpali-的视觉检索方案">另一条路：ColPali 的视觉检索方案</h2>
<p>kapa.ai 的方案是&quot;把图片变成文字再检索&quot;。ColPali（2024，illuin-tech，2652 stars）走了完全不同的路：<strong>直接在视觉空间做检索</strong>。</p>
<p>ColPali 用 PaliGemma-3B 的 ViT 输出作为多向量表示，按 ColBERT 的方式训练。它跳过了 OCR 和版面分析，直接把文档的视觉特征编码成嵌入，查询时在视觉空间里做相似度匹配。</p>
<p>ColPali 的优势在于：</p>
<ul>
<li><strong>不需要 OCR 或版面识别管线</strong>——端到端一个模型搞定</li>
<li><strong>对图表、表格等视觉信息天然友好</strong>——不像 CLIP 那样丢失细节</li>
<li><strong>在 ViDoRe benchmark 上表现优异</strong></li>
</ul>
<p>但 kapa.ai 在生产中对 CLIP 类方法的批评同样适用于 ColPali：</p>
<ul>
<li><strong>短查询 vs 长文档的语义鸿沟</strong>——用户问&quot;how do I configure X&quot;，和一张架构截图之间的匹配，视觉嵌入很难做好</li>
<li><strong>推理成本</strong>——每次查询都要对所有候选文档做视觉编码比对，在百万级查询场景下不现实</li>
<li><strong>工程复杂度</strong>——需要维护专门的视觉检索基础设施</li>
</ul>
<p>说白了，ColPali 更适合<strong>文档问答</strong>场景（比如你有一个 PDF 库，每份文档都要理解图表），而 kapa.ai 的方案更适合<strong>大规模知识库检索</strong>场景（技术文档 RAG，查询量大，成本敏感）。</p>
<h2 id="实操建议你的-rag-管线怎么选">实操建议：你的 RAG 管线怎么选</h2>
<p>根据你的场景，选择不同的路线：</p>
<p><strong>场景 A：技术文档 RAG，查询量大（&gt;1 万次/天）</strong>
→ kapa.ai 方案：索引时用 VLM 生成 caption，独立 chunk 存储，查询走纯文本。成本可控，延迟低，效果有保障。</p>
<p><strong>场景 B：文档问答，查询量小，但文档图表密集</strong>
→ ColPali 方案：直接在视觉空间检索，跳过 OCR 管线。适合研究、法律、金融等需要理解复杂图表的领域。</p>
<p><strong>场景 C：预算有限，只想快速验证</strong>
→ 先用 OCR + 图片 alt text 提取文字，和文本 chunk 一起存。效果不如前两种，但零额外成本。</p>
<p><strong>场景 D：已有成熟文本 RAG，只想补上图片能力</strong>
→ 按 kapa.ai 的方案做增量：给已有文档的图片批量生成 caption，作为新 chunk 加入向量库。不需要改动现有管线。</p>
<h2 id="几个容易踩的坑">几个容易踩的坑</h2>
<p><strong>1. 别用大模型生成 caption。</strong> 前面说了，小模型就够。用 GPT-4o 描述截图是拿大炮打蚊子。</p>
<p><strong>2. 一定要喂上下文。</strong> 没有前后文的图片描述质量差两个档次，这个投入绝对值得。</p>
<p><strong>3. 过滤比生成更重要。</strong> 大多数图片是噪声，不过滤的话 caption chunk 会严重污染检索结果。</p>
<p><strong>4. 独立存储，不要内联。</strong> 这个反直觉，但实测数据很清楚。</p>
<p><strong>5. 别把图片当文本的附属品。</strong> 在 RAG 管线设计时就把图片处理作为一等公民，而不是&quot;后续优化&quot;。</p>
<h2 id="写在最后">写在最后</h2>
<p>RAG 领域的优化已经卷到了 text chunking、embedding、reranking 这些层面，但图片处理一直是个被忽视的角落。kapa.ai 的方案之所以有价值，不是因为技术多新，而是因为它在生产环境里跑通了——百万级查询、200+ 客户、有数据支撑。</p>
<p>如果你在做技术文档类的 RAG，我建议认真考虑这个方案。投入小（1%-6% 成本），回报大（10%-64% 图片引用率），且不需要重构现有管线。</p>
<p>相关阅读：</p>
<ul>
<li><a href="https://blog.hypho.cn/posts/rerank-bi-encoder-cross-encoder/">《Rerank 在 RAG 中的角色：Bi-Encoder vs Cross-Encoder》</a> — 如果你还没加 reranker，先看这篇</li>
<li><a href="https://blog.hypho.cn/posts/stash-open-source-ai-memory-layer/">《Stash：开源 AI 记忆层的工程实践》</a> — RAG 之外的另一种知识管理思路</li>
</ul>
<hr>
<p><strong>信源：</strong></p>
<ul>
<li><a href="https://www.kapa.ai/blog/how-we-index-images-for-rag">kapa.ai: How we index images for RAG</a></li>
<li><a href="https://github.com/illuin-tech/colpali">ColPali: Efficient Document Retrieval with Vision Language Models (GitHub)</a></li>
<li><a href="https://arxiv.org/abs/2407.01449">ColPali Paper (arXiv: 2407.01449)</a></li>
<li><a href="https://github.com/Unstructured-IO/unstructured">Unstructured: Document ETL (GitHub)</a></li>
<li><a href="https://news.ycombinator.com/item?id=48372863">HN Discussion</a></li>
</ul>
]]></content:encoded></item><item><title>向量数据库已经很快了，为什么还要重排？RAG 系统中 Bi-Encoder 与 Cross-Encoder 的工程对决</title><link>https://blog.hypho.cn/posts/rerank-bi-encoder-cross-encoder/</link><pubDate>Thu, 19 Mar 2026 00:00:00 +0000</pubDate><guid>https://blog.hypho.cn/posts/rerank-bi-encoder-cross-encoder/</guid><description>从一次真实的 RAG 系统故障出发，深度解析向量召回与重排模型各自的适用边界，以及生产环境中「先快后准」架构的设计逻辑。</description><content:encoded><![CDATA[<h2 id="一个让工程师失眠的-bad-case">一个让工程师失眠的 Bad Case</h2>
<p>2025 年中，某金融科技公司在内部知识库问答系统中引入 RAG（检索增强生成）。系统上线后，用户反馈普遍不错——直到某天，一个风控团队的用户问：&ldquo;我们有哪些客户曾经有过信用违约记录？&rdquo;</p>
<p>RAG 系统检索返回了三条&quot;高相关&quot;文档，AI 基于这些文档给出了自信满满的答案。风控经理看完后直接冷汗：系统返回的内容全是关于&quot;信用良好客户&quot;的正面案例，和用户的查询意图完全相反。</p>
<p>事后排查发现，问题出在<strong>向量检索阶段</strong>。用户的查询&quot;信用违约记录&quot;和文档中大量出现&quot;信用&quot;&ldquo;违约&quot;字眼的高频正面记录产生了极高的余弦相似度，而真正描述违约事件的文档因为语言表达更隐晦，反而相似度偏低，被 Top-N 过滤掉了。</p>
<p>这是一个典型的<strong>向量检索只看词不看语义关系</strong>的失败案例。<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup></p>
<h2 id="向量检索的物理课两个不相识的学生在各自考试">向量检索的物理课：两个不相识的学生在各自考试</h2>
<p>理解为什么向量检索会&quot;看走眼&rdquo;，需要先理解它的工作原理。</p>
<p>向量检索的核心是<strong>Bi-Encoder（双编码器）<strong>架构。当你把文档存入向量数据库时，每个文档都会通过一个 Encoder 被压缩成一个固定长度的向量——通常 768 维或 1024 维。这个过程发生在</strong>入库时</strong>，与用户未来的查询完全无关。</p>
<p>当用户发起查询时，查询文本同样通过 Encoder 生成一个查询向量。然后，数据库在<strong>高维空间</strong>中做最近邻搜索（通常用余弦相似度或内积），找出与查询向量&quot;距离最近&quot;的 N 个文档。</p>
<p>这个过程有一个非常关键的特征：<strong>Query 和 Document 的编码是独立完成的，它们从未&quot;见面&quot;</strong>。</p>
<p>斯坦福大学 NLP 组有一个很直观的比喻：就像两个学生分别在不同的考场同时参加考试，学生 A（Query 编码器）看了一眼题目后把答案写成压缩笔记，学生 B（Document 编码器）提前把教科书内容写成压缩笔记。考试结束后，系统只是比较这两份笔记的&quot;形状&quot;有多像，而不知道题目问的是什么。</p>
<p>这解释了为什么&quot;信用违约记录&quot;的查询会匹配到&quot;信用良好客户&quot;文档：两份文档都高频出现&quot;信用&quot;字眼，它们的向量在语义空间中距离很近，而模型根本不知道&quot;违约&quot;和&quot;良好&quot;是反义词。</p>
<h2 id="重排模型让-query-和-document-当面对质">重排模型：让 Query 和 Document 当面对质</h2>
<p>Cross-Encoder（交叉编码器）采用了完全不同的策略。</p>
<p>它不做&quot;独立压缩&quot;，而是把**[Query + Document] 拼接成一段完整文本**，一次性通过一个深度神经网络（如 BERT）。在这个过程中，模型的注意力机制（Self-Attention）会在每一个 token 层级做交叉比对——当读到 Query 中的&quot;违约&quot;时，它会立刻去 Document 中寻找是否存在&quot;违约&quot;、是否存在语义矛盾、是否存在否定结构。</p>
<p>这种&quot;当面对质&quot;的方式，让 Cross-Encoder 能捕捉到 Bi-Encoder 完全无法处理的关系：</p>
<ul>
<li><strong>语义否定</strong>：Query &ldquo;如何不用 Python&rdquo; vs Document &ldquo;Python 教程&rdquo;——Bi-Encoder 会给高分，Cross-Encoder 能识别&quot;不&quot;的否定作用</li>
<li><strong>长距离依赖</strong>：查询涉及某个条件组合，文档在开头提到条件A、结尾提到条件B，Cross-Encoder 的注意力能跨越全文找到同时满足两个条件的文档</li>
<li><strong>细微差异</strong>：两份文档语意相近但立场相反（如上面的违约案例），Cross-Encoder 能识别出差异</li>
</ul>
<p>用一个直观的对比表总结：</p>
<table>
  <thead>
      <tr>
          <th>维度</th>
          <th>Bi-Encoder（向量检索）</th>
          <th>Cross-Encoder（重排）</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>计算时机</td>
          <td>Query 和 Doc 独立入库/查询</td>
          <td>查询时实时计算</td>
      </tr>
      <tr>
          <td>Query-Doc 关系</td>
          <td>分离编码，从不见面</td>
          <td>拼接后联合编码</td>
      </tr>
      <tr>
          <td>速度</td>
          <td>毫秒级（向量索引）</td>
          <td>慢（需全文 forward）</td>
      </tr>
      <tr>
          <td>语义理解深度</td>
          <td>浅（只看轮廓）</td>
          <td>深（逐 token 交叉注意）</td>
      </tr>
      <tr>
          <td>适合场景</td>
          <td>海量初筛</td>
          <td>精确排序</td>
      </tr>
  </tbody>
</table>
<h2 id="工程正解先召回后重排的两阶段架构">工程正解：先召回后重排的两阶段架构</h2>
<p>理论上最准确的方案是让所有文档都过 Cross-Encoder，但这是不现实的——Cross-Encoder 的计算成本比向量检索高出 2-3 个数量级。如果对全量文档做 Cross-Encoder 重排，一次查询可能需要几十秒甚至几分钟。</p>
<p>生产环境的正确做法是<strong>先快后准</strong>的两阶段流水线：</p>
<pre tabindex="0"><code>用户查询
    │
    ▼
┌─────────────────────────┐
│  Stage 1: Bi-Encoder    │  ← 向量检索，毫秒级，从百万文档中召回 Top 100-500
│  (向量数据库: FAISS/     │
│   Milvus/Qdrant)         │
└────────────┬──────────────┘
             │ 粗筛候选集（可能包含语义噪声）
             ▼
┌─────────────────────────┐
│  Stage 2: Cross-Encoder  │  ← 重排模型，对 Top 100-500 做精细排序
│  (如 BGE-Reranker、      │
│   Cohere Rerank 3)        │
└────────────┬──────────────┘
             │ 精排结果（Top 10）
             ▼
        LLM 生成答案
</code></pre><p>这个架构在 2025-2026 年已成为 RAG 系统的事实标准。背后的核心洞察是：<strong>速度和准确性是一对矛盾，但它们的适用场景不同</strong>。向量检索负责从海量数据中快速筛选候选集（追求召回率），重排模型负责从候选集中精确选出最好的 N 个（追求精确率）。</p>
<h3 id="2026-年的重排模型格局">2026 年的重排模型格局</h3>
<p>当前生产环境主流的重排模型有几个选择<sup id="fnref:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup><sup id="fnref:3"><a href="#fn:3" class="footnote-ref" role="doc-noteref">3</a></sup>：</p>
<p><strong>BGE-Reranker（智源开源）</strong></p>
<ul>
<li>基于 BAAI/bge-reranker-v2-gemma 模型</li>
<li>支持中英文双语，在中文语义理解上优于西方开源模型</li>
<li>提供 v1（纯 Transformer）和 v2-gemma（更大参数量）两个版本</li>
<li>可直接通过 Sentence Transformers 库调用</li>
</ul>
<p><strong>Cohere Rerank 3</strong>
-闭源 API，按调用次数计费</p>
<ul>
<li>在多语言场景下表现稳定，有完善的评估体系</li>
<li>优点是不需要运维，缺点是数据需要经过第三方</li>
</ul>
<p><strong>Mixedbread mxbai-rerank</strong></p>
<ul>
<li>开源可自托管</li>
<li>专注文档相关性排序，适合企业内部私有知识库场景</li>
</ul>
<h2 id="性能对比数字不会说谎">性能对比：数字不会说谎</h2>
<p>在 Hugging Face 的 MTEB（Massive Text Embedding Benchmark）排行榜上，Bi-Encoder 和 Cross-Encoder 在不同任务类型上的表现差异非常显著<sup id="fnref:4"><a href="#fn:4" class="footnote-ref" role="doc-noteref">4</a></sup>：</p>
<table>
  <thead>
      <tr>
          <th>任务类型</th>
          <th>Bi-Encoder 代表模型</th>
          <th>Cross-Encoder 代表模型</th>
          <th>差距</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>语义相似度</td>
          <td>BGE-large</td>
          <td>BGE-Reranker</td>
          <td>Cross-Encoder +8-12%</td>
      </tr>
      <tr>
          <td>问答匹配</td>
          <td>E5-large</td>
          <td>BGE-Reranker</td>
          <td>Cross-Encoder +15%</td>
      </tr>
      <tr>
          <td>情感分析</td>
          <td>MiniLM</td>
          <td>Cross-Encoder-Sentiment</td>
          <td>Cross-Encoder +6%</td>
      </tr>
      <tr>
          <td>代码检索</td>
          <td>BGE-code</td>
          <td>Cohere Rerank</td>
          <td>Cross-Encoder +10%</td>
      </tr>
  </tbody>
</table>
<p>差距最大的场景是<strong>问答匹配</strong>——这恰恰也是 RAG 系统最核心的场景。这组数字印证了前文那个金融风控 Bad Case 的根因：Bi-Encoder 在&quot;问什么&quot;和&quot;答什么&quot;的语义匹配上天然存在短板。</p>
<h2 id="工程实践中的常见陷阱">工程实践中的常见陷阱</h2>
<p><strong>陷阱 1：召回数量设得太少</strong></p>
<p>很多团队把 Top-N 设成 5 或 10，看起来&quot;够用了&quot;。但研究发现，在复杂查询场景下，正确答案经常出现在 20-50 名之后——特别是当文档库很大、正确答案的表述方式与查询query差异较大时。推荐起始值设为 50-100，留给重排模型足够的候选空间。</p>
<p><strong>陷阱 2：重排后不再过滤</strong></p>
<p>Cross-Encoder 给出的相关性分数是相对的，不是绝对的——它只能告诉你&quot;这篇比那篇更相关&quot;，不能告诉你&quot;这两篇到底有多相关&quot;。如果重排后 Top 10 中出现了明显不相关的文档，需要设置一个相关性分数阈值做二次过滤，而不只是信任重排模型的排序。</p>
<p><strong>陷阱 3：把重排模型和 Embedding 模型混用</strong></p>
<p>Cross-Encoder 的重排效果和它用的 Encoder 有耦合关系。BGE-Reranker 在 BGE 生成的 Embedding 基础上表现最好，换成 OpenAI 的 text-embedding-3-large 后效果会有明显下降。重排模型和向量编码器最好来自同一个模型族，或经过联合调优。</p>
<h2 id="什么时候不需要重排">什么时候不需要重排</h2>
<p>两阶段架构不是银弹。如果你的场景满足以下条件，可能不需要重排：</p>
<ul>
<li><strong>文档结构单一、表述标准</strong>：比如内部 FAQ，Query 和 Answer 通常高度匹配，Bi-Encoder 的召回准确率已经足够</li>
<li><strong>实时性要求极高</strong>：比如流式对话场景，重排增加的 50-200ms 延迟可能不可接受</li>
<li><strong>文档量级较小</strong>：如果你的向量数据库只有几千篇文档，完全可以对全量做 Cross-Encoder 重排</li>
</ul>
<h2 id="延伸多路召回的崛起">延伸：多路召回的崛起</h2>
<p>2025 年下半年，一个更复杂的架构开始流行：<strong>多路召回</strong>（Multi-Retrieval）。它不只是向量检索 + 重排，而是同时调用多种检索路径——向量检索、BM25 关键词检索、GraphRAG 的知识图谱检索——然后用重排模型统一对各路结果做二次排序。</p>
<p>这种架构背后的洞察是：没有任何单一检索方式在所有 query 类型上都表现最优。向量检索擅长语义匹配但对专有名词不敏感，BM25 对精确匹配很强但不懂语义，多路召回通过让各路&quot;投票&quot;，能显著提升召回的鲁棒性。</p>
<hr>
<p><em>相关链接：</em><br>
<em><sup id="fnref1:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup> Stanford HAI RAG Evaluation: <a href="https://hai.stanford.edu/">https://hai.stanford.edu/</a></em><br>
<em><sup id="fnref1:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup> BGE-Reranker v2 on Hugging Face: <a href="https://huggingface.co/BAAI/bge-reranker-v2-gemma">https://huggingface.co/BAAI/bge-reranker-v2-gemma</a></em><br>
<em><sup id="fnref1:3"><a href="#fn:3" class="footnote-ref" role="doc-noteref">3</a></sup> Cohere Rerank Documentation: <a href="https://docs.cohere.com/docs/rerank">https://docs.cohere.com/docs/rerank</a></em><br>
<em><sup id="fnref1:4"><a href="#fn:4" class="footnote-ref" role="doc-noteref">4</a></sup> MTEB Leaderboard: <a href="https://huggingface.co/spaces/mteb/leaderboard">https://huggingface.co/spaces/mteb/leaderboard</a></em></p>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p>该案例为基于真实 RAG 系统故障模式的工程复盘，细节经抽象化处理。类似案例可参考 Stanford HAI 的 RAG 评估研究: <a href="https://hai.stanford.edu/">https://hai.stanford.edu/</a>&#160;<a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a>&#160;<a href="#fnref1:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:2">
<p>BGE-Reranker v2: <a href="https://huggingface.co/BAAI/bge-reranker-v2-gemma">https://huggingface.co/BAAI/bge-reranker-v2-gemma</a>&#160;<a href="#fnref:2" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a>&#160;<a href="#fnref1:2" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:3">
<p>Cohere Rerank: <a href="https://docs.cohere.com/docs/rerank">https://docs.cohere.com/docs/rerank</a>&#160;<a href="#fnref:3" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a>&#160;<a href="#fnref1:3" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:4">
<p>MTEB Benchmark Results, Hugging Face. <a href="https://huggingface.co/spaces/mteb/leaderboard">https://huggingface.co/spaces/mteb/leaderboard</a>&#160;<a href="#fnref:4" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a>&#160;<a href="#fnref1:4" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
</ol>
</div>
]]></content:encoded></item></channel></rss>