RAG實踐技巧:將向量庫降級為“語義路由器”,讓答案更合理

1 評論 2081 瀏覽 3 收藏 23 分鐘

隨著模型上下文長度的增加和知識庫的擴展,傳統(tǒng)的向量化方法在RAG中的應用逐漸暴露出其局限性。本文將深入探討RAG技術的核心鏈路,分析向量化在其中的作用,并提出一種新的架構:將向量庫降級為“語義路由器”,結合結構化的知識庫,以解決語義相似性與答案相關性之間的矛盾,從而讓生成的答案更加合理和精準。

Dify與Langchain都可以被歸屬到Agent平臺,可以幫助用戶快速生成各種Agent,只不過兩者的定位與使用對象是不同的:

  1. Dify的定位是低/零代碼平臺,使用對象甚至可以是HR和財務;
  2. 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)框架為二:

  1. 語義向量庫以及結構化的知識庫:向量庫(語義路由器):專注于理解用戶意圖的自然語言表達,將其映射到預先定義好的、結構化的關鍵概念、類別或索引鍵上。它的核心能力是“理解用戶問的是什么(領域/主題/意圖)”。
  2. 結構化知識庫(知識載體):存儲經過精心組織、清洗、關聯(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é)議。

更多精彩內容,請關注人人都是產品經理微信公眾號或下載App
評論
評論請登錄
  1. 確實,現(xiàn)在做的項目,也是基于RAG的,在語料內的問題,回答質量很高,語料外的就特別低;這些語料的整理成本也非常高;做一個折中的方案,其實也可以試試;

    來自寧夏 回復