RAG實踐技巧:將向量庫降級為“語義路由器”,讓答案更合理
隨著模型上下文長度的增加和知識庫的擴展,傳統(tǒng)的向量化方法在RAG中的應用逐漸暴露出其局限性。本文將深入探討RAG技術的核心鏈路,分析向量化在其中的作用,并提出一種新的架構:將向量庫降級為“語義路由器”,結合結構化的知識庫,以解決語義相似性與答案相關性之間的矛盾,從而讓生成的答案更加合理和精準。
Dify與Langchain都可以被歸屬到Agent平臺,可以幫助用戶快速生成各種Agent,只不過兩者的定位與使用對象是不同的:
- Dify的定位是低/零代碼平臺,使用對象甚至可以是HR和財務;
- Langchain的定位是高代碼平臺,使用對象就是程序員;
與Dify類似的有Coze、FastGPT,其中Coze體驗是最好的,最近還開源了,會對Dify造成一定影響;
與Langchain類似的有n8n(會稍有差異,但這么理解也問題不大),這些框架需要開發(fā)者具備一定的編程能力,對開發(fā)者的技術水平要求較高,相對來說其靈活性也變高了。
就個人使用習慣來說,做POC驗證我一定會選Coze或Dify,做復雜的業(yè)務系統(tǒng)我們會做詳細的框架設計,自己上手寫代碼,暫時不會有Langchain或者n8n出手的空間。
原因也很簡單:我們有自己的開發(fā)習慣,不喜歡按他們那種方式做歸類。
從這里大家也可以看出來了,粉絲疑惑的Langchain其實與RAG沒撒必然的聯(lián)系,Agent平臺/框架確實會涉及到知識庫(會實現(xiàn)該模塊),他們也會用到RAG技術,并且還會涉及到向量化,僅此而已。
為了大家更清晰的理解,我們直接來一套相對完整的RAG鏈路算了:
采集/清洗 → 切分(Chunk) → 向量化 → 建索引 → 召回(Top-K) → 重排(Rerank) → 拼上下文 → 生成 → 校對/引用 → 評測與回流。
RAG概述
RAG技術在兩年多前開始被AI應用熟知,通過在生成前檢索外部知識庫,使模型能夠及時利用最新或私有數據。
怎么說呢?雖然有點不恰當,但個人覺得在當時,RAG更多是微調的一種替代技術,因為微調成本確實太高了,結果用著用著大家還覺得挺香的…
在RAG技術框架中,數據工程便已經開始嶄露頭角,懂行的同學會意識到:AI項目最重要的工作就是讓數據與模型好好配合。
RAG的實現(xiàn)來說有兩個模塊,一個是本地索引模塊,他涉及了原始文檔的清洗和切片,并將每個段落轉為向量存儲到向量庫;
第二個模塊就是實時檢索,他需要在用戶提問的時候,先到向量庫中查找相似的片段,再將檢索到的上下文拼接到提示詞中,最后調用大模型。
舉一個案例:
簡單案例
案例來源于最近接到的一個商家工作流,需求很簡單:構建一個能回答關于“咖啡豆種類、沖泡方法、拿鐵配方”等問題的智能助手。
原始數據包含一份咖啡知識的PDF文檔,里面包含文本、表格、少量格式混亂字符和網頁URL。
工作流的目標是:用戶問 “如何制作一杯標準的拿鐵咖啡?需要多少克咖啡粉和牛奶?”時,系統(tǒng)能精準從知識庫中找到配方步驟和分量,并生成清晰、無誤的答案。
直接上手
實際實施工作流的是下面一個小朋友,他圖省事未對文檔進行清洗,按固定長度512字符切分;
當遭遇用戶提問:”標準拿鐵咖啡的配方是什么?需要多少毫升牛奶?”時,模型果然開始胡說八道了:向量檢索模塊可能返回了包含“拿鐵”字樣但內容并不精確相關的片段。
例如,一個片段來自文檔中關于拿鐵咖啡歷史的段落,另一個片段則包含了對拿鐵咖啡配料的不完整描述,甚至還可能檢索到包含無關引用(如“咖啡師手冊”)的片段。
這些檢索結果在沒有篩選的情況下被一并提供給模型,導致模型的回答驢唇不對馬嘴,出現(xiàn)了明顯錯誤,這背后可能的提示詞是:
根據以下知識片段回答問題:
[知識片段1]
**? 咖啡知識大全 2025**
https://coffee-wiki.com/retail
拿鐵咖啡的歷史與起源
17世紀,維也納柯奇斯基將軍發(fā)現(xiàn)土耳其人留下的咖啡豆…
[知識片段2]
牛奶咖啡在意大利語中稱為”Caffè latte”,傳統(tǒng)使用高溫滅菌奶…
[知識片段3]
取冷藏全脂牛奶200ml
用蒸汽棒加熱至60℃并打出細奶泡
將牛奶緩慢倒入咖啡杯
問題:標準拿鐵咖啡的配方是什么?需要多少毫升牛奶?
這里如果引入CoT也是災難:
1. 用戶問配方和牛奶用量
2. 上下文提供:
– 片段1:拿鐵歷史(無配方)
– 片段2:名稱起源(無用量)
– 片段3:提到200ml牛奶但無完整步驟
3. 關鍵缺失:
– 沒有完整的步驟說明
– 沒有明確說200ml就是標準用量
– 缺少開頭(咖啡萃?。┖徒Y尾(拉花)
4. 基于訓練數據推測:
– 歷史知識常見(維也納起源正確)
– 200ml是常見牛奶量(保留但加”左右”模糊化)
– 缺少步驟→用通用話術填補(”參考咖啡師手冊”)
這里問題就很簡單,沒有對文檔進行清洗,所以在文檔向量化之前一定要對知識進行處理
文檔處理
文檔的全局處理屬于框架型工作是一門技術活,但具體到每個文檔的處理,又變成體力活了,他的動作很簡單:
去掉頁眉/頁腳/URL/無關引用;把表格轉成完整句子;只保留與“拿鐵配方”強相關的干凈文本。
以下是一段清洗后的片段,供大家參考:
[Chunk_A_clean]
【標準拿鐵配方(單杯)】
咖啡粉:18g(萃取一份雙倍濃縮 Espresso)
牛奶:180ml(蒸汽打發(fā),溫度約55–60℃)
步驟:
1) 研磨并萃取濃縮;
2) 將180ml熱奶緩慢倒入;
3) 輕搖融合,可拉花。
[Chunk_B_clean]
比例說明:
常見咖啡:牛奶體積約 1:4(以18g粉對應約30–40ml濃縮+180ml熱奶為例)。
這里形成的提示詞就很清晰了:
角色:你是咖啡知識助手。
規(guī)則:
– 僅基于“資料片段”作答;資料未覆蓋的內容不要編造。
– 回答必須給出“咖啡粉(克)”與“牛奶(毫升)”的具體數字與單位。
– 若資料無答案,請輸出:`未在資料中找到`。
– 在句末用[編號]標注引用來源(如來自片段[1]與[2])。
用戶問題:
“如何制作一杯標準的拿鐵咖啡?需要多少克咖啡粉和牛奶?”
資料片段:
[1] {Chunk_A_clean}
[2] {Chunk_B_clean}
輸出格式:
– 先給出配方用量(粉、奶、溫度)
– 再給3步以內的簡要步驟
– 最后標注引用,如:[1][2]
這里其實不難,我們這里再插一句向量化。
向量化
切片的目的是為了存入向量庫方便后期檢索,這里需要進行的一步就是文本向量化:
向量化是將文字轉換為高維空間中的坐標點(如512維向量 [0.24, -0.57, …, 0.83]),讓機器能計算語義相似度(距離近=語義相關)。
這里還是舉個例子,讓大家有更具象化的認知:
# 測試文本
query =?“酸味明亮的咖啡豆”
doc1 =?“埃塞俄比亞耶加雪菲:柑橘酸感突出”
doc2 =?“巴西咖啡:堅果巧克力風味,低酸度”
# 向量模型1:通用模型 text-embedding-ada-002
vec_query_ada = [0.12, -0.45, 0.23, …] ?# 維度示例
vec_doc1_ada = [0.08, -0.41, 0.19, …]
vec_doc2_ada = [-0.33, 0.72, -0.15, …]
# 計算余弦相似度
sim_ada_doc1 = 0.68 ?# query與耶加雪菲
sim_ada_doc2 = 0.62 ?# query與巴西
# 向量模型2:領域模型 BGE-large-zh
vec_query_bge = [0.87, -0.12, 0.64, …]
vec_doc1_bge = [0.82, -0.08, 0.61, …]
vec_doc2_bge = [-0.24, 0.33, -0.47, …]
sim_bge_doc1 = 0.92 ?# 顯著提升!
sim_bge_doc2 = 0.31 ?# 無關項被壓制
這個簡單的案例,大家可以清晰看出query與doc1更為貼合。
這里再舉一個反面案例,當壞向量遇上檢索會怎么樣?
# 步驟1:向量化(使用text-embedding-ada-002)
– Query向量:`酸味明顯的咖啡豆` → [0.21, -0.33, 0.47, …]
– 相關文檔向量:
– 正例《耶加雪菲》:”柑橘酸感明亮”?→ [0.18, -0.29, 0.42, …] ?# 相似度0.75
– 干擾文檔向量:
– 反例《巴西咖啡》:”低酸度”?→ [0.24, -0.35, 0.39, …] ?# 相似度0.82! (錯誤更高)
# 步驟2:向量檢索(Top 2召回)
| 排名 | 文本 ? ? ? ? ? ? ? ? ? ? ?| 相似度 | 實際內容 ? ? ? ? ? ? ? |
|——|—————————|——–|————————|
| 1 ? ?| 巴西咖啡:堅果巧克力風味 ?| 0.82 ? | **低酸度**(干擾項?。?|
| 2 ? ?| 咖啡因含量對照表 ? ? ? ? ?| 0.78 ? | 無關表格 ? ? ? ? ? ? ? |
| 3 ? ?| 耶加雪菲:柑橘酸感明亮 ? ?| 0.75 ? | 正確答案被擠出Top2 ? ? |
# 步驟3:生成答案
輸入Prompt:
“巴西咖啡:堅果巧克力風味,低酸度”
“羅布斯塔豆咖啡因含量:2.7%,阿拉比卡豆:1.5%”
問題:酸味明顯的咖啡豆推薦?
如果知識問答是這樣的話,就會出問題:推薦巴西咖啡,它具有堅果風味且酸度較低。
正確的索引帶來了錯誤的回答,這種情況在RAG技術中也不是個例,遇到這種問題,多半就要引入數據工程與飛輪系統(tǒng)了,并且可能會涉及部分微調。
最后,在真實使用過程中會對問題進行重寫,比如:
擴展前Query:”酸味明顯的咖啡豆”
擴展后Query:”酸度 或 酸味 或 明亮酸質 的 咖啡豆 品種”
向量化的意義
最后發(fā)散一下,大家其實也發(fā)現(xiàn)了:RAG技術其實并不非要依賴向量庫,也就是只要能將知識搜索出來,事實上并不一定需要向量化。
而RAG技術在2年多之前普遍被大家接受,核心原因有兩個:
- 第一是,當時模型上下文太短,4k、8k、16k是主流(32k都一票難求),在這個基礎上,就算向量庫特別好用,但受限于提示詞長度,其實也不好用;
- 第二是,受限于AI項目認知,并不知道如何組織私有化數據,RAG提供了一個范式,自然而然就用了,至于好不好又再說;
站在這個基礎上,大家事實上可以認為:所謂RAG在初期,事實上也并不好用,因為模型上下文裝不了完整的知識庫,知識庫不全無論如何都回答不好。
因為就是在之前的場景,也不存在將全量數據給模型的可能,將知識變成小片段、將知識進行精煉壓縮以減少長度,這是一種以精度換準度的妥協(xié)。
然后隨著模型技術發(fā)展,模型上下文擴展到足夠大了,RAG反而變得好用了,這也是為什么我前面會說,RAG屬于后訓練(微調)的一個替代方案的原因。
只不過,在上下文如此健壯的今天,另一個問題也就產生了:似乎,向量化意義不大,比如在知識庫不多的時候,全量導入反而是最優(yōu)解。
想象一下,直接把整本《咖啡百科全書》PDF的文本內容(當然,經過必要的清洗和格式優(yōu)化)塞進提示詞,好像也沒什么不好,畢竟也就幾萬字…
所以,也許我們需要的思考的是向量庫在模型階段初期不好用,模型階段后期用不著,重要的可能一直是結構化的知識庫…
進一步的思考
如前所述,向量庫的目的只有一點:將用戶的提問涉及到本地知識的部分搜索出來,僅從這個角度出發(fā),向量檢索完成的功能與模型LLM是類似的,甚至可以粗暴的將向量庫當成“預訓練過的小模型”。
但是,向量化畢竟不是模型訓練,無論分塊策略如何優(yōu)化,向量檢索始終面臨“用固定維度向量表示無限語義”的瓶頸,且其優(yōu)化目標(語義相似性)與下游任務目標(答案精準性)存在天然鴻溝,比如:
# 語義相似高 ≠ 答案支持性強
Query =?“酸味明亮的咖啡豆推薦?”
Doc1 =?“耶加雪菲:柑橘酸感突出(產地埃塞)”??# 高相關!但向量相似度可能被弱化
Doc2 =?“咖啡酸味的化學成因:綠原酸分解”? ? ??# 高相似!但無法直接回答“推薦”
因為根本無法確定用戶會輸出什么莫名其妙的問題,多以一定會存在怎么都索引不到知識的場景,這也許是RAG最大的問題。
當然,我這里并不是要否定向量化,只不過我在思考其更好的用法,以我們的某次實踐為例:也許我們可以設計一套結構化的知識庫,比如知識圖譜。
然后我們對向量索引的使用僅僅壓縮到,對關鍵Key的篩選,比如我們結構化的知識庫中存的是完善的疾病信息,而向量庫中存的是癥狀與疾病的映射信息。
在檢索時,我們只需要關注用戶的描述應該是什么癥狀,再從相關的癥狀向量中將可能關聯(lián)的疾病檢索出來,而后我們直接使用結構化的疾病庫即可。
知識載體-語義路由
這里所謂將向于關鍵Key篩選,其本質是將向量庫從知識載體降級為語義路由器,其實也是一種數據工程的混合架構了…
舉個例子:
- 患者描述“心口疼”被向量匹配到《心肌梗死護理指南》(語義相似度高)
- 實際病因卻是胃食管反流(需關聯(lián)“飯后平躺加重”等非直接相似特征)
如果這里只將向量庫作為語義路由的話,情況會有所變化:
A[患者描述:“飯后心口灼燒樣疼”] –> B(向量癥狀路由器)
B –> C[“灼燒感”聚類:胃酸反流癥狀組]
C –> D{知識圖譜路由}
D –> E1[疾病庫:胃食管反流病]
D –> E2[疾病庫:心絞痛]
E1 –> F[關聯(lián)“體位誘發(fā)”特征:陽性]
E2 –> F[關聯(lián)“運動誘發(fā)”特征:陰性]
F –> G[確診:胃食管反流病]
在這個案例里面,向量層僅完成癥狀語義聚類(將“心口疼”映射到“胸痛癥狀組”),其余工作交給知識圖譜(或者結構化的知識庫)。
該框架的實現(xiàn)框架為二:
- 語義向量庫以及結構化的知識庫:向量庫(語義路由器):專注于理解用戶意圖的自然語言表達,將其映射到預先定義好的、結構化的關鍵概念、類別或索引鍵上。它的核心能力是“理解用戶問的是什么(領域/主題/意圖)”。
- 結構化知識庫(知識載體):存儲經過精心組織、清洗、關聯(lián)的領域知識。形式可以是知識圖譜、關系數據庫、文檔數據庫(包含強元數據)、甚至規(guī)則庫。它的核心能力是“精準、高效、結構化地回答基于關鍵鍵的查詢”。
他核心目標其實是要解決語義相似 ≠ 答案相關的問題,向量路由只負責理解意圖并將其路由到最相關的“知識抽屜”(如“胸痛癥狀組”、“拿鐵配方庫”),而非直接返回可能包含干擾信息的原始文本片段。
后續(xù)由結構化的知識庫基于精確的鍵進行查詢,確保返回的信息與查詢目標高度一致。
這是一種更貼近人類認知的做法,人類在解答問題時,也是先理解問題意圖(路由),然后在結構化的知識體系(記憶、手冊、數據庫)中查找相關信息,最后進行推理和表達(生成)。
只不過,該架構的挑戰(zhàn)也很明顯:其結構化的知識庫十分難設計,并且要考慮其如何與路由向量庫交互,整體工程難度是很高的。
最后給一個流程圖:
用戶Query
└─? 語義路由層(多信號融合)
├─ 向量近鄰召回(癥狀/意圖/主題簇)
├─ 關鍵詞/BM25/正則/實體識別
└─ 輕量規(guī)則(黑白名單、領域優(yōu)先級)
│
▼
路由決策(Top-N 目標:{實體、關系、表、規(guī)則集、API})
│
▼
知識載體層(結構化)
├─ 知識圖譜(實體-關系-約束)
├─ 事實表/維度表(指標、版本、地域)
├─ 規(guī)則引擎(閾值、if-then、時效)
└─ 特征/檢驗庫(醫(yī)療、法務、品控)
│
▼? 生成層(可選)
├─ 模板化口徑(嚴謹場景:醫(yī)療、法務)
└─ LLM 語言潤色(附引證與可追溯ID
結語
好了,今天我們重新梳理了RAG技術的核心鏈路、剖析了向量化的雙面性,并大膽探討了向量路由+結構化知識庫的混合架構。
當然,文章可能有些地方有歧義,比如:向量化意義不大
這里我也不是要否定向量技術的價值,而是在思考他更好的使用場景是什么,畢竟全部丟給大模型的話,成本上面也會高不少。
整體文章如上,一不小心又寫多了,希望對各位有幫助…
本文由人人都是產品經理作者【葉小釵】,微信公眾號:【葉小釵】,原創(chuàng)/授權 發(fā)布于人人都是產品經理,未經許可,禁止轉載。
題圖來自Unsplash,基于 CC0 協(xié)議。
確實,現(xiàn)在做的項目,也是基于RAG的,在語料內的問題,回答質量很高,語料外的就特別低;這些語料的整理成本也非常高;做一個折中的方案,其實也可以試試;