iT邦幫忙

2026 iThome 鐵人賽

DAY 19
0
AI Engineering

地端 AI 建築學系列 第 19 篇

19 案例三:RAG(5)RAG Agent 設計

  • 分享至 

  • xImage
  •  

上一篇聊完了意圖分析,知道使用者問的問題該往哪個方向查。但在那之前,其實有一件更基礎、也更容易被低估的事情 — 你的資料到底要怎麼「切」、怎麼「向量化」。這件事沒做好,意圖分析再準、ReRank 再強,撈回來的東西還是不對,整個 RAG 就是地基歪掉。

這篇先把 RAG Agent 整體架構講一遍,再一項一項拆開細節。


我們的 RAG Agent 架構

https://ithelp.ithome.com.tw/upload/images/20260929/20181345Og4kjekwkP.png

資料層分成三塊:

  • Info(Qdrant):細粒度的內容向量,負責「精準命中」
  • Summary(Qdrant):摘要層級的向量,負責「大方向判斷」
  • Products(MariaDB):完整、未切分的原始資料,負責「補全內容」

圖中 Retrieval (Small-to-Big) 呼應的正是 LlamaIndex 裡 Sentence Window Retrieval(Metadata Replacement) 這個技巧背後的精神:

向量比對用「小」的單位做(因為小單位的語意向量比較精準、不會被稀釋),但真正要組出回答時,回頭抓「大」的完整內容給 LLM 看。

LlamaIndex 官方的具體做法是:用 SentenceWindowNodeParser 把文件拆成一句一句(node),每個 node 只拿單一句子去做 embedding;同時每個 node 的 metadata 裡都存一個「window」——前後各幾句的完整上下文(句數可自行設定)。檢索的時候,比對的是那顆最精準的單句向量;命中之後,再用 MetadataReplacementPostProcessor 把單句換成完整的 window,最後才丟給 LLM 生成答案。

為什麼要這樣拆?因為如果直接拿一大段文字去 embedding,向量其實是整段文字語意:「較長的文字容易稀釋特定關鍵字的訊號」,遇到像特定專有名詞這種很具體的關鍵字,反而容易被稀釋、比對不精準;但如果只拿單句去比對,命中率會明顯提升 — LlamaIndex 官方 demo 就示範過,同一個問題,句子級索引答得出來,段落級索引(chunk 切太大)反而答不出來,除非把 top_k 硬拉高。

回到架構圖:Info / Summary(Qdrant)扮演的就是「小單位、精準比對」的角色,而 MariaDB(Products)扮演的是「補回大內容」的角色——只是「補大」的方式不是靠 node metadata 裡存的 window,而是直接回查資料庫抓完整、結構化的原始資料。概念上是同一套 Small-to-Big 精神,只是把「句子 vs. 句子窗口」換成了「向量摘要 vs. 資料庫完整內容」這種更粗的顆粒度,實作載體不同而已。

理解這個大方向之後,接下來拆兩個主題:怎麼切(Chunking)、怎麼向量化(Embedding)。


切分策略(Chunking)怎麼做

先想清楚你在切什麼

不同來源的資料,切法不會一樣:

  • 結構化清楚的文件(有標題、章節的技術文件、SOP)→ 適合照結構切

  • 一大坨敘述性文字(產品介紹、FAQ 長文)→ 適合語意或固定長度切

  • 表格、規格資料 → 通常不切文字,而是轉成結構化欄位另外處理(這也是為什麼圖上 Products 是另外一個 MariaDB,而不是塞進向量庫)

先分類資料型態,再決定切法,而不是全部套同一套規則硬切,這是最容易被忽略但影響最大的一步。

常見切法比較

切法 概念 優點 缺點
固定長度切分 每 N 個字/token 切一刀 簡單好實作 常常把一句話從中間切斷,語意破碎
Recursive 遞迴切分 先照段落/句子切,長度超過上限才往下細切 比固定長度自然一點 還是可能切到語意邊界上
語意切分(Semantic) 用 embedding 相似度判斷「這段跟下一段像不像」,像的留一起 有機會提升語意完整度 運算成本較高,慢
結構感知切分 依 Markdown 標題、HTML 標籤、章節編號切 天然對齊文件邏輯 依賴資料本身有清楚結構

Small-to-Big 具體怎麼落地:兩種常見做法

做法一:Sentence Window(LlamaIndex 的標準做法)

  • 用 SentenceWindowNodeParser 把文件拆到「句子」這麼細的粒度,一句一個 node
  • 每個 node 的 metadata 存一個 window(前後各幾句,數字自訂)
  • 只拿單句去 embedding、去比對相似度
  • 命中後用 MetadataReplacementPostProcessor 把單句換成完整 window,再丟給 LLM

這個做法的「大 / 小」是在同一個 index 裡、靠 metadata 切換完成的,不需要另外查資料庫。

做法二:Parent-Child/Parent Document Retriever(LangChain 常見的變形)

  • 文件先切成較大的 parent chunk(保留完整語意脈絡)
  • 每個 parent 再切成較小的 child chunk
  • 向量庫裡只存 child 的 embedding(負責精準比對)
  • 命中 child 之後,回傳它所屬的 parent,或進一步回資料庫撈更完整的原文

