:基于Spring AI與pgvector的RAG工程落地指南)
1. 大模型幻覺到底是什么為什么每個做AI應(yīng)用的人都繞不開它第一次被大模型幻覺坑到是我拿它做一個內(nèi)部知識問答的原型。問它我們公司年假制度里入職滿三年有多少天它一本正經(jīng)地回了一段格式工整、語氣篤定的答案連根據(jù)員工手冊第X條都編出來了。我去翻真實的手冊根本沒有那一條天數(shù)也是錯的。那一刻我才真正理解大模型幻覺AI Hallucination不是模型偶爾抽風而是它工作機制里自帶的一種副作用。說白了大模型本質(zhì)上是一個下一個詞預(yù)測器。它讀過海量文本學到的是詞與詞之間的概率分布而不是一個可查詢的事實數(shù)據(jù)庫。當你問它問題它做的事情是根據(jù)上下文一個詞一個詞地往外續(xù)寫最像人話的內(nèi)容。至于這段內(nèi)容是不是真的它自己并不知道也沒有一個內(nèi)置的真值校驗器來攔它。所以當它遇到知識盲區(qū)、或者上下文里沒有足夠信息時它不會說我不知道而是傾向于編一個看起來最合理的答案——這就是幻覺。這件事對做應(yīng)用的人意味著什么意味著你只要把大模型直接懟到用戶面前尤其是懟到企業(yè)知識庫、客服、醫(yī)療、法律、金融這類對準確性要求極高的場景里翻車是遲早的。熱搜里那一堆詞——RAG、rag知識庫、企業(yè)知識庫 rag、rag檢索增強生成、spring ai實戰(zhàn)、java rag問答——本質(zhì)上都是同一件事的不同側(cè)面大家都在想辦法把幻覺壓下去讓模型說的話有據(jù)可查。這篇內(nèi)容我打算按一個真實項目落地的思路來寫。先講清楚幻覺的成因和分類再講主流的抑制手段重點是RAG這條主線然后落到工程實現(xiàn)上把Spring AI、pgvector、切塊、Prompt工程這些熱搜詞串起來講透最后給一份排查手冊。適合正在做AI應(yīng)用、被幻覺折磨過的開發(fā)者也適合剛接觸RAG、想知道為什么我的知識庫答不準的朋友。不管你是用Java棧還是Python棧思路是通用的。2. 幻覺的成因拆解與分類先搞清楚敵人長什么樣2.1 從下一個詞預(yù)測看幻覺的必然性要抑制幻覺得先接受一個前提幻覺不是bug是feature的陰暗面。模型的訓(xùn)練目標是最大化下一個詞的條件概率它優(yōu)化的是像不像真的不是是不是真的。這兩者在絕大多數(shù)情況下重合但在邊緣情況下會分叉。我打個比方。你讓一個博覽群書但從沒查過證的人憑記憶回答一個冷門問題他大概率會給你一個聽起來很對的答案因為他讀過的書里類似的表述太多了他的大腦會自動補全。大模型干的就是這件事而且它補全得比人還流暢所以更有欺騙性。具體到成因我一般分成四類來看知識缺失型訓(xùn)練數(shù)據(jù)里壓根沒有這個知識或者知識太新超出訓(xùn)練截止時間。模型不知道但它不會承認于是編。知識沖突型訓(xùn)練數(shù)據(jù)里同一個問題有多個矛盾的說法模型把它們混在一起輸出一個縫合怪。上下文誤導(dǎo)型你給的Prompt或者檢索回來的資料本身有錯、有歧義模型順著錯誤信息往下編。解碼隨機型采樣溫度temperature太高模型在多個候選詞里隨機挑挑著挑著就偏離了事實軌道。這四類里第一類和第三類是工程上最常遇到的也是RAG主要要解決的。第二類靠數(shù)據(jù)清洗第四類靠參數(shù)調(diào)優(yōu)。2.2 幻覺的幾種典型表現(xiàn)別只盯著編事實很多人以為幻覺就是編造不存在的事實其實它的表現(xiàn)形式比這豐富得多識別不出來就容易誤判?;糜X類型典型表現(xiàn)常見場景事實性幻覺編造不存在的人名、條款、數(shù)據(jù)、引用知識問答、法律咨詢引用性幻覺編造參考文獻、鏈接、文檔編號學術(shù)助手、報告生成邏輯性幻覺推理步驟自相矛盾前后打架數(shù)學解題、多步推理指令性幻覺無視你的格式要求自作主張Prompt工程、結(jié)構(gòu)化輸出時效性幻覺用舊知識回答新問題還說得斬釘截鐵新聞、股價、政策查詢我踩過最隱蔽的一個坑是引用性幻覺。做企業(yè)知識庫時模型回答完還貼心地附上來源XX制度文檔第3.2節(jié)看起來特別可信結(jié)果那個文檔根本沒有3.2節(jié)。這種幻覺最危險因為它自帶可信度偽裝。后來我在系統(tǒng)里強制要求來源必須由檢索層給出模型不許自己編來源這才堵住。2.3 為什么直接問大模型這條路走不通有人會想那我Prompt寫得好一點讓它不要編造不就行了實測下來效果有限。原因有三第一模型對我不知道這件事沒有內(nèi)在驅(qū)動力。你讓它別編它表面上答應(yīng)遇到盲區(qū)還是會編因為編一個流暢答案的概率收益比說我不知道高。第二Prompt的約束力會隨著上下文變長而衰減。熱搜里那個prompt is too long和invalid prompt: your prompt was flagged其實都指向同一個問題Prompt不是越長越好也不是越強硬越好它有邊界。第三模型沒有外部事實的錨點。它所有的判斷都來自參數(shù)里的記憶而記憶是會模糊、會混淆的。你不給它一個可查證的外部來源它就只能靠記憶硬撐。所以結(jié)論很明確要壓幻覺必須給模型外掛一個可靠的知識來源讓它在回答前先去查查到什么說什么查不到就說不知道。這就是RAG檢索增強生成的核心思想也是熱搜里rag、rag知識庫、rag檢索增強生成、rag實戰(zhàn)這些詞反復(fù)出現(xiàn)的原因。3. RAG這條主線把幻覺從編變成查3.1 RAG到底解決了什么一句話講透RAG的全稱是Retrieval-Augmented Generation檢索增強生成。拆開看就三步檢索Retrieve→ 增強Augment→ 生成Generate。用戶提問后系統(tǒng)先去知識庫里檢索出最相關(guān)的幾段資料把這些資料塞進Prompt里作為參考資料然后讓模型基于這些資料來回答。模型的任務(wù)從憑記憶回答變成了閱讀理解后回答幻覺空間一下子被壓縮了。我常跟團隊說RAG的本質(zhì)是給模型開卷考試。閉卷考試它只能靠記憶容易瞎蒙開卷考試它手邊有資料照著資料答準確率立刻上一個臺階。當然開卷也有開卷的問題——資料找錯了、資料太長模型看漏了、資料本身有錯都會導(dǎo)致答錯。所以RAG不是銀彈是一整套需要調(diào)優(yōu)的工程。3.2 RAG的完整鏈路每個環(huán)節(jié)都是幻覺的潛在來源一個標準的RAG鏈路我習慣拆成六個環(huán)節(jié)每個環(huán)節(jié)沒做好幻覺都會從那里鉆出來文檔加載與解析把PDF、Word、網(wǎng)頁、數(shù)據(jù)庫記錄讀進來。解析錯了后面全錯。切塊Chunking把長文檔切成小塊。熱搜里的rag切塊就是這個。切得不好檢索出來的片段是殘缺的模型讀不懂。向量化Embedding把每個塊轉(zhuǎn)成向量存進向量數(shù)據(jù)庫比如pgvector。檢索Retrieval用戶提問也轉(zhuǎn)成向量去庫里找最相似的Top-K個塊。重排Rerank對檢索結(jié)果再排一次序把最相關(guān)的頂上來。這一步很多簡易實現(xiàn)會省掉但它是提準確率的關(guān)鍵。生成Generation把重排后的資料和問題一起塞給模型讓它基于資料回答。這六步里第2步和第4步是幻覺的高發(fā)區(qū)。切塊切碎了檢索就找不準檢索找不準模型拿到的資料就是錯的它再忠實地基于錯誤資料回答輸出就是錯的。這種錯誤比模型自己編還難發(fā)現(xiàn)因為它看起來有據(jù)可查。3.3 RAG和微調(diào)、和MCP的區(qū)別別選錯工具熱搜里有個詞叫rag和mcp區(qū)別說明很多人在這幾個概念之間犯迷糊。我簡單理一下RAG不改模型參數(shù)靠外部檢索給模型喂資料。適合知識頻繁更新、要求可溯源、成本敏感的場景。企業(yè)知識庫首選。微調(diào)Fine-tuning改模型參數(shù)把知識焊進模型里。適合風格遷移、固定領(lǐng)域術(shù)語、對延遲極敏感的場景。但知識更新要重新訓(xùn)練成本高且依然會有幻覺。MCP可以理解為一種讓模型調(diào)用外部工具/數(shù)據(jù)源的協(xié)議標準。它解決的是模型怎么連上外部能力的問題RAG可以是它連接的一種能力。我的經(jīng)驗是知識類問答優(yōu)先RAG風格和格式類需求考慮微調(diào)兩者可以疊加。絕大多數(shù)企業(yè)知識庫場景RAG就夠了別一上來就想著微調(diào)那是殺雞用牛刀還費錢。4. 工程落地用Spring AI pgvector搭一套抗幻覺的RAG4.1 技術(shù)選型背后的考量熱搜里spring ai實戰(zhàn)spring ai 2.0java rag問答spring ai alibabaspring ai對接本地部署的deepseekspring ai連接千問平臺這些詞扎堆出現(xiàn)說明Java棧的開發(fā)者對RAG落地需求很旺。我這邊主力也是Java棧選型邏輯分享一下。為什么用Spring AI而不是自己擼HTTP調(diào)用因為Spring AI把模型調(diào)用、Embedding、向量庫、Prompt模板這些抽象成了統(tǒng)一的接口換模型比如從千問換到本地部署的DeepSeek基本只改配置。熱搜里spring ai連接千問平臺需要引哪個jar包這種問題本質(zhì)就是依賴管理Spring AI的starter幫你屏蔽了大部分差異。為什么用pgvector而不是專用向量庫因為大多數(shù)企業(yè)已經(jīng)有PostgreSQL了pgvector是它的一個擴展不用額外維護一套數(shù)據(jù)庫。數(shù)據(jù)量在百萬級以下pgvector的性能完全夠用運維成本還低。熱搜里基于 fastapilangchainlanggraphragpgvector 的 ai agentic rag也是這個思路pgvector是跨語言棧的通用選擇。為什么強調(diào)抗幻覺而不是高準確率因為準確率是個綜合指標而幻覺是其中最要命的一環(huán)。我的做法是寧可讓模型說資料里沒有也不讓它編。這個原則要貫穿整個設(shè)計。4.2 依賴配置與模型接入先看Maven依賴。以Spring AI對接國內(nèi)模型平臺為例核心依賴大致是這樣具體版本以官方文檔為準熱搜里spring ai 2.0文檔就是查這個的dependency groupIdorg.springframework.ai/groupId artifactIdspring-ai-starter-model-openai/artifactId /dependency dependency groupIdorg.springframework.ai/groupId artifactIdspring-ai-starter-vector-store-pgvector/artifactId /dependency dependency groupIdorg.postgresql/groupId artifactIdpostgresql/artifactId /dependency配置文件里把模型地址、密鑰、向量庫連接配好spring: ai: openai: base-url: https://你的模型平臺地址 api-key: ${API_KEY} chat: options: model: 你的對話模型名 temperature: 0.1 embedding: options: model: 你的向量模型名 datasource: url: jdbc:postgresql://localhost:5432/ragdb username: rag password: rag這里有個關(guān)鍵點temperature一定要調(diào)低。對話場景我一般設(shè)0.1到0.3知識問答場景直接0.1甚至0。溫度越低模型越保守越傾向于選概率最高的詞幻覺概率顯著下降。熱搜里prompt閃退prompt is too long這類問題很多時候不是Prompt本身的問題是參數(shù)和上下文長度沒控制好。4.3 文檔切塊RAG里最容易被低估的一步切塊Chunking這件事我見過太多人隨便按固定字數(shù)切然后抱怨檢索不準。切塊是RAG的地基地基歪了上面全歪。我的切塊策略分三層第一層按語義結(jié)構(gòu)切。優(yōu)先按標題、段落、列表項這些自然邊界切而不是硬按字符數(shù)。一篇制度文檔按章-節(jié)-條切每個條是一個塊語義完整。第二層控制塊大小。塊太小語義不完整檢索出來是碎片塊太大噪聲多還會擠占上下文窗口。我的經(jīng)驗值是300到800個token中文大概200到500字。具體看文檔密度技術(shù)文檔可以小一點敘述性文檔可以大一點。第三層加重疊Overlap。相鄰塊之間保留10%到20%的重疊防止一個完整意思被切斷在兩塊之間。比如塊大小500字重疊50到100字。// 偽代碼示意實際用Spring AI的TokenTextSplitter TokenTextSplitter splitter new TokenTextSplitter( 500, // 目標塊大小 100, // 最小塊大小 50, // 重疊大小 10000, // 最大塊數(shù) true // 保留分隔符 );注意切塊參數(shù)沒有萬能值必須拿你自己的文檔做實驗。我的做法是準備20個典型問題用不同切塊參數(shù)跑檢索看Top-3里有沒有正確答案選命中率最高的那組。4.4 檢索與重排把最相關(guān)的資料頂上來檢索默認是向量相似度Top-K。K取多少我一般取5到10。取太少可能漏掉關(guān)鍵資料取太多會引入噪聲還會讓Prompt變長熱搜里prompt is too long就是這么來的。但純向量檢索有個問題它擅長語義相似不擅長精確匹配。比如用戶問第3.2條怎么規(guī)定的向量檢索可能找不回精確的第3.2條。所以我會加一路關(guān)鍵詞檢索BM25和向量檢索做混合Hybrid Search兩路結(jié)果融合后再重排。重排Rerank這一步簡易實現(xiàn)可以省但想提準確率強烈建議加上。原理是用一個專門的重排模型對問題-候選塊對做精細打分比向量相似度準得多。熱搜里rag檢索增強生成rag實戰(zhàn)講得好的文章基本都會提重排。// 混合檢索 重排的思路示意 ListDocument vectorResults vectorStore.similaritySearch(query, 10); ListDocument keywordResults keywordSearch(query, 10); ListDocument merged mergeAndDedup(vectorResults, keywordResults); ListDocument reranked rerankModel.rerank(query, merged, 5);4.5 Prompt工程讓模型照著資料說檢索回來的資料怎么用全看Prompt怎么寫。這是抑制幻覺的最后一道閘門也是最考驗功夫的地方。熱搜里prompt engineeringprompt提示詞優(yōu)化prompt提示詞這些詞熱度高就是因為大家發(fā)現(xiàn)Prompt寫得好不好直接決定輸出質(zhì)量。我的抗幻覺Prompt模板核心是四條鐵律你是一個嚴謹?shù)闹R問答助手。請嚴格基于下面提供的【參考資料】回答問題。 規(guī)則 1. 只使用【參考資料】中的信息回答不要使用你自己的知識補充。 2. 如果【參考資料】中沒有足夠信息回答直接回復(fù)根據(jù)現(xiàn)有資料無法回答該問題不要編造。 3. 回答時標注信息來源資料編號不要編造來源。 4. 不要對資料內(nèi)容做過度推斷資料說什么就說什么。 【參考資料】 {context} 【問題】 {question}這個模板的關(guān)鍵在于給了模型一條退路——明確告訴它不知道是被允許的、正確的。很多幻覺是因為模型覺得必須給個答案你給它臺階下它就老實了。實操心得規(guī)則不要寫太多條超過5條模型就開始選擇性遵守。把最關(guān)鍵的2到3條放前面用加粗或編號強化。另外規(guī)則里不要編造來源這條一定要有我吃過虧。5. 常見問題與排查技巧實錄5.1 檢索到了正確資料模型還是答錯這是最讓人抓狂的情況。資料明明在上下文里模型卻視而不見或者答成別的。排查思路資料被淹沒了上下文里塞了太多無關(guān)塊正確的那塊被稀釋。解決減少Top-K加強重排。資料位置太靠后模型對上下文中間部分注意力弱lost in the middle現(xiàn)象。解決把最相關(guān)的塊放最前面或最后面。Prompt指令不夠強模型沒意識到必須用資料。解決強化指令甚至用few-shot給個例子。模型能力不足小模型處理長上下文能力弱。解決換更強的模型或減少上下文長度。5.2 模型總說無法回答明明資料里有這是矯枉過正。規(guī)則寫太死模型變得過度保守。解決把無法回答的條件放寬一點比如如果資料部分相關(guān)可以基于相關(guān)部分回答并說明哪些部分資料未覆蓋。另外檢查檢索是不是真的召回了正確資料很多時候是檢索沒召回模型巧婦難為無米之炊。5.3 多輪對話里幻覺變多熱搜里rag多輪對話怎么設(shè)計是個好問題。多輪對話的坑在于歷史對話會污染上下文。用戶前面說錯了一個前提模型后面順著錯前提一路編。我的做法是每輪都重新檢索不要復(fù)用上一輪的檢索結(jié)果歷史對話只保留最近2到3輪且做摘要壓縮關(guān)鍵事實以本輪檢索結(jié)果為準歷史對話只用于理解指代比如它那個指什么。5.4 常見問題速查表現(xiàn)象可能原因排查方向答非所問檢索召回錯誤檢查切塊、Embedding模型、Top-K編造來源Prompt未約束加來源由系統(tǒng)提供規(guī)則答無法回答規(guī)則過嚴或檢索失敗放寬規(guī)則、驗證召回多輪后跑偏歷史污染每輪重檢索、壓縮歷史輸出格式亂指令不明確用結(jié)構(gòu)化輸出、給示例響應(yīng)超時上下文過長減Top-K、壓縮資料5.5 幾個我踩過的坑坑一Embedding模型和對話模型不匹配。檢索用的向量模型和生成用的對話模型是兩回事別混。向量模型選中文效果好的對話模型選指令遵循強的??佣R庫更新后沒重建索引。文檔改了向量庫還是舊的檢索出來是過期內(nèi)容。一定要有增量更新機制??尤雎栽獢?shù)據(jù)過濾。企業(yè)知識庫往往有權(quán)限和分類檢索時要帶上過濾條件比如只搜用戶有權(quán)限的部門文檔否則會召回不該看的內(nèi)容還會引入噪聲??铀陌裄AG當萬能藥。有些問題比如需要復(fù)雜推理、多跳查詢RAG解決不了得上Agentic RAG讓模型自己決定檢索幾次、檢索什么。熱搜里agentic rag基于 fastapilangchainlanggraphragpgvector 的 ai agentic rag就是這個方向適合復(fù)雜場景但復(fù)雜度也高別一上來就上。6. 從RAG到Agentic RAG幻覺治理的下一步單輪RAG能解決大部分知識查詢類幻覺但遇到需要多步推理的問題就力不從心。比如對比A制度和B制度在年假上的差異單輪檢索可能只召回一個制度的資料模型就得靠記憶補另一個幻覺又來了。Agentic RAG的思路是讓模型自己規(guī)劃檢索策略。它可以先檢索A制度再檢索B制度然后對比。LangGraph這類框架就是干這個的把檢索、推理、再檢索串成一個可控的流程。熱搜里基于 fastapilangchainlanggraphragpgvector 的 ai agentic rag描述的就是這套組合。但我要潑盆冷水Agentic RAG的復(fù)雜度和調(diào)試成本比單輪RAG高一個數(shù)量級。我的建議是先用單輪RAG把80%的常見問題解決掉剩下20%的復(fù)雜問題再考慮Agentic。別為了炫技把簡單問題復(fù)雜化。另外無論哪種RAG評估體系都是必須的。我一般建一個幾十到上百條的問題-標準答案集每次改動換模型、調(diào)切塊、改Prompt都跑一遍看準確率和幻覺率的變化。沒有評估調(diào)優(yōu)就是盲人摸象。熱搜里rag歷史用例檢索與實例化適配其實也指向這個——用歷史問答對來評估和優(yōu)化檢索。最后分享一個我個人的判斷標準一個RAG系統(tǒng)好不好不看它答對了多少看它答錯的時候是不是誠實地錯。如果它答錯時說的是資料里沒有那這個系統(tǒng)是可控的如果它答錯時還在編那這個系統(tǒng)就是定時炸彈??够糜X的核心從來不是讓模型變聰明而是讓模型學會在該閉嘴的時候閉嘴。