建指南:從向量化到Agent集成)
1. 為什么 Agent 場景里先打 RAG 基礎(chǔ)做 Lang-Agent 系列做到第 5 篇前面已經(jīng)聊過 Agent 的基本框架、工具調(diào)用、記憶管理。這一篇開始進入一個繞不開的核心環(huán)節(jié)RAG也就是檢索增強生成。我在實際項目里見過不少同學(xué)一上來就讓 Agent 直接對接大模型 API對話一深就開始胡說八道問一個內(nèi)部文檔里寫得很清楚的問題模型愣是給你編出一個不存在的答案。這不是模型智商不夠而是它手里根本沒有那份資料。RAG 要解決的就是這個問題讓模型在回答之前先到知識庫里查資料再基于查到的內(nèi)容做推理。那什么是“知識庫”說白了就是你自己的私有資料集合可能是產(chǎn)品手冊、技術(shù)文檔、客服話術(shù)、歷史工單、實驗記錄等等。這些內(nèi)容模型在訓(xùn)練時沒見過也不可能見過因為它們要么是內(nèi)部數(shù)據(jù)要么是實時更新的動態(tài)內(nèi)容。RAG 的做法是先把這些資料切成小塊做向量化存進向量庫等用戶提問時把問題也向量化再從庫里檢索最相關(guān)的內(nèi)容把這些內(nèi)容塞進提示詞讓模型照著資料回答。整個過程像極了開卷考試模型不需要把書背下來只需要知道翻哪一頁。這篇內(nèi)容適合正在搭建自己的知識問答系統(tǒng)、想把 Agent 接進業(yè)務(wù)數(shù)據(jù)的開發(fā)者也適合剛接觸 RAG、被各種概念繞暈的入門者。我會按照一條完整的構(gòu)建路徑來講先做數(shù)據(jù)準(zhǔn)備再講向量化和檢索鏈路最后把知識庫封裝成 Agent 的工具并分享我實際踩過的坑和評測方法。讀完之后你應(yīng)該能獨立搭出一個能跑、能測、能迭代的 RAG 知識庫而不是只看了一堆概念。2. 知識庫構(gòu)建前的數(shù)據(jù)準(zhǔn)備2.1 先想清楚資料邊界再動手寫代碼很多人搭建 RAG 知識庫的第一步就是寫腳本批量切片導(dǎo)入向量庫這其實順序反了。我見過一個項目團隊把幾千份格式雜亂的舊文檔一股腦灌進去結(jié)果用戶問任何問題都能召回一堆無關(guān)片段回答質(zhì)量還不如直接問大模型。問題根源在于知識庫沒有邊界數(shù)據(jù)質(zhì)量參差不齊臟數(shù)據(jù)比沒有數(shù)據(jù)更可怕。動手之前你應(yīng)該先做三件事圈定知識范圍、確認數(shù)據(jù)格式、估算體量。知識范圍意味著明確哪些文檔允許被檢索哪些必須排除比如保密協(xié)議、內(nèi)部閑聊記錄、未定稿的方案都不該進知識庫。數(shù)據(jù)格式?jīng)Q定了你的解析管線怎么設(shè)計PDF、Word、Markdown、HTML、表格各自有不同的處理方式。體量估算則影響向量庫選型和檢索性能設(shè)計幾百條文檔和幾百萬條文檔是完全不同的架構(gòu)思路。我習(xí)慣在第一步輸出一份簡單的資料清單字段包括來源、文檔類型、負責(zé)人、更新頻率、是否可公開檢索。這份清單不需要多復(fù)雜但它能在后續(xù)迭代時幫你快速定位問題比如某個回答不準(zhǔn)你能立刻查到是哪份來源在拖后腿。2.2 清洗和解析決定知識庫質(zhì)量的下限數(shù)據(jù)清洗是枯燥但決定成敗的環(huán)節(jié)。以最常見的 PDF 為例文字型 PDF 直接用解析庫抽取文本掃描件需要 OCR雙層 PDF 要小心重復(fù)抽取。解析完不要急著切片先做一輪噪聲清理去掉頁眉頁腳、目錄頁碼、表格線符號、異常換行把全角半角統(tǒng)一。我常用的做法是正則清洗加人工抽檢清洗規(guī)則逐項加注釋方便后續(xù)排查。清洗之后還有個容易忽略的點保留結(jié)構(gòu)信息。純文本會丟失標(biāo)題層級、列表關(guān)系、加粗強調(diào)而這些信息對后續(xù)切分和檢索非常有價值。所以我在處理時會給段落打上元數(shù)據(jù)標(biāo)簽比如“標(biāo)題”“正文”“代碼塊”“表格”并把來源文檔名、章節(jié)路徑、頁碼一起記下來。這樣后期檢索不僅能召回內(nèi)容還能告訴用戶“這句話來自哪份文檔的哪個章節(jié)”極大地提升可信度。做個小提醒清洗過度也不行。我試過把所有標(biāo)點都清掉結(jié)果檢索時語義發(fā)生變化召回質(zhì)量反而下降。清洗的原則是去掉干擾、保留語義而不是把文本壓得越干凈越好。2.3 分塊策略切片大小直接影響檢索命中率分塊是 RAG 最容易被低估的環(huán)節(jié)。同樣一份文檔分塊大小不同檢索效果天差地別。塊太大向量里混入太多無關(guān)信息檢索精度下降塊太小語義被切斷模型無法理解上下文。我在實踐中通常從 300 到 800 字中文場景起步根據(jù)文檔類型調(diào)整并且設(shè)置 50 到 100 字的重疊保證相鄰切塊之間的語義連續(xù)性。分塊不是單純按字符數(shù)硬切我更推薦先按文檔結(jié)構(gòu)切。比如一個章節(jié)標(biāo)題下是一段完整的論述那就整段作為一個候選塊如果段落過長再按句號或空行二次切分。這么做的好處是保留自然語義邊界模型拿到的每一塊都是相對完整的論述而不是從句子中間硬掰斷的文字。這里給出一個簡單的分塊思路示例def split_document_by_structure(text, max_chunk500, overlap80): # 偽代碼先按結(jié)構(gòu)分割再按長度合并 sections split_by_heading(text) chunks [] buffer for section in sections: buffer section while len(buffer) max_chunk: cut_idx find_nearest_boundary(buffer, max_chunk) chunks.append(buffer[:cut_idx]) buffer buffer[cut_idx - overlap:] if buffer: chunks.append(buffer) return chunks確定分塊參數(shù)后不要拍腦袋就定而是用一組有代表性的測試問題反復(fù)驗證。我在后面評測章節(jié)會詳細說怎么做這里先記住一個原則分塊策略服務(wù)于檢索效果而不是服務(wù)于存儲容量。3. 向量化與檢索鏈路搭建3.1 嵌入模型怎么選效果和成本的平衡知識庫準(zhǔn)備好之后下一步就是把文本變成向量。嵌入模型的選擇直接影響檢索效果但很多人容易走極端要么直接調(diào)一大型模型的接口效果不錯但成本高尤其數(shù)據(jù)量大時非常燒錢要么本地跑一個小模型快是快語義理解一塌糊涂。我的建議是分場景對待。如果你的知識庫內(nèi)容接近日??谡Z比如客服對話、郵件、聊天記錄選一個通用性強、支持中文的嵌入模型就夠用。如果知識庫專業(yè)術(shù)語密集比如醫(yī)學(xué)、法律、工程文檔那就要考察模型在垂直領(lǐng)域的表現(xiàn)最好用你自己的數(shù)據(jù)做對照測試而不是只看榜單分數(shù)。嵌入模型還需要注意輸入長度限制。有些模型單次最多編碼 512 個 token如果你的分塊超過了這個長度向量化時會被截斷等于后半段內(nèi)容白切了。所以分塊參數(shù)必須與嵌入模型的編碼上限匹配這是我踩過的一個實際教訓(xùn)一開始分塊設(shè)成 1000 字嵌入模型上限 512 token結(jié)果長文檔后半截語義丟失檢索出來的內(nèi)容總是缺尾。3.2 向量庫選型從原型到生產(chǎn)向量庫的選擇取決于你的規(guī)模和使用場景。做原型驗證時一個小型本地向量庫就足夠了數(shù)據(jù)量在幾十萬條以內(nèi)檢索性能完全夠用。到了生產(chǎn)環(huán)境如果涉及多端訪問、高并發(fā)、權(quán)限隔離就要考慮更完整的數(shù)據(jù)庫方案但部署和運維成本也更高。我整理了一份簡單的選型對照場景建議方案理由個人學(xué)習(xí)、原型驗證輕量級本地向量庫零配置快速跑通流程中小團隊內(nèi)部工具獨立向量庫服務(wù)支持多端訪問便于管理大規(guī)模生產(chǎn)環(huán)境分布式向量檢索集群性能、擴展性、權(quán)限控制更完善已有關(guān)系型數(shù)據(jù)庫數(shù)據(jù)庫內(nèi)置向量檢索能力減少額外組件統(tǒng)一維護這里的核心不是讓你照搬而是提醒你向量庫不等于最終方案它是檢索鏈路的一個組件。你的數(shù)據(jù)權(quán)限邏輯、更新機制、備份策略都需要圍繞它設(shè)計。比如知識庫內(nèi)容經(jīng)常更新那向量庫必須支持按文檔 ID 刪除和增量插入否則舊數(shù)據(jù)殘留會導(dǎo)致回答過時。3.3 召回策略向量檢索之外還要做什么單純向量檢索在實踐中往往不夠尤其是知識庫內(nèi)容龐雜時。向量檢索擅長捕捉語義相似但它不理解精確匹配也不懂關(guān)鍵詞的重要性。比如用戶查找“設(shè)備型號 ABC-200 的故障代碼 E01”向量檢索可能召回一堆關(guān)于“設(shè)備故障”的泛泛內(nèi)容而精確匹配“ABC-200”“E01”的記錄反而沒排到前面。我現(xiàn)在的檢索鏈路通常是混合召回向量檢索負責(zé)語義召回關(guān)鍵詞檢索負責(zé)精確命中最后把兩路結(jié)果做融合。融合時需要設(shè)定權(quán)重我的習(xí)慣是語義為主、關(guān)鍵詞為輔比例大約 7:3然后加權(quán)排序取 Top N。召回之后還可以加一層重排用一個更強的模型對候選塊做精細打分把最相關(guān)的內(nèi)容排到最前。重排的價值在知識庫內(nèi)容豐富時特別明顯它能顯著提升最終答案的相關(guān)性。檢索參數(shù)的調(diào)節(jié)也很講究召回數(shù)量 top_k 不是越大越好因為塞進提示詞的上下文過多模型容易分不清主次反而降低回答質(zhì)量。我的初始值通常是 top_k 4 到 6重排后取前 3 到 4 個塊進入提示詞。具體數(shù)值要根據(jù)評測結(jié)果迭代沒有標(biāo)準(zhǔn)答案。4. 把 RAG 接進 Agent 工作流4.1 工具化封裝讓 Agent 主動去查書RAG 鏈路本身可以獨立工作但放進 Agent 場景后它的價值會放大。原因在于 Agent 的核心能力是規(guī)劃和決策它可以判斷當(dāng)前問題是否需要查知識庫需要查哪個知識庫查完之后怎么用結(jié)果繼續(xù)推理。這意味著知識庫不再是被動被調(diào)用的接口而是 Agent 的一個主動決策分支。具體做法是把檢索能力封裝成一個工具函數(shù)注冊給 Agent。工具函數(shù)通常接收一個查詢字符串返回檢索到的相關(guān)內(nèi)容片段。比如def search_knowledge_base(query_text: str): # 偽代碼調(diào)用檢索鏈路返回結(jié)果 candidates hybrid_retrieve(query_text, top_k6) reranked rerank(query_text, candidates, top_k3) return format_for_prompt(reranked)函數(shù)本身不難寫難在工具的描述信息。Agent 依賴工具描述來判斷“什么時候該用這個工具”描述寫得太籠統(tǒng)它遇到數(shù)學(xué)題也會跑去翻知識庫描述太具體它又容易漏掉該觸發(fā)的場景。我常用的寫法是在描述里包含這個知識庫覆蓋哪些領(lǐng)域、適合回答哪類問題、不適合回答哪類問題、返回結(jié)果是什么格式。這套描述需要反復(fù)打磨要和檢索鏈路一起調(diào)優(yōu)。4.2 提示詞組裝上下文不是越多越好Agent 查到結(jié)果后如何把這些片段正確地喂給大模型也是一門學(xué)問。我會在提示詞里明確劃分區(qū)域檢索到的資料放一個區(qū)塊用戶原始問題放一個區(qū)塊模型的任務(wù)是“優(yōu)先依據(jù)資料回答資料不足時明確說明”。這種顯式引導(dǎo)比直接把資料堆在對話上下文里要穩(wěn)定得多。同時要保留引用信息。每條檢索結(jié)果都攜帶來源文檔、章節(jié)、頁碼模型在回答時應(yīng)當(dāng)把這些信息一并輸出。這樣做有幾個好處用戶能驗證答案、你能定位知識庫問題、模型在知道會被溯源的狀態(tài)下也會更謹慎地組織語言。我的輸出格式通常是“回答正文”加“參考來源列表”來源列表用編號與正文中的引用標(biāo)記對應(yīng)。上下文溢出是我經(jīng)??吹降牧硪粋€問題。知識庫返回 6 個塊每個塊 500 字加起來 3000 字再加上對話歷史和 Agent 的推理過程很快就把上下文窗口擠滿。模型注意力分散回答質(zhì)量直線下降。我的處理辦法是控制進入提示詞的塊數(shù)優(yōu)先返回最相關(guān)的 3 個塊級去重多個檢索結(jié)果來自同一段落時只保留完整度最高的對話歷史做摘要壓縮避免每輪問答都全量塞入。4.3 多知識庫路由涉及 Agent 權(quán)限與偏見實際業(yè)務(wù)里往往不止一個知識庫比如產(chǎn)品知識庫、技術(shù)文檔庫、運營手冊庫。如果把這些合并成一個向量庫檢索時容易互相干擾。更合理的做法是分庫存儲給 Agent 多個工具讓它在規(guī)劃階段先判斷該查哪個庫。分庫的路由邏輯可以放在 Agent 里由模型自行決策也可以在工具層寫規(guī)則。我的實踐是當(dāng)知識庫主題差異明顯時讓模型做路由當(dāng)主題存在交叉時規(guī)則路由更可靠。比如用戶問“這個功能定價多少”如果產(chǎn)品庫和銷售政策庫都可能覆蓋我會在工具描述中寫清楚兩個庫的邊界再讓模型綜合判斷。邊界模糊時寧可多查一個庫也不能只查一個庫而漏掉關(guān)鍵信息。5. 落地效果評測與常見問題排查5.1 評測不能只看“回答得像不像”RAG 系統(tǒng)的評測比一般模型評測更復(fù)雜因為效果取決于兩個環(huán)節(jié)檢索準(zhǔn)不準(zhǔn)生成對不對。我最初犯的錯是把兩者混在一起只看最終回答好不好結(jié)果出問題后不知道是檢索召回差還是模型生成差。后來我把評測拆成兩層先評檢索層再評生成層。檢索層評測的核心指標(biāo)是召回率和排序質(zhì)量。我準(zhǔn)備一組測試問題每個問題對應(yīng)知識庫里的一個標(biāo)準(zhǔn)答案片段跑一遍檢索鏈路看正確答案有沒有出現(xiàn)在返回結(jié)果里、排在什么位置。生成層評測則關(guān)注三個點回答是否忠于檢索結(jié)果忠實性、是否覆蓋用戶問題的全部要點完整性、引用是否正確可溯源。忠實性這一項我格外看重因為它直接對應(yīng)幻覺問題——模型有沒有編造知識庫里沒有的信息。評測集不用一開始就建得很大我建議先從 50 到 100 條問題起步覆蓋各知識庫核心主題再逐漸補充邊緣場景。關(guān)鍵是記錄每次迭代的評測結(jié)果否則你改了分塊參數(shù)、換了檢索權(quán)重效果是好是壞全靠感覺那就沒法持續(xù)優(yōu)化了。5.2 踩坑實錄空召回、上下文溢出、答案與資料矛盾講幾個我實際遇到過、排查了很久的問題。第一個是空召回。某個特定問題在知識庫里明明有對應(yīng)內(nèi)容但檢索返回為空或相關(guān)度極低。排查步驟是先檢查問題本身的表述是否與文檔術(shù)語差異過大。比如知識庫里寫的是“退款到賬周期”用戶問的是“錢啥時候退回來”語義相似但用詞完全不同如果嵌入模型一般召回就失敗了。解決辦法一是換更強的嵌入模型二是在檢索前加查詢改寫由模型把口語化問題轉(zhuǎn)寫成文檔術(shù)語再檢索。第二個是上下文溢出。這個問題我之前提過典型癥狀是對話進行到后面模型開始忽略檢索到的資料或者回答變得啰嗦且偏離主題。這通常是上下文窗口被占滿關(guān)鍵資料被“擠”出去了。解決思路是優(yōu)先保證最高優(yōu)先級的內(nèi)容最新檢索結(jié)果和當(dāng)前問題永遠在窗口內(nèi)歷史對話做壓縮。第三個是答案與資料矛盾。模型檢索到正確的資料但回答時依然堅持它自己的“想法”。這種情況最容易出現(xiàn)在有爭議性或模型預(yù)訓(xùn)練知識很強的領(lǐng)域。比如知識庫里明確規(guī)定某流程不允許某個操作但模型覺得另一個做法更合理就自作主張給了錯誤答案。應(yīng)對方法是在提示詞中加強約束把“資料說不行就不行”作為硬性指令并在輸出解析時檢查回答與檢索結(jié)果的一致性發(fā)現(xiàn)矛盾時觸發(fā)重新生成。5.3 迭代優(yōu)化節(jié)奏RAG 項目不是一次性搭完就結(jié)束的它更像一個需要持續(xù)維護的服務(wù)。我現(xiàn)在的節(jié)奏是每次修改后先跑評測集對比前后效果每兩周補充新的測試問題每次用戶反饋出問題都記錄在案歸入評測集。通過這種方式知識庫的質(zhì)量會隨著時間不斷提升而不是越跑越偏。具體的調(diào)參順序我一般先調(diào)數(shù)據(jù)層的分塊參數(shù)再調(diào)檢索層的 top_k 和權(quán)重最后才考慮換模型。原因是從底層往上層調(diào)每次改動的影響更可控。如果一上來就換嵌入模型效果變化了也不知道是誰的功勞。6. 兩個進階方向幫你把 RAG 做得更順手如果基礎(chǔ)鏈路已經(jīng)跑通我會建議你在兩個方向上做增強。第一個方向是查詢改寫和意圖擴展。用戶的問題往往不夠清晰直接把原句拿去檢索效果有限??梢宰?Agent 在檢索之前先做一次查詢分析識別關(guān)鍵詞、補全簡寫、補充同義詞。比如“上次說的連接問題”這種模糊指代需要結(jié)合對話上下文改寫為“設(shè)備連接不穩(wěn)定如何排查”再去檢索。我的經(jīng)驗是查詢改寫增加的一次模型調(diào)用非常值它能明顯提升召回精度。第二個方向是知識庫的自動更新與版本管理。知識庫內(nèi)容會變但向量庫里的舊數(shù)據(jù)不會自動消失。如果不做版本管理就會出現(xiàn)新舊文檔內(nèi)容沖突回答隨檢索結(jié)果隨機搖擺。我建議給文檔加版本號和生效時間檢索時優(yōu)先返回有效版本舊版本數(shù)據(jù)進入歸檔庫不參與默認檢索。這樣既能保證回答與當(dāng)前事實一致也保留了追溯歷史信息的通道。這兩個方向都建立在基礎(chǔ)鏈路已經(jīng)穩(wěn)定可控的前提之上。先把檢索、生成、評測這條主線打磨扎實再往這兩個方向擴展會比較從容。7. 一些個人經(jīng)驗最后分享三點我在實操中反復(fù)驗證過的經(jīng)驗希望能幫你少走彎路。第一日志記錄比什么都重要。RAG 鏈路涉及的環(huán)節(jié)多任何一個環(huán)節(jié)出問題如果沒有日志只能靠猜。我現(xiàn)在的做法是對每次查詢記錄原始問題、改寫后的問題、檢索返回的塊 ID 和分數(shù)、重排后的排序、進入提示詞的最終內(nèi)容、模型回答、引用來源。這些日志是排查問題、優(yōu)化效果的第一手素材。第二參數(shù)調(diào)整要一次只動一個變量。分塊大小、重疊長度、top_k、重排模型、提示詞措辭任何一個變化都可能影響最終效果。同時改多個參數(shù)即便效果變好你也不知道哪個改動起了作用。我吃過這個虧后來強迫自己一次只動一個變量效果變化立刻能歸因。第三先跑通最小閉環(huán)再追求優(yōu)化。我第一次搭建 RAG 時做過一個最基本的版本切塊、向量化、檢索、拼提示詞沒有重排、沒有混合檢索就是能跑。先讓鏈路通起來再一項項加功能每個加完做一次評測對比這樣做既不容易迷失方向也能在每一步都明確改動價值。