<?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/" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Search on KbWen Blog</title>
    <link>https://www.kbwen.com/tags/search/</link>
    <description>Articles and notes on AI systems, machine learning, Python, data engineering, and software development.</description>
    <generator>Hugo</generator>
    <language>zh-tw</language>
    <image>
      <url>https://www.kbwen.com/images/og-default.png</url>
      <title>KbWen Blog</title>
      <link>https://www.kbwen.com/</link>
    </image>
    
    <lastBuildDate>Tue, 27 Oct 2026 14:40:00 +0800</lastBuildDate><atom:link href="https://www.kbwen.com/tags/search/index.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>RAG 混合檢索的 Reciprocal Rank Fusion</title>
      <link>https://www.kbwen.com/rag-reciprocal-rank-fusion-zh/</link>
      <pubDate>Tue, 27 Oct 2026 14:40:00 +0800</pubDate><dc:creator>KbWen</dc:creator>
      <guid>https://www.kbwen.com/rag-reciprocal-rank-fusion-zh/</guid>
      <description>從官方範例看看文字搜尋和向量搜尋的結果，如何用 RRF 合併排序。</description>
      <content:encoded><![CDATA[<blockquote>
<p>計算 RRF 時，保留各份搜尋結果的名次，將每份文件的 <code>1 / (常數 + 名次)</code> 加總後再排序；原本的檢索分數不直接相加。</p>
</blockquote>
<p><a href="https://www.elastic.co/docs/reference/elasticsearch/rest-apis/reciprocal-rank-fusion">Elastic 的 Reciprocal Rank Fusion（RRF）範例</a>有五份文件，文字搜尋與向量搜尋各自回傳一份排名。RAG 的混合檢索會用 RRF 合併這樣的搜尋結果，下面用文件 ID 對照兩份名次：</p>
<table>
  <thead>
      <tr>
          <th>文件 ID</th>
          <th style="text-align: right">文字搜尋名次</th>
          <th style="text-align: right">向量搜尋名次</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>4</td>
          <td style="text-align: right">1</td>
          <td style="text-align: right">沒出現</td>
      </tr>
      <tr>
          <td>3</td>
          <td style="text-align: right">2</td>
          <td style="text-align: right">1</td>
      </tr>
      <tr>
          <td>2</td>
          <td style="text-align: right">3</td>
          <td style="text-align: right">2</td>
      </tr>
      <tr>
          <td>1</td>
          <td style="text-align: right">4</td>
          <td style="text-align: right">3</td>
      </tr>
      <tr>
          <td>5</td>
          <td style="text-align: right">沒出現</td>
          <td style="text-align: right">4</td>
      </tr>
  </tbody>
</table>
<p>文件 4 在文字搜尋排第一，向量搜尋卻沒有它。範例裡，文件 4 沒有向量欄位，因此只出現在文字結果中。文件 2 則是兩邊都找到，文字排第三、向量排第二。兩份清單是分開排序的，文字的第三名只表示前面有兩份文件；向量的第二名也只描述向量結果中的順序。先保留這兩份名次，RRF 會用名次產生新的分數。</p>
<p>表格裡的「沒出現」也需要留著。文件 4 的向量名次不能填成第五名，或是直接接在已經找到的文件後面。向量搜尋並沒有回傳它，從那份結果就拿不到它的位置。合併時能使用的資料，是表格中已經有名次的那些欄位。</p>
<h2 id="名次的加總">名次的加總</h2>
<p>RRF 對每份文件計算：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">RRF(d) = Σ [1 / (c + rank_L(d))], L: d ∈ L
</span></span></code></pre></div><p><code>L</code> 是其中一份搜尋結果，名次從 1 起算；文件有出現在那份結果裡，才加上該項分數。這裡用 <code>c</code> 表示 RRF 的常數，Elastic API 的參數名稱是 <code>rank_constant</code>。官方範例把它設為 1，目前產品預設值則是 60。</p>
<p>套回文件 4，文字第一名提供的分數是 <code>1 / (1 + 1) = 0.5</code>。向量結果沒有文件 4，也就沒有第二筆分數，所以合併分數仍是 0.5。缺少向量不會讓文件從混合結果中消失，它還有文字搜尋找到的名次。</p>
<p>文件 2 的文字名次雖然較後面，卻有兩筆分數可以加總。文字第三名得到 <code>1 / (1 + 3)</code>，向量第二名得到 <code>1 / (1 + 2)</code>，加起來約 0.5833，比文件 4 的 0.5 高。因此文件 2 在合併後排到文件 4 前面。同份文件出現在兩邊，計算時會把兩筆分數都加進去，不需要選其中較高的那筆，也不會因為內容重複就只留第一次找到的名次。</p>
<p>不過，光是兩邊都有找到，並沒有保證一定排在前面。名次較後面的項目，代入公式後分母較大，對加總的貢獻就較小。文件 2 在這個例子裡得到的分數，來自表上的第三名與第二名；若名次改變，就得照改變後的位置重新計算。原始檢索分數沒有放進公式，只要各份清單內的順序不變，這次加總就不變。</p>
<p>再看文件 3，文字第二名加上向量第一名，得到約 0.8333。範例最後只回傳三份文件，順序是 3、2、4。</p>
<h2 id="候選範圍">候選範圍</h2>
<p>這個計算要先拿到各份搜尋結果。Elastic 用 <code>rank_window_size</code> 決定每份結果有多少候選參加 RRF，再用 <code>size</code> 決定最後回傳多少份文件。前者限制輸入清單，後者裁切合併後的清單。</p>
<p>如果各份候選結果只保留前兩名，文件 2 就只剩向量第二名，文字第三名被截掉了。計算時少了文字搜尋提供的那筆分數，合併結果也會改變。只調整最後回傳的文件數，不會補回已經截掉的名次。對照搜尋結果時，可以先看看文件在哪些輸入清單裡，再核對加總是否符合那些名次。</p>
<p>本文的數字來自 Elastic 的小範例，適合拿來核對 RRF 的計算；換成自己的文件後，哪些結果對提問有幫助，還是得搭配實際的問題與搜尋結果來看。</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>Reciprocal rank fusion with multiple vector fields</title>
      <link>https://www.kbwen.com/rag-reciprocal-rank-fusion-en/</link>
      <pubDate>Tue, 27 Oct 2026 14:40:00 +0800</pubDate><dc:creator>KbWen</dc:creator>
      <guid>https://www.kbwen.com/rag-reciprocal-rank-fusion-en/</guid>
      <description>A closer look at the result lists that go into reciprocal rank fusion when a hybrid search uses several vector fields.</description>
      <content:encoded><![CDATA[<p>Reciprocal rank fusion (RRF) combines ranked search results, including keyword and vector results in a hybrid RAG retriever. Each search produces an ordered list of documents before fusion begins.</p>
<p><a href="https://learn.microsoft.com/en-us/azure/search/hybrid-search-ranking">Azure AI Search</a> counts a full-text query plus a vector query targeting two vector fields as three query executions. There can still be just one vector query in the request, with each targeted field producing a separate ranking for fusion.</p>
<h2 id="rank-contributions">Rank contributions</h2>
<p>In the <a href="https://www.elastic.co/docs/reference/elasticsearch/rest-apis/reciprocal-rank-fusion">RRF formula</a>, a document at rank <code>r</code> contributes <code>1 / (c + r)</code> from that list, with ranks starting at one. Add the contributions for every list containing the document to get its fused score. Here <code>c</code> names the ranking constant, so it stays separate from the <code>k</code> that a vector query uses to request nearest neighbors. The calculation below fixes <code>c</code> at 60 and gives every list equal weight, as Elasticsearch&rsquo;s RRF child retrievers do.</p>
<p>Take two hypothetical documents, A and B, with these positions in the keyword and vector results:</p>
<table>
  <thead>
      <tr>
          <th>Document</th>
          <th style="text-align: right">Keyword rank</th>
          <th style="text-align: right">Vector rank</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>A</td>
          <td style="text-align: right">1</td>
          <td style="text-align: right">4</td>
      </tr>
      <tr>
          <td>B</td>
          <td style="text-align: right">4</td>
          <td style="text-align: right">2</td>
      </tr>
  </tbody>
</table>
<p>A has the stronger keyword position. B appears earlier in the vector results, although neither document ranks first there. The intervening documents can remain out of this calculation: each document&rsquo;s RRF score uses its own rank in each list. A and B both appear in both lists, so each receives two contributions.</p>
<p>For A, the sum is <code>1/61 + 1/64</code>. For B, it is <code>1/64 + 1/62</code>. A finishes slightly ahead. Both sums contain a fourth-place contribution. After that common term, A has a first-place contribution where B has a second-place contribution. No keyword score or vector similarity value is needed to make that comparison. The keyword scores might be close together or far apart. With those ranks held fixed, the reciprocal contributions would be the same.</p>
<p>Now add a second vector result list with exactly the same order as the first. This is a constructed example, rather than a claim that two real embedding fields will return identical rankings. A receives another fourth-place contribution. B receives another second-place contribution, which is a little larger.</p>
<table>
  <thead>
      <tr>
          <th>Document</th>
          <th style="text-align: right">Keyword + one vector list</th>
          <th style="text-align: right">Keyword + two identical vector lists</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>A</td>
          <td style="text-align: right">0.032018443</td>
          <td style="text-align: right">0.047643443</td>
      </tr>
      <tr>
          <td>B</td>
          <td style="text-align: right">0.031754032</td>
          <td style="text-align: right">0.047883065</td>
      </tr>
  </tbody>
</table>
<p>B now ranks ahead of A. The keyword result list has not changed, and the original vector list has not changed either. A still has its first-place keyword result. But B&rsquo;s better vector position contributes twice, enough to reverse this pair&rsquo;s order.</p>
<h2 id="multiple-vector-fields">Multiple vector fields</h2>
<p>Azure&rsquo;s execution count gives this arithmetic an ordinary place to occur: a text query and a vector query over two fields provide more lists to fuse than a text query and a vector query over one field. Different per-field rankings would change the other terms in the calculation as well.</p>
<p>A ranking change after adding a vector field therefore leaves a practical question open. How much came from the field finding different documents, and how much came from adding another ranked list? The fused scores alone do not separate those effects. Comparing the input lists for the same query gives something more concrete to inspect: whether a newly favored document appeared only in the new field&rsquo;s results, or already appeared across the original lists and gained an additional contribution.</p>
<p>For an actual comparison, keep the query and documents fixed and save the per-field ranks alongside the fused order. Use the candidate lists actually passed to fusion. A separate search with different filters or a different candidate limit may give a document a different position, so its rank would no longer explain the original sum. If the service applies explicit weights, retain those too; the equal-weight calculation here is only the stated example. Whether the resulting context helps the RAG answer still needs to be checked against the questions the application is meant to answer.</p>
]]></content:encoded>
    </item>
    
  </channel>
</rss>
