近幾日在研究fast gpt 的源碼,對於它的混合檢索比較好奇:全文檢索是怎麼實現的?向量檢索呢?
看了源碼才瞭解到,實際上fastgpt 支持三種向量庫,一是minlvs , 二是 pgsql vector, 三是oceanbase(阿里的)。
向量檢索,以pgvector為例簡單説一下,就是在pg庫里加載了pgvector插件,在postgresql 庫中創建了 modeldata 的表,其中有字段名為 vector,類型也是vector,就存儲切塊後的內容的向量值。是1536維的,據説在精準度和計算量之間達到了一個比較好的平衡。
建立向量索引,以及搜索算法,都是pgvector所支持的,不再細説。
令我好奇的是,其全文檢索是基於mongo db內置的來實現的。從源碼來看,用到了jieba 分詞,將句子分此後,用空格連接,以便兼容mongo db的算法(按照英文習慣,單詞之間用空格分隔)。這説明實際上原始內容存儲到mongo db的數據庫字段裏,也應該是經過分詞後,將每個詞之間採用空格分開的。
那麼,接下來我的疑問便是,其內部究竟是怎麼實現的。為此我求助了騰訊元寶的DeepSeek深度搜索,提示詞如下:
假如在 mongo db 中存儲了文章的內容,並對內容字段建立了全文檢索的索引,那麼加入搜索一個經過中文分詞後的句子“中國 汽車 品牌 世界 最強”,在mongo db 內部是怎麼進行檢索並對檢索結果進行打分的?假如有個記錄的內容是 “中國的汽車品牌 是世界上最強的沒有之一” ,另一個是“日本汽車比不上中國汽車”, 它們各自的得分會是多少?這個得分是按照內容包含了相應的關鍵字就得分呢?還是關鍵字在其中出現次數多也有助於高的得分呢?還是怎麼樣的?
以下便是值得記錄的它的回答:
MongoDB 的全文檢索基於 倒排索引 和 TF-IDF 算法 進行匹配和打分。以下是針對你問題的詳細分析:
一、檢索過程解析
1. 索引構建
- 對文章內容字段建立全文索引後,MongoDB 會:
- 分詞:將文本拆分為詞元(如
["中國", "汽車", "品牌", "世界", "最強"])。 - 構建倒排索引:記錄每個詞元出現的文檔位置及頻率。
"中國" → [文檔1_ID, 文檔2_ID]
"汽車" → [文檔1_ID, 文檔2_ID]
"品牌" → [文檔1_ID]
"世界" → [文檔1_ID]
"最強" → [文檔1_ID]
2. 查詢處理
- 查詢
中國 汽車 品牌 世界 最強被拆分為獨立詞元。 - MongoDB 執行 OR 邏輯:匹配包含任意查詢詞元的文檔(除非使用
""指定短語)。
二、相關性打分機制
得分由 TF-IDF 算法 計算,公式簡化如下:
Score = TF(詞頻) × IDF(逆文檔頻率)
1. TF (詞頻)
- 詞在文檔中出現的次數越高,得分越高。
- 示例:
- 文檔1:
"中國的汽車品牌是世界上最強的沒有之一"
中國:1,汽車:1,品牌:1,世界:1,最強:1
- 文檔2:
"日本汽車比不上中國汽車"
中國:1,汽車:2(其他詞未出現)
2. IDF (逆文檔頻率)
- 詞在整個集合中的稀有程度。詞越常見,IDF 越低。
- 公式:
IDF = log(總文檔數 / 包含該詞的文檔數)
- 假設集合中有 1000 篇文檔:
汽車出現在 800 篇 → IDF = log(1000/800) ≈ 0.1最強出現在 10 篇 → IDF = log(1000/10) ≈ 2.0
3. 最終得分計算
- 文檔1:
Score = (TF_中國 × IDF_中國) + (TF_汽車 × IDF_汽車) + ...
≈ (1×0.2) + (1×0.1) + (1×1.5) + (1×0.3) + (1×2.0) = 4.1
- 文檔2:
Score = (TF_中國 × IDF_中國) + (TF_汽車 × IDF_汽車)
≈ (1×0.2) + (2×0.1) = 0.4
✅ 結論:文檔1 得分遠高於文檔2,因為它包含更多查詢詞元且包含高 IDF 詞(如“最強”)。
三、其他影響因素
- 詞元位置與權重:
- 標題中的詞可能比正文權重更高(取決於索引配置)。
- 短語匹配:
- 若查詢用引號包裹
"中國汽車",連續出現的短語會獲得額外加分。
- 停用詞過濾:
- “的”、“是”等停用詞會被忽略,不計入打分。
- 詞幹提取:
- 英文中“running”會被歸併為“run”,中文需依賴分詞工具(如你的代碼中的
jiebaSplit)。
四、你的示例結果對比
|
文檔內容
|
匹配詞元
|
TF-IDF 得分
|
原因分析
|
|
中國的汽車品牌是世界上最強的沒有之一 |
中國、汽車、品牌、世界、最強 |
較高 (如4.1) |
命中全部查詢詞元,且包含高 IDF 詞(如“最強”) |
|
日本汽車比不上中國汽車 |
中國、汽車 |
較低 (如0.4) |
僅命中2個詞元,且“汽車”因常見導致 IDF 低 |
關鍵結論
- 命中詞元數量和詞元的稀有度(IDF)是得分的核心因素。
- 代碼中的
score: { $meta: 'textScore' }正是獲取這個 TF-IDF 分數。 - 中文場景需依賴分詞工具(如你的
jiebaSplit)預處理查詢詞,否則 MongoDB 默認按空格分詞(對中文不友好)。
ok,本篇結束。感謝AI 的回覆。