義向量到企業(yè)級(jí)問(wèn)答系統(tǒng))
1. 為什么說(shuō) Embedding 是問(wèn)答系統(tǒng)的勝負(fù)手我在這個(gè)系列前面的章節(jié)里把 RAG檢索增強(qiáng)生成的整體架構(gòu)拆開(kāi)講過(guò)一遍文檔切塊、索引構(gòu)建、召回、重排、最終生成。很多讀者看到第 8 章才開(kāi)始著急問(wèn)我“Embedding 到底怎么落地”。這很正常因?yàn)榍懊娴沫h(huán)節(jié)就算做得粗糙一點(diǎn)問(wèn)答系統(tǒng)湊合著也能跑但 Embedding 做得不好整套系統(tǒng)的“上限”就被鎖死了。你后面再怎么調(diào) Prompt、換大模型答案質(zhì)量都很難有明顯提升。這里要先把一個(gè)概念掰清楚Embedding 到底是什么。通俗點(diǎn)說(shuō)Embedding 就是把一段文本不管是一個(gè)詞、一句話還是一整段文檔轉(zhuǎn)換成一串固定長(zhǎng)度的數(shù)字?jǐn)?shù)組。例如“企業(yè)知識(shí)庫(kù)”和“公司文檔庫(kù)”這兩個(gè)詞如果分別做 Embedding我們期望它們?cè)谙蛄靠臻g里的距離非常近因?yàn)樗鼈兠枋龅氖峭粋€(gè)東西。反過(guò)來(lái)“企業(yè)知識(shí)庫(kù)”和“今天中午吃什么”的距離就應(yīng)該非常遠(yuǎn)。也就是說(shuō)Embedding 把人類能理解的“語(yǔ)義相似度”翻譯成了計(jì)算機(jī)能計(jì)算的“向量距離”。一套企業(yè)級(jí)智能問(wèn)答系統(tǒng)本質(zhì)上做的事就三步先把你所有的知識(shí)文檔向量化存進(jìn)向量數(shù)據(jù)庫(kù)用戶提問(wèn)時(shí)把問(wèn)題做同樣的向量化然后在數(shù)據(jù)庫(kù)里找出距離最近的幾個(gè)文本片段連同問(wèn)題一起交給大模型生成答案。第二步和第三步里的“找最近”前提就是第一步做對(duì)了。我見(jiàn)過(guò)不少團(tuán)隊(duì)在文檔解析、切塊策略上花了很多功夫卻隨便選了一個(gè)開(kāi)源的 Embedding 模型理由是“反正都是向量化應(yīng)該差不多”。實(shí)際測(cè)下來(lái)你會(huì)發(fā)現(xiàn)差距非常大同一個(gè)問(wèn)題好的 Embedding 模型能精確召回答案所在的那幾個(gè)段落差的模型可能召回的文本在字面上有重疊但語(yǔ)義上完全不是用戶想表達(dá)的意思。這種“字面相似、語(yǔ)義無(wú)關(guān)”的召回結(jié)果是問(wèn)答系統(tǒng)最常見(jiàn)的失敗模式之一。所以我傾向于把 Embedding 稱為問(wèn)答系統(tǒng)的“地基工程”。這一章我把這套體系里最關(guān)鍵的部分完整走一遍先聊傳統(tǒng)方法為什么不行再講現(xiàn)代 Embedding 模型的核心邏輯然后給出選型和落地的具體步驟最后把實(shí)際項(xiàng)目中容易踩的坑集中列出來(lái)。你照著做至少能保證基礎(chǔ)質(zhì)量是穩(wěn)的。2. 傳統(tǒng)文本表示與語(yǔ)義向量的本質(zhì)差別2.1 One-Hot、TF-IDF 這些老朋友輸在哪里早期做問(wèn)答系統(tǒng)文本表示主要靠?jī)深惙椒ㄔ~袋模型和 TF-IDF。它們的核心邏輯是“統(tǒng)計(jì)”而非“理解”。詞袋模型把每個(gè)文本表示成一個(gè)向量向量的長(zhǎng)度等于語(yǔ)料庫(kù)的詞表大小每一個(gè)維度對(duì)應(yīng)一個(gè)詞出現(xiàn)就記為 1沒(méi)出現(xiàn)就記為 0。這種方式帶來(lái)的問(wèn)題非常直觀如果兩個(gè)文本共享的詞匯很少哪怕它們的語(yǔ)義幾乎一樣向量也會(huì)被判斷為“非常不相似”。比如“如何申請(qǐng)年假”和“休假的申請(qǐng)流程”字面重疊只有“申請(qǐng)”和“的”詞袋模型會(huì)認(rèn)為它們關(guān)系很弱。TF-IDF 在此基礎(chǔ)上做了改進(jìn)用詞頻乘以逆文檔頻率來(lái)給詞加權(quán)試圖體現(xiàn)“哪些詞更重要”。這在一定程度上緩解了高頻無(wú)意義詞如“的”“了”“是”的干擾但它本質(zhì)上還是字面匹配——它不知道“年假”和“休假”是同一個(gè)概念不知道“離職補(bǔ)償”和“裁員賠償”在特定場(chǎng)景下高度相關(guān)。企業(yè)知識(shí)庫(kù)里用戶提問(wèn)的方式是極其多樣化的同一件事可能有一百種問(wèn)法字面匹配的召回方式在這種場(chǎng)景下基本就是聽(tīng)天由命。2.2 預(yù)訓(xùn)練模型打開(kāi)了語(yǔ)義向量的大門2013 年 Word2Vec 出現(xiàn)之后文本表示進(jìn)入了“分布式表示”時(shí)代。它是通過(guò)神經(jīng)網(wǎng)絡(luò)把詞嵌入到低維稠密向量里讓“語(yǔ)義相近的詞在向量空間里靠在一起”。這個(gè)思路是對(duì)的但它只能表征一個(gè)詞而問(wèn)答系統(tǒng)需要的是一整句話甚至一整段的表征。后來(lái)BERT 這類預(yù)訓(xùn)練語(yǔ)言模型的出現(xiàn)把“整句語(yǔ)義表征”這件事推向了一個(gè)新高度。用 BERT 類的模型做 Embedding不是簡(jiǎn)單地把句子里的詞向量加一加而是通過(guò)深層的 Transformer 編碼器在每一層里讓每個(gè) token 和其他所有 token 做交互從而捕捉上下文相關(guān)的語(yǔ)義信息。同一個(gè)詞“蘋(píng)果”在“蘋(píng)果公司發(fā)布了新手機(jī)”和“我今天吃了一個(gè)蘋(píng)果”這兩個(gè)句子里模型給出的語(yǔ)義向量應(yīng)該是不同的這就是上下文感知的能力也是它能真正理解語(yǔ)義的關(guān)鍵。當(dāng)然直接拿 BERT 的 [CLS] 向量或者所有 token 的平均池化結(jié)果來(lái)做文本 Embedding效果并不一定好原因以后再展開(kāi)。這里你只需要理解一個(gè)核心變化從“統(tǒng)計(jì)字面重合”到“建模語(yǔ)義關(guān)聯(lián)”這是 Embedding 這條路能走通的前提。2.3 對(duì)比學(xué)習(xí)讓“接近”與“遠(yuǎn)離”更明確現(xiàn)在主流的高質(zhì)量 Embedding 模型幾乎都使用了對(duì)比學(xué)習(xí)Contrastive Learning的思想。原理不復(fù)雜訓(xùn)練時(shí)給模型一批文本對(duì)——正樣本對(duì)是由query, positive_document組成兩者是相關(guān)的負(fù)樣本對(duì)則是query, negative_document兩者不相關(guān)。訓(xùn)練目標(biāo)是讓正樣本對(duì)的向量距離盡可能小讓負(fù)樣本對(duì)的向量距離盡可能大同時(shí)拉大負(fù)樣本和所有正樣本之間的距離。這種做法非常契合問(wèn)答系統(tǒng)的使用場(chǎng)景。因?yàn)樵趯?shí)際運(yùn)行時(shí)用戶的問(wèn)題形態(tài)和知識(shí)庫(kù)的文本形態(tài)往往差異很大——問(wèn)題比較口語(yǔ)化、簡(jiǎn)短知識(shí)庫(kù)文本則是正式的句子或段落。通過(guò)對(duì)比學(xué)習(xí)訓(xùn)練出來(lái)的模型能刻意縮小“口頭問(wèn)法”和“書(shū)面表述”之間的距離。這也解釋了為什么我不建議直接用原生的 BERT 模型做 Embedding——它的訓(xùn)練目標(biāo)掩碼語(yǔ)言模型不是為了衡量句子之間的語(yǔ)義相似度而設(shè)計(jì)的。明白了這些你就能理解為什么選模型而不是隨便拿一個(gè)來(lái)用不同模型的訓(xùn)練數(shù)據(jù)、優(yōu)化目標(biāo)、向量維度、支持的最大序列長(zhǎng)度都不一樣這些都直接決定了它在你的業(yè)務(wù)數(shù)據(jù)上表現(xiàn)好壞。3. 模型選型榜單之外的四個(gè)關(guān)鍵維度每次一聊選型就會(huì)有人直接甩給你一個(gè) MTEB 榜單地址說(shuō)“按榜單從高到低選就行了”。但企業(yè)落地跟學(xué)術(shù)刷榜完全是兩碼事。我在實(shí)際項(xiàng)目里的選型邏輯基本圍繞下面四個(gè)維度展開(kāi)。3.1 先看業(yè)務(wù)語(yǔ)言的適配度第一個(gè)維度是你的業(yè)務(wù)語(yǔ)言是什么。中文場(chǎng)景下你必須重點(diǎn)關(guān)注模型對(duì)中文語(yǔ)義的支持程度。這里有一個(gè)常見(jiàn)誤區(qū)很多模型雖然在 MTEB 中文測(cè)試集上分?jǐn)?shù)不錯(cuò)但那只能說(shuō)明它在通用領(lǐng)域的中文數(shù)據(jù)上表現(xiàn)尚可到了垂直領(lǐng)域——比如醫(yī)療、法律、制造業(yè)、互聯(lián)網(wǎng)金融——效果可能一落千丈。我的建議是初篩只看它是否在中文語(yǔ)料上有過(guò)專門訓(xùn)練然后一定會(huì)拿自己業(yè)務(wù)里真實(shí)的數(shù)據(jù)去測(cè)試。你從知識(shí)庫(kù)里拿 100 條具有代表性的問(wèn)答樣本來(lái)實(shí)測(cè)比什么榜單都靠譜。具體怎么測(cè)我在第 4 章里會(huì)給出一個(gè)可以直接照搬的測(cè)試腳本。3.2 注意向量維度與存儲(chǔ)成本第二個(gè)維度容易被低估輸出向量的維度。市面常見(jiàn)的模型輸出向量維度從 384 到 768、1024、1536 甚至更高。維度越高通常意味著模型的表征能力越強(qiáng)但代價(jià)是存儲(chǔ)空間和檢索計(jì)算開(kāi)銷同步上漲。舉一個(gè)直觀的數(shù)字如果你有 100 萬(wàn)條文本片段每條向量維度是 768用 4 字節(jié)浮點(diǎn)數(shù)存儲(chǔ)那么裸數(shù)據(jù)需要 1000000 × 768 × 4 ≈ 3GB。維度翻倍到 1536就是 6GB加上向量索引的額外開(kāi)銷實(shí)際占用的存儲(chǔ)還得再乘個(gè)系數(shù)。在挑選模型時(shí)不要盲目追求高維度。對(duì)于多數(shù)企業(yè)知識(shí)庫(kù)場(chǎng)景768 維度已經(jīng)能在效果和成本之間取得很好的平衡。3.3 輸入長(zhǎng)度限制決定切塊策略第三個(gè)維度是最大輸入長(zhǎng)度Max Sequence Length。這個(gè)參數(shù)直接決定了你單條文本能喂給模型的最大長(zhǎng)度。常見(jiàn)模型支持 512 token新的模型普遍支持到 2048、4096 甚至更長(zhǎng)。它跟文檔切塊策略是強(qiáng)綁定的如果你用的模型最大長(zhǎng)度是 512那么你在切塊時(shí)每一塊文本的內(nèi)容就需要控制在 512 token 以內(nèi)。有些團(tuán)隊(duì)選了一個(gè)支持 8192 token 的模型然后認(rèn)為所有文檔都可以整篇塞進(jìn)去不切塊了——這是個(gè)很危險(xiǎn)的懶人思路。因?yàn)橄蛄炕皇窃凇霸~面”上做了信息壓縮你喂進(jìn)去一段 8000 字的文檔模型給出的 1024 維向量只能力圖概括整個(gè)文檔的主題但用戶問(wèn)的具體問(wèn)題往往只涉及其中一小塊細(xì)節(jié)這個(gè)細(xì)節(jié)信息在壓縮過(guò)程中很容易被稀釋掉。所以切塊環(huán)節(jié)依然不能省模型長(zhǎng)度只是給了你更大的切塊彈性它不是讓你放棄切塊的理由。3.4 近期值得留意的模型動(dòng)態(tài)順著網(wǎng)絡(luò)上的熱點(diǎn)話題多說(shuō)一句最近關(guān)于 SIGLIP2 的討論比較多。有人把它當(dāng)作新的 Embedding 模型來(lái)用這是一個(gè)方向但需要留意它的定位SIGLIP 本身是一個(gè)視覺(jué)與文本聯(lián)合的模型它擅長(zhǎng)的是圖文跨模態(tài)的對(duì)齊任務(wù)例如讓圖片和描述它的文本產(chǎn)生相近的向量而不是純粹的中文文本語(yǔ)義檢索。如果你是做“圖文混合檢索”的場(chǎng)景比如企業(yè)資料庫(kù)里有大量帶圖紙、截圖的手冊(cè)那像 SIGLIP2 這樣的多模態(tài)模型確實(shí)值得加入候選清單但如果你的場(chǎng)景是純文本問(wèn)答當(dāng)前已有的成熟文本 Embedding 模型通常更合適、更省資源。還有一個(gè)趨勢(shì)是模型榜單的快速洗牌。今天排名第一的模型可能兩三個(gè)月后就被替代。我的原則是不追最新只追實(shí)測(cè)。任何一個(gè)新模型進(jìn)到我的選型池之前都要先在同樣一批數(shù)據(jù)集上跑一遍用同樣的評(píng)測(cè)指標(biāo)對(duì)比新舊模型的差距然后再?zèng)Q定要不要升級(jí)。盲目追新模型還有一個(gè)隱藏風(fēng)險(xiǎn)——向量維度的變化可能迫使你重建整個(gè)向量庫(kù)。從 768 維模型切換到 1536 維模型不是改一個(gè)參數(shù)那么簡(jiǎn)單你歷史上已經(jīng)向量化的所有存量數(shù)據(jù)都得重新跑一遍。這一點(diǎn)在存量數(shù)據(jù)很大的時(shí)候往往是升級(jí)的最大阻力。下面我把選型要點(diǎn)整理成一個(gè)對(duì)比表方便你作為決策參考。選型維度關(guān)鍵問(wèn)題判斷方式語(yǔ)言適配度中文效果是否達(dá)標(biāo)用真實(shí)業(yè)務(wù)數(shù)據(jù)分批實(shí)測(cè)向量維度存儲(chǔ)成本是否可接受結(jié)合數(shù)據(jù)量估算存儲(chǔ)占用輸入長(zhǎng)度是否滿足切塊策略與切塊策略聯(lián)動(dòng)評(píng)估生態(tài)成熟度是否有穩(wěn)定的推理框架檢查 ONNX 導(dǎo)出、推理庫(kù)兼容性模型更新節(jié)奏能否平滑升級(jí)升級(jí)是否要求重建全部向量4. 向量化實(shí)操?gòu)慕涌诘酱a的完整鏈路選型定了之后就進(jìn)入到“向量化實(shí)戰(zhàn)”的正題。這一節(jié)我默認(rèn)你的目標(biāo)是把一套代碼可運(yùn)行、可監(jiān)控的向量化管線搭起來(lái)而不是只在 Notebook 里跑個(gè) demo。生產(chǎn)環(huán)境和實(shí)驗(yàn)環(huán)境的差別在于你要考慮數(shù)據(jù)一致性、異常重試、批量效率以及單條數(shù)據(jù)出了問(wèn)題之后的可觀測(cè)性。4.1 環(huán)境準(zhǔn)備與依賴安裝我建議用 Python 做這個(gè)環(huán)節(jié)的主力語(yǔ)言因?yàn)樯鷳B(tài)最成熟。你至少需要安裝以下幾個(gè)核心庫(kù)numpy用于向量數(shù)據(jù)的數(shù)值計(jì)算與歸一化處理transformers加載和運(yùn)行 Hugging Face 生態(tài)的模型如果用本地模型requests調(diào)用云服務(wù) API 時(shí)的 HTTP 客戶端sklearn可選的相似度計(jì)算工具其實(shí)更推薦直接手寫(xiě)內(nèi)積和余弦相似度依賴更少如果你走的是調(diào)用云廠商 Embedding API 的路線那transformers可以不裝如果你走本地模型路線我強(qiáng)烈建議你安裝 CPU 版本的 PyTorch并在條件允許的情況下配置好 CUDA。純 CPU 跑一個(gè) 100M 參數(shù)級(jí)別的模型速度會(huì)慢到你懷疑人生而一張普通 GPU 能把吞吐量提升一兩個(gè)數(shù)量級(jí)。4.2 第一版用 API 快速打通鏈路最快的驗(yàn)證方案是調(diào)成熟的服務(wù)商 Embedding 接口。以下是 Python 偽代碼示例核心邏輯是通用的import requests import numpy as np EMBEDDING_ENDPOINT your-embedding-endpoint EMBEDDING_API_KEY your-api-key def get_embedding(text: str) - np.ndarray: resp requests.post( EMBEDDING_ENDPOINT, headers{Authorization: fBearer {EMBEDDING_API_KEY}}, json{input: text, encoding_format: float} ) resp.raise_for_status() data resp.json() # 多數(shù)服務(wù)的返回結(jié)構(gòu)是 data[0][embedding] return np.array(data[data][0][embedding], dtypenp.float32) if __name__ __main__: vec get_embedding(企業(yè)年假申請(qǐng)流程) print(vec.shape) # 輸出如 (768,)這段代碼會(huì)把一句話變成固定維度的numpy數(shù)組。在這里你可以順便做一件事拿“企業(yè)年假申請(qǐng)流程”和“公司年假的申請(qǐng)步驟”兩句話分別生成向量然后計(jì)算它們的余弦相似度。如果這個(gè)相似度明顯高于“企業(yè)年假申請(qǐng)流程”和“今天天氣不錯(cuò)”的相似度說(shuō)明模型在這個(gè)語(yǔ)義維度上是靠譜的。這是整個(gè)系統(tǒng)能否跑通的最基礎(chǔ)驗(yàn)證。4.3 第二版本地模型與批量效率API 方案的優(yōu)勢(shì)是接入簡(jiǎn)單但長(zhǎng)期運(yùn)行有兩個(gè)問(wèn)題成本隨調(diào)用量線性增長(zhǎng)以及數(shù)據(jù)出網(wǎng)帶來(lái)的合規(guī)壓力。很多企業(yè)內(nèi)部知識(shí)庫(kù)對(duì)敏感程度要求很高并不適合把全文直接發(fā)到外部 API。因此在實(shí)際項(xiàng)目里本地模型往往是最終選擇。本地模型的處理流程用transformers庫(kù)就能完成。以加載一個(gè)中文 Embedding 模型為例from transformers import AutoTokenizer, AutoModel import torch import numpy as np model_name your-chinese-embedding-model-name tokenizer AutoTokenizer.from_pretrained(model_name) model AutoModel.from_pretrained(model_name) model.eval()然后是文本向量化的核心函數(shù)。這里有一個(gè)關(guān)鍵細(xì)節(jié)值得展開(kāi)很多初學(xué)項(xiàng)目直接用模型的最后一層 CLS token 輸出作為整句向量但更常見(jiàn)的做法是對(duì)所有 token 的最后一層隱藏狀態(tài)做均值池化def encode_texts(texts, max_length512): encoded tokenizer( texts, paddingTrue, truncationTrue, max_lengthmax_length, return_tensorspt ) with torch.no_grad(): outputs model(**encoded) # 均值池化對(duì)非 padding 位置的向量取平均 attention_mask encoded[attention_mask].unsqueeze(-1) token_embeddings outputs.last_hidden_state * attention_mask summed token_embeddings.sum(dim1) counts attention_mask.sum(dim1) mean_embeddings summed / counts # L2 歸一化這一步對(duì)余弦檢索非常重要 norm torch.linalg.norm(mean_embeddings, dim1, keepdimTrue) normalized mean_embeddings / norm return normalized.cpu().numpy().astype(np.float32)注意最后一步的 L2 歸一化。如果你的向量庫(kù)檢索用的是余弦相似度而向量沒(méi)有歸一化那檢索結(jié)果的排序就會(huì)受到向量模長(zhǎng)的影響而不是純粹反映方向上的相似度。歸一化之后余弦相似度等價(jià)于內(nèi)積實(shí)現(xiàn)更簡(jiǎn)單排序也更穩(wěn)定。批量處理是生產(chǎn)環(huán)境必須考慮的。模擬一下你有 10 萬(wàn)條文檔要向量化如果一條一條循環(huán)調(diào)用慢且浪費(fèi)算力。一定要設(shè)計(jì)成批量每次喂 32 條或 64 條充分利用 GPU 的并行能力。上面這段代碼已經(jīng)支持傳入texts列表一次處理一批。實(shí)測(cè)下來(lái)在普通單卡環(huán)境下批量處理可以把吞吐量提升 5 到 10 倍。4.4 強(qiáng)一致性向量化的策略全量建庫(kù)與增量更新向量化管線看起來(lái)不難難的是數(shù)據(jù)一致性。想象這樣一段演進(jìn)過(guò)程第一天你用某個(gè)模型的 v1 版本把 10 萬(wàn)條老文檔全部向量化入庫(kù)了第二周官方發(fā)布了 v2 版本評(píng)測(cè)結(jié)果提升了 2%你是不是要馬上更新如果馬上更新意味著存量 10 萬(wàn)條數(shù)據(jù)要全部重新向量化重新構(gòu)建索引。如果不更新新入庫(kù)的數(shù)據(jù)用 v2老數(shù)據(jù)用 v1兩種向量在同一個(gè)空間里比較效果必然混亂。這類問(wèn)題沒(méi)有完美的免費(fèi)午餐但有務(wù)實(shí)的做法。在項(xiàng)目冷啟動(dòng)階段不要頻繁切換模型版本每次切換都把“全量重算”當(dāng)作必要成本計(jì)算進(jìn)去。具體的執(zhí)行策略可以分成三種策略適用場(chǎng)景執(zhí)行方式全量重算數(shù)據(jù)量不大或者算法剛升級(jí)跑離線任務(wù)一次性重算所有文檔向量增量追加每日少量新增文檔配置定時(shí)任務(wù)僅對(duì)新增文本做向量化混合重建新舊向量必須共存雙寫(xiě)雙讀逐步灰度遷移直到舊版本清零這里有一個(gè)我踩過(guò)的坑提醒你“增量追加”很容易在不知不覺(jué)中變成“臟數(shù)據(jù)累積”。尤其是當(dāng)你在某一天調(diào)整了切塊策略比如把 chunk 大小從 256 改成 512那么從這一天起新增的向量和老向量在“文本粒度”上就已經(jīng)不一致了。找回時(shí)很容易出現(xiàn)漏召或誤召。所以任何切塊策略的改動(dòng)都應(yīng)當(dāng)觸發(fā)一次全量重算沒(méi)有例外。4.5 向量入庫(kù)前的最后一道工序向量從模型里出來(lái)到真正進(jìn)入向量數(shù)據(jù)庫(kù)之前還應(yīng)該做一件小事去重和漫游檢測(cè)。原因在于知識(shí)庫(kù)里經(jīng)常存在完全一樣的、或者極其相似的文本比如同一份制度在不同目錄下存了兩遍。這些重復(fù)向量占用存儲(chǔ)空間是小事更麻煩的是它們?cè)跈z索時(shí)會(huì)返回多條相同的文本白白占用了大模型的上下文窗口。做去重的邏輯很簡(jiǎn)單計(jì)算向量間的余弦相似度相似度高于 0.95 的兩條文本保留內(nèi)容更完整的一條即可。在離線流程里我會(huì)把這一步做成一個(gè)獨(dú)立腳本每次入庫(kù)前先跑一遍。這個(gè)步驟雖然不復(fù)雜但能顯著減少生成階段“內(nèi)容重復(fù)”的問(wèn)題。5. 向量化的隱藏雷區(qū)與應(yīng)對(duì)方式5.1 維度對(duì)齊最容易忽略的硬性約束向量維度是系統(tǒng)層面的硬約束。如果你在用 768 維的模型生成向量那么所有文本、所有查詢都必須由同一個(gè)模型生成同維度的向量庫(kù)里的索引結(jié)構(gòu)也按照 768 維建立。如果某一天你在查詢側(cè)不小心換了一個(gè)輸出維度不同的模型會(huì)導(dǎo)致什么結(jié)果輕則檢索直接報(bào)錯(cuò)重則因?yàn)闆](méi)有校驗(yàn)邏輯向量被靜默截?cái)嗵畛錂z索結(jié)果毫無(wú)意義。我見(jiàn)過(guò)的最典型的錯(cuò)誤是在 Notebook 里先后加載了兩個(gè)不同維度的模型然后忘記重啟內(nèi)核導(dǎo)致后續(xù)所有查詢都用了錯(cuò)誤的模型。排查了很久才發(fā)現(xiàn)是環(huán)境里的模型實(shí)例沒(méi)切換干凈。規(guī)避方式很簡(jiǎn)單在向量化服務(wù)里增加一個(gè)啟動(dòng)自檢函數(shù)輸出當(dāng)前加載模型的維度并寫(xiě)進(jìn)日志。每次大批量任務(wù)開(kāi)始前先檢查日志確認(rèn)模型和維度一致。5.2 數(shù)據(jù)漂移與更新頻率知識(shí)庫(kù)里的數(shù)據(jù)不是靜止的。企業(yè)制度會(huì)改版產(chǎn)品文檔會(huì)迭代問(wèn)題的熱門程度也在變化。如果你的向量庫(kù)從不更新那系統(tǒng)給你的答案就是“基于三年前的文檔內(nèi)容”生成的。這在問(wèn)答場(chǎng)景里是要命的用戶問(wèn)的是今年才生效的新規(guī)你召回的是已經(jīng)廢止的舊條款。更新頻率如何設(shè)定我的建議是按業(yè)務(wù)數(shù)據(jù)的真實(shí)變化節(jié)奏來(lái)不要拍腦袋。制度類文檔通常每季度或半年修訂一次可以低頻全量重建新聞?lì)?、公告類?nèi)容更新快可以每天做增量。所有更新任務(wù)都要有可觀測(cè)的日志記錄成功條數(shù)和失敗條數(shù)失敗要能定位到具體文檔。千萬(wàn)不要在深夜后臺(tái)任務(wù)彈了個(gè)異常第二天早上日志文件超過(guò) 2GB 才發(fā)現(xiàn)系統(tǒng)已經(jīng)靜默失敗好幾天了。5.3 混合檢索與重排向量不是萬(wàn)能的做問(wèn)答系統(tǒng)久了你會(huì)發(fā)現(xiàn)純向量檢索在某些場(chǎng)景下會(huì)失效。典型場(chǎng)景用戶問(wèn)“2023 年第三季度的營(yíng)收是多少”如果你的文本片段里存在“2023Q3 營(yíng)業(yè)收入 12.7 億元”這種表達(dá)向量模型能勝任但如果知識(shí)庫(kù)里有很多年份和營(yíng)收數(shù)字用戶指定的年份、指標(biāo)需要精確匹配向量檢索就容易模糊。這個(gè)時(shí)候引入關(guān)鍵詞檢索混入向量檢索做成“混合檢索”是業(yè)界非常常見(jiàn)的做法。向量負(fù)責(zé)召回語(yǔ)義相關(guān)的候選關(guān)鍵詞負(fù)責(zé)保證精確詞不丟兩者結(jié)果合并再交給重排模型Reranker做精排。這套體系里的重排環(huán)節(jié)非常值得重視。重排模型本質(zhì)上是一個(gè)小型的交叉編碼器它能把用戶問(wèn)題和候選文本拼在一起逐對(duì)計(jì)算出更精細(xì)的相關(guān)性分?jǐn)?shù)。因?yàn)橛辛酥嘏拍P驮谧詈蟀殃P(guān)前排的向量召回哪怕漏掉或誤捕也還有一次糾正機(jī)會(huì)。說(shuō)句實(shí)在話如果你受限于算力只能加強(qiáng)一個(gè)環(huán)節(jié)我會(huì)優(yōu)先加強(qiáng)重排而不是盲目追求最頂級(jí)的 Embedding 模型。提示混合檢索的重排階段建議單獨(dú)做一次詳細(xì)的評(píng)測(cè)。不要只看 Top1 準(zhǔn)確率還要關(guān)注召回率是否找到正確答案和 MRR正確答案排名是否靠前。這兩個(gè)指標(biāo)在問(wèn)答場(chǎng)景里比 Top1 更能反映系統(tǒng)的真實(shí)可用性。5.4 開(kāi)源模型與商業(yè) API 的混用禁忌最后說(shuō)一個(gè)很多人踩過(guò)的坑不要在同一個(gè)系統(tǒng)里混用兩套來(lái)源不同的向量模型。常見(jiàn)的錯(cuò)誤是存量數(shù)據(jù)用的是開(kāi)源模型 A新入庫(kù)的數(shù)據(jù)圖省事用了商業(yè) API 模型 B??雌饋?lái)都是“768 維向量”但背后的語(yǔ)義空間根本不是同一個(gè)坐標(biāo)系檢索結(jié)果自然是一團(tuán)亂麻。如果一定要從開(kāi)源模型切換到商業(yè) API請(qǐng)選擇流量低谷時(shí)段做一次全量重建并在切換完成后抽查幾個(gè)典型問(wèn)題的檢索結(jié)果確認(rèn)排序質(zhì)量沒(méi)有明顯回退。這事不復(fù)雜但不主動(dòng)做等到用戶抱怨“答案變差了”再排查就非常被動(dòng)了。6. 用一套標(biāo)準(zhǔn)評(píng)測(cè)腳本驗(yàn)收你的向量化質(zhì)量選型時(shí)“拿真實(shí)數(shù)據(jù)實(shí)測(cè)”到底怎么測(cè)才有效我分享一套自己在項(xiàng)目里反復(fù)使用的評(píng)測(cè)腳本思路你拿到自己環(huán)境里改改就能用。第一步準(zhǔn)備一組評(píng)測(cè)集。從知識(shí)庫(kù)里挑 50 到 100 個(gè)具有代表性的真實(shí)問(wèn)題每個(gè)問(wèn)題人工標(biāo)注 2 到 3 條“正確答案應(yīng)該出現(xiàn)在哪一個(gè)文檔片段”。注意這里標(biāo)注的是文檔片段而不是文檔標(biāo)題因?yàn)槠螌蛹?jí)才能精準(zhǔn)反映向量檢索的能力。第二步腳本自動(dòng)對(duì)所有文檔做向量化儲(chǔ)存在內(nèi)存向量列表里然后對(duì)每個(gè)問(wèn)題做同樣的向量化計(jì)算問(wèn)題向量與所有文檔向量的余弦相似度按從高到低排序。第三步統(tǒng)計(jì)三個(gè)指標(biāo)指標(biāo)含義合格參考值RecallK正確答案是否出現(xiàn)在前 K 條結(jié)果里建議 K10追求 ≥ 85%MRR正確答案排名的倒數(shù)均值追求 ≥ 0.7平均耗時(shí)單次檢索所需時(shí)間視數(shù)據(jù)量通常 200ms第四步對(duì)比不同候選模型在同樣數(shù)據(jù)上的指標(biāo)結(jié)果。不要只看平均值還要抽樣看失敗案例為什么這個(gè)問(wèn)題的答案沒(méi)有被召回是因?yàn)榍袎K時(shí)把答案切散了還是模型本身對(duì)這個(gè)語(yǔ)義的表達(dá)能力不足一個(gè)模型在 100 個(gè)問(wèn)題上表現(xiàn)好在另外 20 個(gè)問(wèn)題上全軍覆沒(méi)你要弄明白那 20 個(gè)問(wèn)題有什么共性——往往是業(yè)務(wù)場(chǎng)景里的高頻特殊表達(dá)值得單獨(dú)調(diào)優(yōu)。我在做這套評(píng)測(cè)時(shí)印象最深的一次經(jīng)歷是某模型整體分?jǐn)?shù)比另一個(gè)模型高出 3%但當(dāng)我把數(shù)據(jù)按業(yè)務(wù)線拆開(kāi)對(duì)比時(shí)發(fā)現(xiàn)這 3% 的優(yōu)勢(shì)主要集中在 A 業(yè)務(wù)線上而 B 業(yè)務(wù)線反而明顯變差。這促使我最終采用了“按業(yè)務(wù)線分庫(kù) 各用各的模型”的方案雖然運(yùn)維成本高了一些但實(shí)際問(wèn)答效果是各個(gè)方向中最穩(wěn)的。這事說(shuō)明任何評(píng)測(cè)都不能只看總分要把數(shù)據(jù)分拆到業(yè)務(wù)維度去觀察否則很容易被平均值迷惑。在這套評(píng)測(cè)體系撐住底線之后你才算把 Embedding 這個(gè)環(huán)節(jié)真正“落地”進(jìn)了企業(yè)級(jí)系統(tǒng)。后續(xù)要做的事情就是把向量化管線接入你整體的索引構(gòu)建流程中讓全量重建、增量更新、模型發(fā)布這些操作都變成可復(fù)用、可觀測(cè)的標(biāo)準(zhǔn)流程。到這一步你的問(wèn)答系統(tǒng)才配得上“企業(yè)級(jí)”這三個(gè)字。