測試方法:圍繞「大模型閘道接入」構造 20 篇中文文件 + 一一對應的 20 篇英文文件,
20 條中文 query + 20 條英文 query 人工標註答案,同一批語料在同一時間窗內跑三個模型。
語料規模有限,小於 5 個百分點的差距不構成結論,請按自己的語料復現。
一、先選對模型
選 bge-m3 的場景
- 語料以中文為主:檢索品質與 3-small 同檔,單價是它的一半, 同一批中文文本的 token 數又只有它的 42% —— 實際花費約 1/5
- 想省儲存:1024 維比 1536 省 33%、比 3072 省 66%
- 需要覆蓋冷門語種(100+ 語言)
- 希望本地能跑同一個開源模型,保證線上線下向量一致
選 OpenAI 的場景
- 語料以英文或程式碼為主:檢索品質高一檔。這類內容上 bge-m3 的 token 數反而多 15%–50%, 但單價減半之後總花費仍然更低,所以這裡該按品質選,不是按價格選
- 知識庫裡中英文混排、又只要返回一條答案
- 需要
dimensions降維來壓縮儲存 - 已有大量按 OpenAI 分數帶標定的閾值,不想重標
二、長文件一定要切塊
bge-m3 的視窗是 8192 token,一篇上萬字的手冊整篇塞得進去。但不該這麼做。
實測:把 20 個小節串成一篇長手冊,候選集裡另放 20 篇「每篇正好對應其中一節」的強幹擾短文,
再用 20 條問題去檢索——
同一個問題,對「整篇」和「正確那一小節」的相似度平均相差 +0.10:
一篇長文的單一向量是全文語義的平均值,會被任何一段精準的短文本壓過去。
三、相似度閾值必須按模型重標
這是從 OpenAI 遷到bge-m3 時最容易翻車的地方。兩者的分數帶完全不同。
同一批人工標註的語義對,三個模型給出的分:
實測出的最佳單一閾值(
bge-m3):
作為對照,
text-embedding-3-small 中文場景的最佳閾值是 0.45,3-large 是 0.33。
四、相似度不能用來判斷「說得對不對」
這一條對所有 embedding 模型都成立,不是某個模型的缺陷,但必須提前知道:
三個模型全部失守。餘弦相似度衡量的是「在不在談同一件事」,不是「說法是否一致」。
所以否定、價格數字、版本號、實體名這類關鍵差異,檢索階段一定分不開。正確的兜底是:
1
向量召回 Top 50–100
用
bge-m3 把候選範圍快速縮小,閾值只用來擋掉明顯無關的。2
重排序精排 Top 3–5
用
bge-reranker-v2-m3 對候選逐條打分。
它是 query 和文件拼在一起過一遍模型的 Cross-Encoder,恰好擅長區分這類細微差異,
而且和 bge-m3 出自同一個模型家族。3
生成階段讓大模型自己判斷
把 Top 3–5 連同原始問題一起交給大模型,在提示詞裡明確要求「若檢索內容與問題不符,直接說沒有找到」。
五、批次與併發怎麼開
批次:64–128 是拐點
128 條以後每條攤薄成本幾乎不再下降(58ms → 53ms),單次耗時卻漲了 7 倍。
單次耗時直接決定客戶端超時風險,以及一次失敗要重做多少工作。
併發:線上 8,灌庫 32–48
- 線上即時檢索走併發 8:實測 200 次零失敗
- 離線灌庫可以開到 32–48:吞吐最高,但已經開始出現 429,必須帶指數退避
- 不要超過 64:96 併發失敗率 11.8%,且出現 59 秒級的掛起請求
六、成本怎麼算
成本由兩件事相乘決定:單價和同一段文本被切成多少 token。兩者在這裡都不一樣。bge-m3 是 $0.01 / 1M tokens,text-embedding-3-small 是 $0.02,單價先差一半。
再疊上分詞效率——bge-m3 用 XLM-R 的 SentencePiece,中文約 2.1 字/token,
OpenAI 的 cl100k 中文只有約 0.9 字/token:
中文語料的實際花費約為
text-embedding-3-small 的 1/5。
英文和程式碼上 bge-m3 消耗的 token 更多,但單價減半之後總花費仍然更低——
所以這兩類語料該不該用它,看的是檢索品質(英文 Recall@1 70% vs 80%),不是價格。
儲存側同樣有差距。向量已 L2 歸一化、返回值本身就是 fp16 精度,
所以用 float16 存 bge-m3 的向量是零精度損失的:
七、幾個會靜默出錯的坑
7.1 LangChain 的預設配置會讓召回率從 80% 掉到 15%
實測代價:
修復方式:
7.2 空字串會被當成有效輸入
input: "" 在 bge-m3 上返回 200(OpenAI 官方此處是 400),會得到一條 1024 維向量並計費 2 token。
切塊指令碼如果沒過濾空塊,知識庫裡就會混進一批無意義向量,還會在檢索時隨機冒出來。
入庫前自己過濾空白文本。
7.3 dimensions 引數不能用
Model "bge-m3" does not support matryoshka representation, changing output dimensions will lead to poor results.
bge-m3 沒有做 Matryoshka 訓練,截斷向量會顯著掉點。要壓縮儲存請用 float16,不要自己截斷維度。
7.4 模型名大小寫敏感、沒有別名
只有bge-m3 可用。BAAI/bge-m3、BGE-M3 都會返回 503「無可用渠道」。
7.5 8192 是「每條輸入」的上限
單條超過 8192 token 直接返回 400,不會靜默截斷(這是好事:不會拿到一個丟了後半段卻看著正常的向量)。 但這個上限是逐條判定的,不是整次請求:實測單次請求塞進 1024 條 / 102560 token 仍然正常返回。 批次裡只要有一條超限,整個請求就 400,報錯裡的 token 數是那一條的,不是總和。八、一份可以直接抄的最小實現
相關文件
文本向量化 API
介面引數、返回格式、快速上手
重排序模型
bge-reranker-v2-m3,精排環節的正確解法RAG 實戰調優
兩段式檢索架構、召回條數怎麼定
模型價格
全部 embedding 模型的即時價格