博客 / 詳情

返回

Agentic RAG:用LangGraph打造會自動修正檢索錯誤的 RAG 系統

標準 RAG 流水線有個根本性的毛病:檢索到的文檔一旦與用户意圖對不上號,模型照樣能面不改色地輸出一堆看似合理的胡話,既沒有反饋機制也談不上什麼糾錯能力。

而Agentic RAG 的思路截然不同,它不急着從檢索結果裏硬擠答案,而是先判斷一下拿回來的東西到底有沒有用,如果沒用則會重寫查詢再來一輪。這套機制實際上構建了一條具備自我修復能力的檢索鏈路,面對邊界情況也不至於直接崩掉。

本文要做的就是用 LangGraph 做流程編排、Redis 做向量存儲,搭一個生產可用的 Agentic RAG 系統。涉及整體架構設計、決策邏輯實現,以及狀態機的具體接線方式。

傳統 RAG 的"一錘子買賣"

假設知識庫裏有一篇《大語言模型的參數高效訓練方法》,用户問的是"怎麼微調 LLM 效果最好"。

語義相似度確實存在但不夠強。檢索器拉回來的可能是模型架構相關的內容雖然沾邊但答非所問,LLM 本身沒法意識到上下文是錯的,照樣能生成一段貌似專業實則離題萬里的回答。

傳統 RAG 對這種失敗模式完全沒有辦法。查詢文檔、生成答案,整個過程是單向的沒有任何質量把關環節。

Agentic RAG 的解法是在流程中插入檢查點:智能體先判斷要不要檢索;檢索完了有評分環節確認相關性;不相關就重寫查詢再試;如此循環直到拿到合格的上下文,或者把重試次數耗盡為止。

系統架構拆解

整個系統拆成六個模塊:

配置層負責環境變量和 API 客户端的初始化工作。Redis 連接串、OpenAI 密鑰、模型名稱全部歸攏到這裏統一管理。

檢索器模塊承擔文檔攝取的全套流程,文檔經過

WebBaseLoader

加載後用

RecursiveCharacterTextSplitter

切塊,再通過 OpenAI Embedding 向量化,最後存進

RedisVectorStore

。檢索器本身會被包裝成 LangChain 工具供智能體調用。

智能體節點是決策入口。拿到用户問題後先做判斷:這個問題需要查資料還是直接能答?需要查就調檢索器,不需要就直出答案。

評分(Grade Edge)決定檢索結果的去向。相關性夠就往生成環節走;不夠就觸發重寫。這是整個系統裏最關鍵的質量關卡。

重寫節點把原始問題改寫成更適合檢索的形式,用户表述太口語化、缺少關鍵詞,這些問題都在這裏修正。

生成節點只有在評分環節確認上下文合格後才會執行,基於檢索到的文檔產出最終答案。

流程圖和代碼

關鍵在於從"重寫"回到"智能體"這條反饋路徑。系統不會因為一次檢索失敗就直接給出一個牽強附會的答案,它會調整策略重新嘗試。

 src/  
├── config/  
│   ├── settings.py      # 環境變量  
│   └── openai.py        # 模型名稱和 API 客户端  
├── retriever.py         # 文檔攝取和 Redis 向量存儲  
├── agents/  
│   ├── nodes.py         # 智能體、重寫和生成函數  
│   ├── edges.py         # 文檔評分邏輯  
│   └── graph.py         # LangGraph 狀態機  
 └── main.py              # 入口點

職責劃分很清晰:配置歸

config/

,智能體相關的都在

agents/

,向量存儲操作全在

retriever.py

。這種結構調試起來方便,單測也好寫。

配置模塊設計

配置層解決兩個問題:環境變量加載和 API 客户端複用。

settings.py

集中讀取 Redis 連接信息、OpenAI API Key、索引名稱,不用滿項目找配置。

openai.py

負責實例化 Embedding 模型和 LLM 客户端。切換到別的模型、調整 Embedding 維度等等配置也只要一處

這個設計在生產環境裏很實用,因為模型會迭代、Key 會輪換、服務商可能換掉,集中管理意味着改動成本可控。

檢索器實現

檢索器負責整條數據攝取鏈路:抓文檔、切塊、向量化、入庫。

語料選的是 Lilian Weng 關於 Agent 和 Prompt Engineering 的博客文章。

WebBaseLoader

負責抓取,

RecursiveCharacterTextSplitter