這個做法的「大 / 小」是預先切好、分層儲存的,檢索到小的之後,用 ID 去換回大的——這正是你架構圖裡「從 DB 查詢完整內容(有需要時)」在做的事。

小提醒:這兩種做法不是互斥的,甚至可以疊加——例如 child chunk 內部也用句子級拆分去產生更細的候選,再視情況決定要不要拉到 parent。實務上通常先選一種做基礎版本,跑出問題再考慮要不要疊加。

chunk size 與 overlap 怎麼抓

以下抓的是**做法二(Parent-Child/DB 回查)**的經驗值;如果走 Sentence Window,「chunk size」這個概念基本不存在,要調的是句子拆分品質跟 window 要抓幾句,邏輯不太一樣,抓法會另外談。

沒有絕對正確的數字,但可以抓一個經驗起點再慢慢調:

  • Child chunk:大約 300~500 tokens,overlap 抓 10~20%(例如 50~100 tokens),避免切在句子中間造成語意斷裂

  • Parent chunk:以「一個小節」或「一個完整主題」為單位,不特別限制固定字數

  • 判斷標準只有一個:這個 chunk 單獨拿出來看,人類看得懂在講什麼嗎?看不懂,代表切太碎了

Metadata 千萬別漏

每個 chunk 存進 Qdrant 的時候,一定要附上:

  • 對應到 MariaDB 的主鍵(回查完整內容用)
  • 來源文件、章節資訊(方便 ReRank 階段判斷可信度、也方便除錯)
  • parent chunk 的 ID(child 要能找到自己的 parent)

Embedding 架構怎麼設計

為什麼要分 Info 跟 Summary 兩個 Pool

因為使用者問問題的「顆粒度」本來就不一樣。有人問很細節的規格問題,適合去 Info Pool 精準比對;有人問「幫我整理一下這個產品系列大概是什麼」,這種概略性問題丟到細碎的 Info chunk 裡反而比不準,交給 Summary Pool(每個 parent 先用 LLM 產生一段摘要再向量化)會準得多。

這也解釋了架構圖上「根據意圖分析選擇 Pool」那條線——意圖分析的產出,其實就是在幫檢索階段決定「這次要用哪把尺去量」。

Embedding model 怎麼選

這塊選型會隨時間一直變動,但決策邏輯大致固定,抓幾個維度來想:

  • 地端 vs API:這個系列講的是地端 AI 架構,如果資料敏感或希望完全掌控成本,開源、可以本機/內網跑的模型會是首選;如果不介意資料出網,API 型的模型通常開發體驗比較省事。

  • 語言支援度:中文(尤其繁體)場景要特別測過,不能只看英文評測榜單就選型,因為很多榜單是以簡體語料為主,繁體表現不一定對等。

  • 文本長度:長文件、長段落多的話,要挑對長文本支援較好的模型,不然超長 parent chunk 會被截斷。

以目前(2026 年)的大方向來說,開源陣營裡 BGE-M3 這類多語言模型是地端部署很常見的起手式,成本可控、多語言覆蓋廣;如果對中文檢索品質要求更高、也有足夠算力,Qwen 系列的 Embedding 模型近期在中文評測上表現相當亮眼,但相對更吃資源。

維度與儲存成本

向量維度越高,理論上語意保留越完整,但儲存空間和檢索速度的代價也越大。有些新一代模型支援「降維」(例如 Matryoshka 表示法),可以把向量維度砍到一半甚至更低,換來大幅節省儲存空間,品質損失通常在可接受範圍內——但降多少會開始明顯影響準確率,一定要自己測,不要憑感覺設。

一個容易踩的坑:模型要前後一致

Query 端跟 Document 端一定要用同一個 embedding 模型,依該模型規範分別處理 Query 與 Document去產生向量,兩邊對不齊,相似度算出來就是亂的。更麻煩的是,如果之後想換模型(例如換一個評測分數更高的新模型),代表 Qdrant 裡所有既有的向量都要重新算過一次,這是不小的工程成本,選型階段就該想清楚,不要之後才後悔。


小結:實作流程整理

把上面講的串成一個實際的處理流程:

  1. 文件前處理:清洗雜訊(頁首頁尾、亂碼、重複段落),統一格式
  2. Parent chunk 切分:依文件結構切出大塊,保留完整語意
  3. Child chunk 切分:從每個 parent 再細切,附上 overlap
  4. (可選)產生摘要:對每個 parent 呼叫 LLM 產生摘要文字,準備進 Summary Pool
  5. Embedding:child chunk → 向量 → 寫入 Info(Qdrant);摘要文字 → 向量 → 寫入 Summary(Qdrant)
  6. 寫入 Metadata:每筆向量都帶上來源 ID、parent ID、對應 MariaDB 主鍵
  7. 完整內容落地:原始/結構化內容存進 MariaDB(Products),供 Small-to-Big 回查使用

上一篇
18 案例三:RAG(4)意圖分析實作 II - 模組本體建立
下一篇
20 案例三:RAG(6)RAG Agent 實作
系列文
地端 AI 建築學 共 22 篇
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言