:從文檔切分到檢索評估的完整指南)
1. 為什么RAG值得你花時間搞明白RAG這個詞這兩年在大模型圈子里出現(xiàn)的頻率高得離譜。不管你是做AI應用的開發(fā)者還是剛入門大模型的小白只要涉及到“讓模型回答得更準”“讓模型知道它原本不知道的東西”“讓模型別瞎編”繞來繞去最終都會落到RAG上。RAG的全稱是Retrieval-Augmented Generation翻譯過來叫檢索增強生成。名字聽著挺學術但核心邏輯其實特別樸素——模型自己記不住或者不知道的東西你幫它去外面查資料查到了再讓它組織語言回答。我剛開始接觸RAG的時候也覺得這東西應該不難不就是把文檔切一切、向量化、存進數(shù)據(jù)庫、查詢的時候做相似度匹配、把匹配到的內容塞進提示詞里讓模型生成答案嗎但真正動手做起來才發(fā)現(xiàn)坑遠比想象的多。切分粒度怎么定向量模型選哪個檢索回來一堆不相關的內容怎么辦檢索評估怎么做這些問題不解決搭出來的RAG系統(tǒng)就是個花架子看著能用實際一問就露餡。這篇內容適合誰看如果你是零基礎想入門RAG我會從最基礎的概念講起把每個環(huán)節(jié)的原理和實操都拆開揉碎如果你已經(jīng)搭過簡單的RAG流程但效果不理想我會重點講檢索評估和優(yōu)化策略這些是區(qū)分“能用”和“好用”的關鍵。整篇內容會圍繞RAG的功能原理、系統(tǒng)構建、檢索評估三條主線展開每一步都配上可復現(xiàn)的操作思路和參數(shù)選擇邏輯。注意RAG不是萬能藥。它解決的是“知識時效性”和“領域知識注入”的問題但解決不了模型本身推理能力不足的問題。搞清楚它的能力邊界比盲目上手更重要。2. RAG到底在解決什么問題2.1 大模型的三道硬傷要理解RAG的價值得先搞清楚大模型本身有哪些繞不過去的局限。我總結下來主要是三道硬傷。第一道是知識截止。任何大模型的訓練數(shù)據(jù)都有時間邊界比如某個模型訓練數(shù)據(jù)截止到2024年初那你問它2025年發(fā)生的事它要么說不知道要么就開始編。這不是模型笨是它確實沒見過。第二道是私有知識盲區(qū)。你公司內部的文檔、產品手冊、客戶資料這些東西不可能出現(xiàn)在公開訓練數(shù)據(jù)里。你直接問模型“我們公司產品的退貨政策是什么”它只能瞎猜。第三道是幻覺問題。大模型本質上是概率模型它在生成每一個token的時候是在預測“下一個最可能出現(xiàn)的詞”。當它沒有足夠信息支撐的時候它會用看起來合理但實際錯誤的內容來填充答案。而且它說得特別自信你如果不了解情況根本分辨不出來。這三道硬傷靠重新訓練模型或者微調成本高、周期長、效果還不一定好。RAG提供了一條更輕量的路徑不改模型參數(shù)通過外掛知識庫的方式讓模型在生成回答之前先“查資料”。2.2 RAG的核心工作流拆解RAG的完整流程可以拆成兩個階段索引階段和查詢階段。索引階段是離線的你先把所有知識文檔處理好存進向量數(shù)據(jù)庫。具體步驟包括文檔加載、文本切分、向量化、存入向量數(shù)據(jù)庫。這個階段相當于你給模型建了一個“圖書館”把所有的書都編好目、上好書架。查詢階段是在線的用戶提問之后系統(tǒng)先把問題向量化然后去向量數(shù)據(jù)庫里找最相似的文本片段把這些片段和原始問題一起塞進提示詞最后交給大模型生成答案。這個階段相當于用戶來圖書館問問題你先去書架上找到相關的幾本書翻到相關頁碼然后基于這些內容來回答。聽起來很簡單對吧但每個環(huán)節(jié)都有大量細節(jié)需要做決策。比如文本切分你切得太碎語義不完整切得太粗檢索精度下降。比如向量模型你選中文效果差的模型檢索出來的東西驢唇不對馬嘴。比如檢索策略你只用向量相似度可能漏掉關鍵詞精確匹配的情況。2.3 RAG和微調怎么選經(jīng)常有人問RAG和微調到底用哪個我的經(jīng)驗是它們解決的不是同一類問題。微調適合改變模型的行為模式比如讓模型的輸出風格更正式、更簡潔或者讓模型學會某種特定的輸出格式。微調是在教模型“怎么說話”。RAG適合給模型補充知識內容比如讓模型知道最新的政策法規(guī)、公司內部流程、特定領域的專業(yè)知識。RAG是在告訴模型“說什么”。實際項目中兩者經(jīng)常配合使用。先用RAG解決知識注入的問題如果發(fā)現(xiàn)模型在特定任務上的輸出格式或推理方式不理想再考慮用微調來調整行為。但絕大多數(shù)場景下RAG的投入產出比遠高于微調因為微調需要標注數(shù)據(jù)、需要GPU資源、需要反復實驗而RAG的迭代周期短得多。實操心得如果你剛開始做RAG不要一上來就追求完美。先用最簡單的方案跑通全流程哪怕切分策略很粗糙、向量模型用的是默認的先看到效果再逐步優(yōu)化每個環(huán)節(jié)。我見過太多人卡在“選哪個向量模型”這一步糾結好幾天結果全流程都沒跑通。3. 動手搭建RAG系統(tǒng)從文檔到向量庫3.1 文檔加載與預處理搭建RAG的第一步是把你的知識文檔加載進來。文檔格式可能五花八門PDF、Word、Markdown、HTML、Excel、數(shù)據(jù)庫導出等等。不同格式需要不同的加載器。PDF是最麻煩的格式之一。有些PDF是掃描件需要OCR有些PDF有復雜的表格和圖表直接提取文本會丟失結構信息有些PDF是多欄排版提取出來的文本順序是亂的。我的建議是如果PDF質量太差寧可手動整理成Markdown或純文本也不要硬用自動提取因為垃圾進垃圾出后面檢索效果一定好不了。Word文檔相對好處理用python-docx之類的庫可以提取段落和表格。Markdown和純文本最簡單直接讀取就行。HTML需要去掉標簽保留正文內容。預處理階段還需要做幾件事去掉頁眉頁腳、去掉重復內容、統(tǒng)一編碼格式、處理特殊字符。這些看起來是小事但如果不做后面切分出來的文本塊會包含大量噪音影響檢索質量。# 以PDF加載為例的偽代碼思路 from langchain.document_loaders import PyPDFLoader loader PyPDFLoader(knowledge_base.pdf) pages loader.load() # 檢查每頁內容質量 for i, page in enumerate(pages): text page.page_content.strip() if len(text) 50: print(f第{i}頁內容過短可能是掃描件或空白頁需要單獨處理)3.2 文本切分粒度決定成敗文本切分是RAG系統(tǒng)中最容易被低估的環(huán)節(jié)。很多人隨便設個chunk_size1000、chunk_overlap200就完事了結果檢索效果一塌糊涂。切分的核心矛盾在于塊太小語義不完整塊太大噪音太多。你想想如果一個文本塊只有一句話“該政策自2024年1月1日起施行”檢索出來之后模型根本不知道這是什么政策。但如果一個文本塊有3000字里面涵蓋了五個不同的主題你檢索“退貨政策”的時候可能匹配到的是這個塊里關于“換貨政策”的那部分但模型看到的是整個3000字容易被無關信息干擾。我的經(jīng)驗是按語義邊界切分比按固定字數(shù)切分效果好得多。具體來說對于結構化文檔如Markdown、HTML按標題層級切分每個小節(jié)作為一個塊如果小節(jié)太長再按段落細分。對于非結構化文檔如純文本、對話記錄按段落切分如果段落太長再按句子切分。對于代碼文檔按函數(shù)或類切分。對于表格把表頭和每一行組合成一個完整的描述塊。chunk_size的設置沒有標準答案但有一個經(jīng)驗范圍中文文本建議在300-800字之間英文文本建議在200-500詞之間。chunk_overlap建議設為chunk_size的10%-20%目的是讓相鄰塊之間有上下文銜接避免在邊界處丟失信息。# 按標題層級切分的思路 from langchain.text_splitter import MarkdownHeaderTextSplitter headers_to_split_on [ (#, 一級標題), (##, 二級標題), (###, 三級標題), ] splitter MarkdownHeaderTextSplitter(headers_to_split_onheaders_to_split_on) chunks splitter.split_text(markdown_content) # 如果某個塊超過800字再用遞歸切分器細分 from langchain.text_splitter import RecursiveCharacterTextSplitter recursive_splitter RecursiveCharacterTextSplitter( chunk_size600, chunk_overlap100, separators[\n\n, \n, 。, , , , , ] )注意事項切分的時候一定要保留元數(shù)據(jù)。比如每個塊屬于哪個文檔、哪個章節(jié)、哪個頁碼。這些元數(shù)據(jù)在檢索階段可以用來做過濾在生成階段可以用來做引用標注。我見過有人切完只保留文本內容后面想加過濾功能的時候發(fā)現(xiàn)元數(shù)據(jù)全丟了只能重新處理一遍。3.3 向量化選對模型比選貴的更重要向量化就是把文本轉成一串數(shù)字向量這串數(shù)字代表了文本的語義信息。語義相近的文本向量距離就近語義無關的文本向量距離就遠。向量模型的選擇直接決定了檢索質量的上限。如果向量模型本身對中文語義理解不好后面再怎么優(yōu)化檢索策略都是白搭。選向量模型的時候我主要看幾個維度語言支持是否支持中文中文效果如何。有些模型英文很強但中文拉胯。維度向量維度越高表達能力越強但存儲和計算成本也越高。常見的有384維、768維、1024維、1536維。最大輸入長度模型能處理多長的文本。如果你的chunk_size是800字模型最大輸入只有512個token那超出的部分會被截斷。推理速度如果你有大量文檔需要向量化推理速度直接影響索引構建時間。是否開源開源模型可以本地部署數(shù)據(jù)不出內網(wǎng)閉源API模型通常效果更好但需要聯(lián)網(wǎng)調用。目前中文場景下常用的向量模型有幾類一類是專門針對中文優(yōu)化的開源模型一類是多語言通用模型還有一類是商業(yè)API。我的建議是如果你的數(shù)據(jù)敏感度不高可以先用商業(yè)API快速驗證效果如果數(shù)據(jù)不能出內網(wǎng)就選開源模型本地部署。# 向量化示例以開源模型為例 from sentence_transformers import SentenceTransformer model SentenceTransformer(your-chinese-embedding-model) texts [文本塊1的內容, 文本塊2的內容] embeddings model.encode(texts, normalize_embeddingsTrue) # normalize_embeddingsTrue 很重要 # 歸一化之后余弦相似度計算可以用點積代替速度更快實操心得不要盲目追求高維度。我實測下來768維和1536維在大多數(shù)中文檢索場景下的效果差異并不明顯但存儲成本差了一倍。先用768維跑起來如果發(fā)現(xiàn)檢索效果確實不夠再考慮升級。3.4 向量數(shù)據(jù)庫選型與入庫向量數(shù)據(jù)庫是專門用來存儲和檢索向量的。選型的時候主要考慮幾個因素數(shù)據(jù)量級、查詢延遲要求、是否需要持久化、是否需要分布式、運維成本。小規(guī)模場景幾萬到幾十萬條向量用FAISS就夠了。FAISS是Facebook開源的向量檢索庫輕量、快、不需要額外部署服務直接嵌在Python代碼里就能用。缺點是它本質上是個索引文件不支持增刪改查的實時操作每次更新都需要重建索引。中等規(guī)模場景百萬到千萬級可以考慮Milvus、Qdrant、Weaviate這類專門的向量數(shù)據(jù)庫。它們支持實時增刪改查、支持分布式部署、有完善的API和監(jiān)控。缺點是部署和運維復雜度上來了。如果已經(jīng)有PostgreSQL可以用pgvector擴展直接在關系型數(shù)據(jù)庫里存向量。好處是不用額外維護一套數(shù)據(jù)庫壞處是性能和功能不如專門的向量數(shù)據(jù)庫。大規(guī)模場景億級以上那就需要考慮分布式向量數(shù)據(jù)庫了比如Milvus集群版。這個量級一般公司也碰不到這里不展開。入庫的時候除了向量本身還要存原始文本和元數(shù)據(jù)。原始文本用于后續(xù)塞進提示詞元數(shù)據(jù)用于過濾和引用。# 以FAISS為例的入庫思路 import faiss import numpy as np dimension 768 # 向量維度 index faiss.IndexFlatIP(dimension) # IP Inner Product內積 # 假設embeddings是numpy數(shù)組shape為(n, 768) embeddings np.array(embeddings).astype(float32) index.add(embeddings) # 保存索引 faiss.write_index(index, vector_index.faiss) # 同時保存文本和元數(shù)據(jù) import json with open(chunks.json, w, encodingutf-8) as f: json.dump(chunks, f, ensure_asciiFalse)4. 檢索策略與效果評估4.1 向量檢索的局限與補充方案向量檢索的核心是語義相似度它擅長處理“意思相近但用詞不同”的情況。比如用戶問“怎么退錢”文檔里寫的是“退款流程”向量檢索能匹配上。但向量檢索也有明顯的短板。第一它對精確匹配不敏感。比如用戶問“產品型號X200的保修期”如果文檔里寫的是“X200型號保修期為兩年”向量檢索可能匹配到其他型號的保修信息因為語義上都是“保修期”。第二它對否定語義處理不好。比如用戶問“哪些情況不支持退貨”向量檢索可能匹配到“支持退貨的情況”因為語義相似度很高。第三它對數(shù)字和專有名詞不敏感。比如“2024年政策”和“2023年政策”向量可能很接近但實際內容完全不同。解決這些問題的常見方案是混合檢索向量檢索 關鍵詞檢索如BM25然后把兩路結果融合。融合策略有幾種加權求和、RRFReciprocal Rank Fusion、先向量后關鍵詞過濾等。# RRF融合策略的偽代碼 def rrf_fusion(vector_results, keyword_results, k60): scores {} for rank, doc_id in enumerate(vector_results): scores[doc_id] scores.get(doc_id, 0) 1 / (k rank 1) for rank, doc_id in enumerate(keyword_results): scores[doc_id] scores.get(doc_id, 0) 1 / (k rank 1) # 按分數(shù)降序排列 sorted_docs sorted(scores.items(), keylambda x: x[1], reverseTrue) return [doc_id for doc_id, _ in sorted_docs]RRF的好處是不需要調權重對兩路檢索的分數(shù)尺度不敏感。我實測下來RRF在大多數(shù)場景下比加權求和更穩(wěn)定。4.2 重排序讓最相關的結果排到最前面檢索回來Top-K個結果之后還有一個優(yōu)化空間重排序。向量檢索用的是雙塔模型查詢和文檔分別編碼然后算相似度。這種方式速度快但精度有限。重排序用的是交叉編碼器把查詢和文檔拼在一起輸入模型模型直接輸出相關性分數(shù)。這種方式精度高但速度慢所以只適合對少量候選結果做精排。典型流程是向量檢索召回Top-50然后用重排序模型對50個結果精排取Top-5塞進提示詞。這樣既保證了召回率又保證了精度。重排序模型的選擇和向量模型類似也要看中文支持、推理速度、是否開源。有些向量模型本身就提供了配套的重排序模型搭配使用效果更好。注意事項重排序不是必須的。如果你的檢索結果本身已經(jīng)很好Top-5都是相關的加不加重排序差別不大。但如果你的檢索結果噪音比較多重排序能顯著提升效果。建議先做檢索評估看看當前的問題出在召回階段還是排序階段再決定要不要加重排序。4.3 檢索評估怎么知道你的RAG好不好這是很多人忽略的環(huán)節(jié)。搭完RAG系統(tǒng)隨便問幾個問題覺得“好像還行”就上線了。結果用戶一問稍微偏一點的問題就露餡了。檢索評估需要一套系統(tǒng)的指標和方法。核心指標包括召回率相關文檔有多少被檢索出來了。召回率低說明你的檢索策略漏掉了重要信息。精確率檢索出來的文檔有多少是相關的。精確率低說明噪音太多會干擾模型生成。MRR第一個相關文檔排在第幾位。MRR高說明最相關的內容排在最前面。NDCG考慮排序位置的相關性指標越相關的內容排得越靠前分數(shù)越高。做評估需要標注數(shù)據(jù)。你需要準備一批查詢每個查詢標注哪些文檔是相關的。標注數(shù)據(jù)不用很多50-100個查詢就能看出問題。關鍵是覆蓋不同類型的查詢事實型、對比型、否定型、多跳推理型等。# 簡單的檢索評估示例 def evaluate_retrieval(queries, ground_truth, retriever, k5): recall_scores [] precision_scores [] mrr_scores [] for query, relevant_ids in zip(queries, ground_truth): retrieved retriever.search(query, top_kk) retrieved_ids [doc.id for doc in retrieved] # 召回率 hit len(set(retrieved_ids) set(relevant_ids)) recall hit / len(relevant_ids) if relevant_ids else 0 recall_scores.append(recall) # 精確率 precision hit / k precision_scores.append(precision) # MRR for rank, doc_id in enumerate(retrieved_ids): if doc_id in relevant_ids: mrr_scores.append(1 / (rank 1)) break else: mrr_scores.append(0) return { recallk: sum(recall_scores) / len(recall_scores), precisionk: sum(precision_scores) / len(precision_scores), mrr: sum(mrr_scores) / len(mrr_scores) }評估結果怎么用如果召回率低說明你的切分粒度、向量模型、檢索策略有問題需要調整。如果精確率低但召回率高說明檢索回來的東西太多太雜需要加重排序或者調整Top-K。如果MRR低說明排序有問題最相關的內容沒有排到前面。4.4 生成階段的優(yōu)化技巧檢索做完了最后一步是把檢索結果和用戶問題一起塞給大模型生成答案。這一步也有不少優(yōu)化空間。提示詞設計是關鍵。你需要明確告訴模型只根據(jù)提供的參考資料回答如果參考資料里沒有相關信息就說不知道不要自己編。這個約束能大幅降低幻覺。上下文長度控制也很重要。檢索回來的內容不是越多越好。塞太多內容進去一是可能超出模型的上下文窗口二是無關信息會干擾模型。我的經(jīng)驗是Top-3到Top-5通常就夠了具體看chunk_size和模型上下文窗口。引用標注能提升可信度。在提示詞里要求模型在回答中標注信息來源比如“根據(jù)文檔A第3節(jié)的內容...”。這樣用戶能驗證答案的可靠性也方便排查問題。# 提示詞模板示例 PROMPT_TEMPLATE 你是一個知識助手。請根據(jù)以下參考資料回答用戶問題。 參考資料 {context} 用戶問題{question} 回答要求 1. 只根據(jù)參考資料回答不要使用你自己的知識 2. 如果參考資料中沒有相關信息直接說根據(jù)現(xiàn)有資料無法回答該問題 3. 回答時標注信息來源格式為[來源文檔名] 4. 回答要簡潔準確不要展開無關內容 回答5. 常見問題與排查技巧實錄5.1 檢索效果差的排查思路檢索效果差是最常見的問題。排查的時候我一般按這個順序來第一步檢查切分質量。隨便抽幾個chunk出來看看是不是語義完整有沒有被切斷的句子有沒有包含大量噪音如果切分質量差后面怎么優(yōu)化都是白搭。第二步檢查向量模型。拿幾個查詢和對應的相關文檔手動算一下向量相似度。如果相關文檔的相似度還不如無關文檔高說明向量模型不適合你的場景。第三步檢查檢索策略。是不是只用了向量檢索有沒有考慮混合檢索Top-K設的是多少K太小可能漏掉相關結果K太大噪音太多。第四步檢查評估方法。你的評估指標合理嗎標注數(shù)據(jù)準確嗎有時候不是系統(tǒng)效果差是評估方法有問題。5.2 模型回答不準確的原因分析檢索回來的內容是對的但模型回答還是不對這種情況通常有幾個原因提示詞約束不夠模型沒有嚴格遵循“只根據(jù)參考資料回答”的指令摻雜了自己的知識。上下文太長塞了太多內容模型注意力被分散關鍵信息被淹沒。參考資料格式混亂檢索回來的文本沒有清晰的邊界模型分不清哪些是參考資料、哪些是問題。模型本身能力不足有些小模型在長上下文場景下表現(xiàn)確實差換更大的模型可能就解決了。5.3 性能優(yōu)化的幾個方向RAG系統(tǒng)的性能優(yōu)化主要從兩個維度考慮延遲和成本。延遲優(yōu)化向量檢索本身很快瓶頸通常在向量化和重排序。如果向量模型推理慢可以考慮用更小的模型或者做量化。如果重排序慢可以減少候選數(shù)量或者用更快的重排序模型。成本優(yōu)化如果用的是商業(yè)API向量化和生成都是按量計費的。優(yōu)化方向包括減少不必要的向量化比如增量更新而不是全量重建、壓縮上下文長度、緩存常見查詢的結果。實操心得我踩過最大的坑是忽略了增量更新。一開始每次文檔有更新就全量重建索引幾萬條數(shù)據(jù)要跑好幾個小時。后來改成只對新增和修改的文檔做向量化刪除的文檔從索引里移除更新時間縮短到幾分鐘。如果你的知識庫更新頻繁一定要設計好增量更新機制。5.4 常見問題速查表問題現(xiàn)象可能原因排查方向解決思路檢索結果完全不相關向量模型不匹配檢查向量模型的中文效果換用中文優(yōu)化的向量模型檢索結果漏掉關鍵信息切分粒度過粗或過細檢查chunk_size和切分邊界調整切分策略按語義邊界切分相似問題檢索結果不穩(wěn)定向量模型對語義變化敏感測試不同表述的相似度增加查詢改寫或混合檢索模型回答包含幻覺提示詞約束不夠檢查提示詞模板強化“只根據(jù)參考資料回答”的約束系統(tǒng)響應慢向量化或重排序耗時分階段計時優(yōu)化模型選擇或減少候選數(shù)量新增文檔檢索不到索引未更新檢查索引更新機制實現(xiàn)增量更新流程6. 從能用走向好用持續(xù)迭代的思路RAG系統(tǒng)不是搭完就完事了它需要持續(xù)迭代。迭代的依據(jù)來自兩個方面用戶反饋和評估數(shù)據(jù)。用戶反饋是最直接的信號。用戶點了“答案不準”的按鈕或者追問“你確定嗎”這些都是優(yōu)化線索。把這些問題收集起來分析是檢索問題還是生成問題然后針對性優(yōu)化。評估數(shù)據(jù)是更系統(tǒng)的依據(jù)。定期跑一遍評估集看各項指標的變化趨勢。如果召回率在下降可能是知識庫內容老化了如果精確率在下降可能是文檔噪音增加了。迭代的優(yōu)先級建議是先解決檢索問題再解決生成問題。因為檢索是根基檢索不準生成再優(yōu)化也沒用。檢索問題里先解決切分和向量模型的問題再考慮混合檢索和重排序。還有一個容易被忽略的點知識庫的維護。文檔會過期、會更新、會刪除。你需要一套機制來保證知識庫和實際業(yè)務保持一致。我見過一些RAG系統(tǒng)剛上線效果很好過了半年就越來越差因為知識庫沒人維護里面全是過時信息。最后分享一個我在實際項目中總結的小技巧給每個chunk打上時間戳和版本號。檢索的時候可以優(yōu)先返回最新版本的內容。如果同一個問題有多個版本的答案模型可以基于時間戳來判斷哪個是最新的。這個小小的元數(shù)據(jù)字段在知識頻繁更新的場景下能省很多事。