切分成適當大小的塊,OpenAI Embedding 完成向量化。

向量存儲用

RedisVectorStore

。檢索器通過

create_retriever_tool

封裝成 LangChain 工具形態。這一步的意義在於讓智能體能夠"調用"檢索而不是被動觸發,意味着它有權決定什麼時候需要查資料、什麼時候直接回答。

為什麼用Redis?因為夠快,夠簡單。向量相似度搜索本身 Redis 就能做,不用額外引入專門的向量數據庫。對於已經跑着 Redis 的技術棧來説,加 RAG 能力幾乎零額外運維負擔。

智能體節點

nodes.py

裏有三個核心函數。

智能體函數接收當前狀態(用户問題、歷史對話等),判斷下一步怎麼走。它能調用包括檢索器在內的工具集。問題需要外部知識就調檢索,不需要就直接生成回答。

重寫函數處理那些被評分環節打回來的查詢。它會讓 LLM 把原始問題改寫成檢索友好的形式,用詞更精準、關鍵信息更突出。改寫後的查詢再交回智能體重新發起檢索。

生成函數產出最終答案。輸入是原始問題加上已確認相關的文檔,輸出是基於這些上下文的回答。

三個函數都是無狀態的。狀態走圖,不走函數內部變量。這對測試和排查問題都有好處。

文檔評分邏輯

edges.py

裏的

grade_documents

是整個 Agentic 機制的核心。

檢索完成後它會逐個審視返回的文檔:這東西跟用户問的相關不相關?能不能幫上忙?

評分本身是通過一次 LLM 調用完成的,Prompt 設計成要求模型返回二元判斷——相關或者不相關。

判定相關就返回

"generate"

,流程走向答案生成;判定不相關則返回

"rewrite"

,觸發查詢改寫。

這個環節的價值在於攔截那些本會導致標準 RAG 胡説八道的情況,與其硬着頭皮從不靠譜的上下文裏編答案,不如給系統一次修正查詢的機會。

狀態機接線

graph.py

用 LangGraph 的狀態機原語把所有節點串起來。

圖結構定義了節點(智能體、檢索、生成、重寫)和邊(節點間的連接關係,包括基於評分結果的條件路由)。

接線邏輯如下:查詢先到智能體節點,智能體決定調檢索器的話流程就到檢索節點,檢索完進評分,評分過了走生成,沒過走重寫,重寫完的查詢再回智能體重新來過。生成節點執行完流程結束。

LangGraph 接管狀態流轉的細節。每個節點只管接收當前狀態、返回狀態更新,具體消息怎麼路由由圖引擎根據邊的條件邏輯處理。

運行時流程

main.py

是入口,做三件事:構建圖、接收問題、流式輸出結果。

build_graph()

在啓動時執行一次,完成 LangGraph 狀態機的構建和檢索器工具的初始化.

問題進來之後的流轉過程:智能體接收問題決定調檢索 → Redis 返回文檔 → 評分環節判斷相關性 → 相關就生成答案,不相關就重寫查詢繼續循環。

腳本會把各節點的輸出實時打到控制枱,方便觀察決策過程——什麼時候觸發了檢索、評分結果如何、有沒有走到重寫環節,一目瞭然。

架構的優勢

自校正能力:檢索質量差能發現並修復,不會悶頭輸出一個基於垃圾上下文的錯誤答案然後假裝沒事發生。

決策透明:狀態機讓每個分支點都是顯式的。路由決策可以全量記錄,想排查為什麼系統選擇了重寫而不是直接生成,日誌裏全有。

模塊解耦:每個組件職責單一。想把 Redis 換成 Pinecone?改檢索模塊。想把 OpenAI 換成 Anthropic?改配置層。其他部分不受影響。

總結

標準 RAG 把檢索當黑盒,查詢丟進去、文檔出來,至於相不相關全憑運氣。Agentic RAG 打開這個黑盒在關鍵位置加了質量控制。

LangGraph 加 Redis 的組合提供了一個可以直接上生產的骨架。流程編排的複雜度 LangGraph 消化掉了,向量檢索的性能 Redis 兜住了,剩下的評分和重寫邏輯負責兜底那些簡單系統搞不定的邊角案例。

代碼:

https://avoid.overfit.cn/post/a45e19af576a4826a605807d8fcfe298

作者:Kushal Banda

user avatar
0 位用戶收藏了這個故事!

發佈 評論

Some HTML is okay.