級知識庫與Agent網(wǎng)關(guān)一體化架構(gòu)設(shè)計(jì)與性能優(yōu)化實(shí)戰(zhàn))
1. 生產(chǎn)級知識庫與 Agent 網(wǎng)關(guān)的整體設(shè)計(jì)思路1.1 為什么要把知識庫和 Agent 網(wǎng)關(guān)放在一起做單獨(dú)做一個 RAG 知識庫或者單獨(dú)做一個 Agent 調(diào)度服務(wù)其實(shí)都不算太難。難的是把這兩樣?xùn)|西塞進(jìn)同一個生產(chǎn)環(huán)境里還要保證它們在高并發(fā)、多租戶、長鏈路調(diào)用下不互相拖垮。我最近這段時間主要就在干這件事一邊優(yōu)化知識庫的檢索質(zhì)量一邊把 Agent 網(wǎng)關(guān)的穩(wěn)定性和可觀測性補(bǔ)起來。先說清楚這兩個東西各自是什么。知識庫在這里指的是一個面向大模型應(yīng)用的檢索增強(qiáng)生成系統(tǒng)核心鏈路是文檔入庫、切分、向量化、索引、檢索、重排、拼裝上下文。Agent 網(wǎng)關(guān)則是所有 Agent 請求的統(tǒng)一入口負(fù)責(zé)路由、鑒權(quán)、限流、會話管理、工具調(diào)用編排、模型切換和結(jié)果回傳。把兩者放在一起是因?yàn)閷?shí)際業(yè)務(wù)里 Agent 幾乎必然要查知識庫而知識庫的檢索結(jié)果又直接影響 Agent 的回答質(zhì)量。如果網(wǎng)關(guān)和知識庫各自為政就會出現(xiàn)超時疊加、上下文重復(fù)、權(quán)限穿透、計(jì)費(fèi)混亂這些典型問題。我見過不少團(tuán)隊(duì)的做法是知識庫用一套服務(wù)Agent 用另一套服務(wù)中間靠 HTTP 硬調(diào)。前期 demo 跑得挺歡一上生產(chǎn)就原形畢露。原因很簡單檢索和推理是兩種完全不同的負(fù)載特征。檢索是短平快、高 QPS、對延遲極度敏感推理是長耗時、占顯存、對并發(fā)敏感。你把它們混在一條同步鏈路上任何一個環(huán)節(jié)抖動都會放大成整體雪崩。所以我的整體設(shè)計(jì)思路是網(wǎng)關(guān)做編排和治理知識庫做檢索和召回兩者通過明確的內(nèi)部協(xié)議通信而不是互相直接依賴對方的實(shí)現(xiàn)細(xì)節(jié)。網(wǎng)關(guān)不關(guān)心你用的是向量檢索還是 BM25知識庫也不關(guān)心請求來自哪個 Agent只認(rèn)標(biāo)準(zhǔn)化的檢索請求和返回結(jié)構(gòu)。這樣后面換嵌入模型、換重排策略、加新的檢索通道都不會影響網(wǎng)關(guān)側(cè)的邏輯。1.2 核心鏈路的拆解與職責(zé)邊界把整條鏈路拆開看大概是這么幾段請求進(jìn)入網(wǎng)關(guān)完成鑒權(quán)、租戶識別、限流、會話綁定。網(wǎng)關(guān)根據(jù) Agent 配置決定是否需要檢索需要的話構(gòu)造檢索請求。知識庫執(zhí)行混合檢索返回候選片段和分?jǐn)?shù)。網(wǎng)關(guān)做重排、去重、上下文裁剪拼裝成最終 prompt。調(diào)用大模型處理流式返回記錄 token 消耗和鏈路耗時。結(jié)果回傳同時異步寫入日志和評估樣本。這里面有幾個邊界必須劃清楚。重排放在網(wǎng)關(guān)還是知識庫我糾結(jié)過很久。放知識庫好處是檢索側(cè)可以統(tǒng)一調(diào)優(yōu)壞處是網(wǎng)關(guān)拿不到原始候選做不了跨知識庫的融合。放網(wǎng)關(guān)靈活但會增加網(wǎng)關(guān)的計(jì)算負(fù)擔(dān)。最后我選擇折中知識庫返回帶原始分?jǐn)?shù)的候選集網(wǎng)關(guān)做輕量重排和裁剪重模型重排作為可選步驟放在知識庫側(cè)異步執(zhí)行。這樣既保留了靈活性又不至于讓網(wǎng)關(guān)變成計(jì)算密集型服務(wù)。另一個邊界是會話上下文和檢索上下文的關(guān)系。很多實(shí)現(xiàn)會把歷史對話直接拼進(jìn)檢索 query這在小規(guī)模下沒問題但生產(chǎn)環(huán)境里會導(dǎo)致檢索漂移。我的做法是網(wǎng)關(guān)維護(hù)一個獨(dú)立的 query 改寫層用最近幾輪對話生成檢索用的獨(dú)立 query而不是把原始對話一股腦塞進(jìn)去。這個改寫層可以很輕甚至用規(guī)則加小模型就夠了。1.3 方案選型背后的取舍邏輯選型這塊我踩過不少坑說幾個關(guān)鍵決策。向量庫選型。早期用過內(nèi)存版的 FAISS單機(jī)跑得飛快但一到多副本、持久化、增量更新就露怯。后來換成支持持久化和分布式的主流向量庫代價是運(yùn)維復(fù)雜度上來了。我的建議是如果數(shù)據(jù)量在百萬級以下、更新不頻繁單機(jī)加定期快照完全夠用一旦涉及多租戶隔離和實(shí)時增量就必須上支持命名空間和增量寫入的方案。檢索策略。純向量檢索在語義匹配上強(qiáng)但對專有名詞、編號、代碼符號這類精確匹配很弱。純 BM25 反過來精確匹配強(qiáng)但語義泛化差。所以生產(chǎn)級知識庫基本都要做混合檢索把兩路結(jié)果融合。融合方式我用過兩種一種是加權(quán)分?jǐn)?shù)融合簡單但需要調(diào)權(quán)重另一種是 RRFReciprocal Rank Fusion對分?jǐn)?shù)尺度不敏感工程上更穩(wěn)。實(shí)測下來 RRF 在跨領(lǐng)域數(shù)據(jù)上表現(xiàn)更一致推薦優(yōu)先考慮。Agent 網(wǎng)關(guān)的編排方式。有人喜歡把編排邏輯寫死在代碼里有人喜歡用配置驅(qū)動。我傾向于配置驅(qū)動加插件化工具注冊因?yàn)?Agent 的工具集經(jīng)常變寫死代碼意味著每次加工具都要發(fā)版。配置驅(qū)動的好處是運(yùn)營側(cè)可以自己調(diào)整壞處是需要一套校驗(yàn)機(jī)制防止配置錯誤導(dǎo)致線上事故。2. 知識庫檢索質(zhì)量的核心細(xì)節(jié)與實(shí)操要點(diǎn)2.1 文檔切分最容易被低估的一步很多人把精力全花在換嵌入模型上卻忽略了切分策略。我可以很負(fù)責(zé)任地說切分沒做好換再好的模型也救不回來。切分的核心目標(biāo)是讓每個 chunk 語義自洽同時保留足夠的上下文線索。我常用的策略是結(jié)構(gòu)化切分加語義切分結(jié)合。對于 Markdown、HTML 這類有明確結(jié)構(gòu)的文檔優(yōu)先按標(biāo)題層級切保證每個 chunk 不跨章節(jié)。對于純文本或結(jié)構(gòu)混亂的文檔用固定長度加重疊窗口重疊比例一般取 10% 到 20%。重疊太少會丟上下文太多會導(dǎo)致檢索結(jié)果重復(fù)。這里有個細(xì)節(jié)chunk 大小不是越小越好。小 chunk 檢索精度高但拼裝上下文時容易碎片化模型拿到的信息不完整。大 chunk 上下文完整但檢索時噪聲大。我的經(jīng)驗(yàn)值是中文場景下 300 到 500 字比較均衡英文場景 200 到 400 token。當(dāng)然這要看你的文檔類型技術(shù)文檔可以小一點(diǎn)敘述性文檔可以大一點(diǎn)。還有一個容易被忽略的點(diǎn)是元數(shù)據(jù)保留。每個 chunk 必須帶上來源文檔、章節(jié)路徑、更新時間、權(quán)限標(biāo)簽。這些元數(shù)據(jù)在檢索過濾和結(jié)果展示時至關(guān)重要。我見過有人只存文本不存元數(shù)據(jù)后面想做權(quán)限過濾或者按時間排序時只能重建索引代價極大。2.2 混合檢索的分?jǐn)?shù)融合與參數(shù)調(diào)優(yōu)混合檢索的關(guān)鍵在于怎么把向量分?jǐn)?shù)和 BM25 分?jǐn)?shù)合到一起。這兩路分?jǐn)?shù)的量綱完全不同向量相似度通常在 0 到 1 之間BM25 分?jǐn)?shù)則可能是任意正數(shù)。直接相加是沒有意義的。我推薦用 RRF公式很簡單每個文檔的最終分?jǐn)?shù)等于它在各路結(jié)果中排名的倒數(shù)之和再乘一個平滑常數(shù)。這個方法的妙處在于它只看排名不看原始分?jǐn)?shù)天然規(guī)避了量綱問題。參數(shù)上主要調(diào)那個平滑常數(shù)一般取 60 左右太小會讓頭部結(jié)果過于集中太大則區(qū)分度不夠。如果非要用加權(quán)融合那必須先做分?jǐn)?shù)歸一化。我一般用 min-max 歸一化但要注意歸一化要在同一批候選內(nèi)做不能跨批次否則分?jǐn)?shù)不可比。權(quán)重方面語義檢索為主、關(guān)鍵詞檢索為輔的場景向量權(quán)重給 0.7、BM25 給 0.3 是個不錯的起點(diǎn)。但具體數(shù)值一定要用你自己的評估集去調(diào)別照搬。提示混合檢索的召回?cái)?shù)量要留足余量。比如你最終要 5 個片段那每路至少召回 20 個候選融合后再重排取前 5。召回太少會導(dǎo)致融合空間不足好結(jié)果可能在單路里排名靠后就被丟了。2.3 重排模型的選擇與部署考量重排是提升檢索精度的最后一道關(guān)卡。它的原理是把 query 和候選片段一起送進(jìn)一個交叉編碼器直接輸出相關(guān)性分?jǐn)?shù)。相比向量檢索的雙塔結(jié)構(gòu)交叉編碼器精度更高但速度慢得多所以只能用在候選集上不能用于全量檢索。選型上中文場景我一般用專門的中文重排模型英文場景用通用的多語言重排模型。部署時要注意幾點(diǎn)批處理能顯著提升吞吐把多個候選拼成一個 batch 送進(jìn)去量化能降低顯存占用但會損失一點(diǎn)精度需要評估超時控制必須做重排服務(wù)一旦卡住會拖垮整條鏈路。我的做法是給重排設(shè)一個硬超時比如 200 毫秒超時就降級用融合分?jǐn)?shù)排序。這樣即使重排服務(wù)抖動整體鏈路也不會掛。這個降級邏輯一定要在網(wǎng)關(guān)側(cè)實(shí)現(xiàn)不能指望重排服務(wù)自己保證。2.4 檢索評估沒有評估就沒有優(yōu)化優(yōu)化檢索質(zhì)量最怕的就是憑感覺。你覺得這次改得好可能只是碰巧。所以必須建立評估集。我的做法是從真實(shí)日志里采樣 query人工標(biāo)注每個 query 的相關(guān)片段形成黃金集。然后每次改動都跑一遍看召回率、精確率、MRR 這些指標(biāo)的變化。評估集不用很大幾百條就能看出趨勢。但一定要覆蓋不同類型的 query事實(shí)型、對比型、多跳型、模糊型。只測單一類型會誤導(dǎo)優(yōu)化方向。我吃過這個虧早期評估集全是事實(shí)型 query優(yōu)化后指標(biāo)很好看上線后發(fā)現(xiàn)多跳問題依然一塌糊涂。3. Agent 網(wǎng)關(guān)的實(shí)操實(shí)現(xiàn)與關(guān)鍵環(huán)節(jié)3.1 網(wǎng)關(guān)的核心模塊劃分Agent 網(wǎng)關(guān)我拆成了幾個核心模塊每個模塊職責(zé)單一方便獨(dú)立擴(kuò)展和測試。接入層負(fù)責(zé)協(xié)議解析和連接管理支持流式和一次性返回兩種模式。鑒權(quán)與租戶模塊負(fù)責(zé)識別請求歸屬加載對應(yīng)的配額和權(quán)限策略。路由模塊根據(jù) Agent 配置決定走哪條鏈路是否需要檢索、調(diào)用哪些工具、用哪個模型。編排模塊負(fù)責(zé)多步調(diào)用的狀態(tài)管理包括工具調(diào)用的串行和并行??捎^測模塊負(fù)責(zé)埋點(diǎn)、日志、鏈路追蹤和指標(biāo)上報。這幾個模塊之間通過內(nèi)部事件總線通信而不是直接函數(shù)調(diào)用。這樣做的好處是每個模塊可以獨(dú)立擴(kuò)縮容也方便做灰度。比如我想換一個新的路由策略只需要讓新策略訂閱請求事件逐步切流量即可。3.2 限流與配額的具體實(shí)現(xiàn)生產(chǎn)環(huán)境里限流是保命的東西。我用的方案是令牌桶加滑動窗口組合。令牌桶控制瞬時突發(fā)滑動窗口控制長期速率。兩者結(jié)合既能應(yīng)對突發(fā)流量又能防止長期超用。配額維度上我按租戶、按 Agent、按模型三個維度分別設(shè)限。租戶維度防止單個客戶拖垮整體Agent 維度防止某個 Agent 配置錯誤導(dǎo)致瘋狂調(diào)用模型維度防止昂貴模型被濫用。每個維度的配額都可以動態(tài)調(diào)整不需要重啟服務(wù)。這里有個實(shí)操細(xì)節(jié)限流要在最外層做越早拒絕越好。如果請求已經(jīng)進(jìn)了編排模塊才被限流那前面消耗的資源就白費(fèi)了。所以我在接入層就做第一道粗粒度限流在路由前做第二道細(xì)粒度限流。注意限流的拒絕響應(yīng)要帶明確的錯誤碼和重試建議不要只返回一個 429??蛻舳四玫矫鞔_信息才能做正確的退避重試否則會瘋狂重試加劇擁堵。3.3 工具調(diào)用的編排與錯誤處理Agent 調(diào)用工具是生產(chǎn)環(huán)境里最容易出問題的環(huán)節(jié)。工具可能超時、可能返回格式錯誤、可能部分成功。我的處理原則是每個工具調(diào)用都必須有超時、重試和降級。超時設(shè)置要區(qū)分工具類型。查詢類工具可以給短超時比如 3 秒寫入類工具要給長超時因?yàn)榭赡苌婕笆聞?wù)。重試要區(qū)分冪等性查詢類可以重試寫入類不能盲目重試。降級策略要提前定義比如檢索失敗時是返回空上下文還是返回緩存結(jié)果。并行工具調(diào)用能顯著降低總延遲但要注意依賴關(guān)系。沒有依賴的工具可以并行有依賴的必須串行。我用一個有向無環(huán)圖來描述工具依賴編排模塊按拓?fù)漤樞驁?zhí)行。這樣既保證了正確性又最大化了并行度。3.4 流式返回與背壓處理流式返回是提升用戶體驗(yàn)的關(guān)鍵但也是背壓問題的重災(zāi)區(qū)。模型生成速度快于客戶端消費(fèi)速度時如果不做背壓內(nèi)存會迅速膨脹。我的做法是在網(wǎng)關(guān)和客戶端之間加一個帶緩沖的通道緩沖區(qū)滿了就暫停從模型側(cè)讀取。同時給每個連接設(shè)一個最大緩沖上限超過就斷開并記錄。這樣單個慢客戶端不會影響其他連接。流式場景下還有一個坑是錯誤處理。流已經(jīng)開始返回后如果中途出錯不能簡單地返回錯誤碼因?yàn)?HTTP 狀態(tài)碼已經(jīng)發(fā)出去了。我的做法是在流內(nèi)發(fā)送一個結(jié)構(gòu)化的錯誤事件客戶端解析到這個事件就知道后續(xù)內(nèi)容不可信。這個約定必須在客戶端和服務(wù)端之間提前對齊。4. 常見問題與排查技巧實(shí)錄4.1 檢索質(zhì)量突然下降的排查路徑檢索質(zhì)量下降是最常見也最頭疼的問題。我整理了一套排查順序基本能覆蓋大部分情況。排查項(xiàng)檢查方法常見原因索引是否完整對比文檔總數(shù)和索引條目數(shù)增量更新失敗導(dǎo)致部分文檔缺失嵌入模型是否變更檢查模型版本和配置模型升級后舊向量未重建切分策略是否調(diào)整對比 chunk 數(shù)量和平均長度切分參數(shù)改動導(dǎo)致語義斷裂檢索參數(shù)是否被改檢查權(quán)重和召回?cái)?shù)量運(yùn)營側(cè)誤改配置數(shù)據(jù)分布是否漂移抽樣看新入庫文檔類型新業(yè)務(wù)文檔風(fēng)格差異大排查時一定要先看指標(biāo)再看日志。指標(biāo)能告訴你問題出在哪個環(huán)節(jié)日志能告訴你具體是什么。我見過有人一上來就翻日志翻半天沒頭緒其實(shí)指標(biāo)上早就顯示是召回率掉了。4.2 網(wǎng)關(guān)超時與雪崩的預(yù)防網(wǎng)關(guān)超時往往不是單一原因而是多個環(huán)節(jié)延遲疊加。我的預(yù)防策略是全鏈路超時預(yù)算。給整條鏈路設(shè)一個總超時然后按環(huán)節(jié)分配預(yù)算每個環(huán)節(jié)的超時必須小于剩余預(yù)算。這樣任何環(huán)節(jié)超時都不會導(dǎo)致整體超時。雪崩預(yù)防的核心是隔離和熔斷。不同租戶、不同 Agent 之間要資源隔離一個出問題不影響其他。熔斷器要按依賴維度設(shè)置某個下游服務(wù)錯誤率超過閾值就快速失敗給它恢復(fù)時間。還有一個容易被忽略的點(diǎn)是連接池管理。下游服務(wù)的連接池如果配置不當(dāng)高并發(fā)時會大量等待。我一般把連接池大小設(shè)為預(yù)期并發(fā)的 1.5 倍并設(shè)置合理的獲取超時。4.3 上下文超長的處理技巧上下文超長是 RAG 場景的經(jīng)典問題。檢索回來的片段加上歷史對話很容易超過模型窗口。我的處理順序是先裁剪歷史對話保留最近幾輪和摘要再對檢索片段做去重和壓縮最后如果還超就按相關(guān)性截?cái)?。去重這塊有個技巧用語義去重而不是字符串去重。兩個片段可能表述不同但信息重復(fù)字符串比對發(fā)現(xiàn)不了。我一般用嵌入相似度做粗篩相似度超過閾值的只保留分?jǐn)?shù)高的那個。壓縮方面可以用小模型對片段做摘要但要注意摘要可能丟關(guān)鍵信息。我的做法是只對低相關(guān)性的片段做壓縮高相關(guān)性的保留原文。4.4 多租戶數(shù)據(jù)隔離的實(shí)現(xiàn)要點(diǎn)多租戶隔離做不好會出大事故。我的原則是物理隔離優(yōu)先邏輯隔離兜底。能物理隔離的獨(dú)立索引、獨(dú)立存儲就物理隔離成本高但安全。不能物理隔離的必須在每個查詢里強(qiáng)制帶上租戶過濾條件而且這個條件不能由業(yè)務(wù)代碼傳入必須由網(wǎng)關(guān)從鑒權(quán)信息里提取。這里有個坑向量檢索的過濾是在檢索后做的。如果先檢索全量再過濾租戶不僅性能差還可能因?yàn)楹蜻x集被其他租戶占滿導(dǎo)致本租戶結(jié)果缺失。正確做法是把租戶過濾下推到檢索層讓向量庫在檢索時就只搜本租戶的數(shù)據(jù)。5. 性能優(yōu)化與成本控制的實(shí)戰(zhàn)經(jīng)驗(yàn)5.1 緩存策略的分層設(shè)計(jì)緩存是降本增效的利器但要用對地方。我做了三層緩存。第一層是檢索結(jié)果緩存key 是 query 加租戶加過濾條件適合高頻重復(fù)查詢。第二層是嵌入緩存key 是文本內(nèi)容哈希避免相同文本重復(fù)計(jì)算嵌入。第三層是模型響應(yīng)緩存只對確定性高的場景啟用比如固定模板的問答。緩存失效策略要區(qū)分。檢索結(jié)果緩存可以設(shè)短 TTL比如 5 分鐘因?yàn)橹R庫更新不頻繁。嵌入緩存可以長期有效因?yàn)槲谋静蛔兦度刖筒蛔?。模型響?yīng)緩存要謹(jǐn)慎涉及實(shí)時數(shù)據(jù)的場景不能緩存。5.2 批處理與異步化的收益很多操作可以批處理來提升吞吐。嵌入計(jì)算可以攢一批一起算比逐條算快好幾倍。日志寫入可以批量提交減少 IO 次數(shù)。指標(biāo)上報可以聚合后定時發(fā)送。異步化方面非關(guān)鍵路徑的操作全部異步。比如日志、評估樣本采集、計(jì)費(fèi)統(tǒng)計(jì)這些都不應(yīng)該阻塞主鏈路。我用一個內(nèi)部隊(duì)列把這些操作解耦主鏈路只管返回結(jié)果。但異步化要注意可靠性。隊(duì)列可能丟消息所以關(guān)鍵操作要有補(bǔ)償機(jī)制。我的做法是異步操作先寫本地日志再由后臺任務(wù)消費(fèi)消費(fèi)失敗可以重放。5.3 模型調(diào)用的成本優(yōu)化模型調(diào)用是最大的成本項(xiàng)。優(yōu)化手段有幾個用小模型做路由和改寫只把真正需要大模型的請求交給大模型控制輸出長度通過 prompt 約束讓模型簡潔回答復(fù)用 KV 緩存相同前綴的請求可以復(fù)用緩存降低計(jì)算量。還有一個技巧是分級響應(yīng)。簡單問題用便宜模型復(fù)雜問題才升級到貴模型。判斷復(fù)雜度可以用規(guī)則也可以用一個小分類器。實(shí)測下來能省不少成本而且用戶體驗(yàn)沒有明顯下降。6. 可觀測性建設(shè)與線上問題定位6.1 關(guān)鍵指標(biāo)的定義與采集可觀測性不是日志越多越好而是要采集對的指標(biāo)。我關(guān)注的核心指標(biāo)分幾類。延遲類端到端延遲、檢索延遲、模型首 token 延遲、模型總生成延遲。質(zhì)量類檢索召回率、重排命中率、回答采納率。成本類token 消耗、模型調(diào)用次數(shù)、緩存命中率。穩(wěn)定性類錯誤率、超時率、熔斷次數(shù)。這些指標(biāo)要按租戶、按 Agent、按模型維度分別聚合否則出了問題定位不到具體范圍。采集頻率上延遲和錯誤率要秒級質(zhì)量和成本可以分鐘級。6.2 鏈路追蹤的落地方式鏈路追蹤能讓你看到單個請求在各個環(huán)節(jié)的耗時。我用的是標(biāo)準(zhǔn)的 trace 加 span 模型每個環(huán)節(jié)一個 span記錄開始時間、結(jié)束時間、狀態(tài)和關(guān)鍵屬性。落地時要注意采樣策略。全量采集成本太高我一般對正常請求采樣 1%對錯誤請求全量采集。這樣既能控制成本又不會漏掉問題請求。span 的屬性要包含足夠信息用于定位比如租戶 ID、Agent ID、檢索到的片段數(shù)、模型名稱、token 數(shù)。但要注意不要記錄敏感內(nèi)容用戶 query 和文檔內(nèi)容要做脫敏或哈希。6.3 告警規(guī)則的設(shè)計(jì)原則告警設(shè)計(jì)不好會導(dǎo)致告警疲勞真正的問題反而被淹沒。我的原則是告警必須可行動。每條告警都要有明確的處理動作沒有處理動作的告警不如不設(shè)。告警閾值要基于歷史數(shù)據(jù)動態(tài)調(diào)整而不是拍腦袋定死值。比如延遲告警可以用 P99 的歷史分位數(shù)作為基線超過基線一定倍數(shù)才告警。這樣能適應(yīng)流量的自然波動。告警分級也很重要。P0 告警要能叫醒人P1 告警工作時間處理P2 告警只記錄不通知。分級標(biāo)準(zhǔn)要提前和團(tuán)隊(duì)對齊避免所有告警都當(dāng) P0 處理。7. 我在這套系統(tǒng)上踩過的坑與個人體會說幾個印象深刻的坑。第一個是嵌入模型升級沒有重建索引。當(dāng)時覺得新模型更好直接切了結(jié)果檢索質(zhì)量暴跌。原因是新舊模型的向量空間不兼容query 用新模型編碼文檔還是舊模型的向量根本對不上。后來老老實(shí)實(shí)全量重建停機(jī)了幾個小時。教訓(xùn)是模型升級必須配套索引重建而且要提前規(guī)劃好重建期間的降級方案。第二個是網(wǎng)關(guān)的會話狀態(tài)存在了本地內(nèi)存。單機(jī)測試沒問題一擴(kuò)容就出問題同一個會話的請求被路由到不同實(shí)例狀態(tài)對不上。后來改成外部存儲雖然多了一次 IO但換來了水平擴(kuò)展能力。這個坑很典型任何有狀態(tài)的東西都不能存在本地內(nèi)存里。第三個是限流閾值設(shè)得太死。上線初期按預(yù)估流量設(shè)了閾值結(jié)果業(yè)務(wù)增長后頻繁觸發(fā)限流用戶體驗(yàn)很差。后來改成動態(tài)閾值根據(jù)系統(tǒng)負(fù)載自動調(diào)整才解決了問題。限流的目的是保護(hù)系統(tǒng)不是限制業(yè)務(wù)這個平衡要把握好。我個人在實(shí)際操作中的體會是生產(chǎn)級系統(tǒng)的難點(diǎn)從來不在單個技術(shù)點(diǎn)而在各個組件之間的協(xié)作和邊界。知識庫和網(wǎng)關(guān)各自做好不難難的是它們之間的協(xié)議設(shè)計(jì)、錯誤傳播、超時傳遞、權(quán)限穿透。這些邊界問題在 demo 階段暴露不出來只有上了生產(chǎn)、有了真實(shí)流量才會顯現(xiàn)。所以我的建議是在架構(gòu)設(shè)計(jì)階段就要把這些邊界想清楚定義好協(xié)議和降級策略而不是等出了問題再補(bǔ)。最后再分享一個小技巧給每個關(guān)鍵鏈路都準(zhǔn)備一個開關(guān)。檢索可以關(guān)重排可以關(guān)工具調(diào)用可以關(guān)。出問題時能快速降級到最簡鏈路先保住可用性再慢慢排查。這個開關(guān)機(jī)制在幾次線上故障里救過我比任何復(fù)雜的容錯邏輯都管用。