戰(zhàn):讓AI從資料庫(kù)升級(jí)為自主決策者)
最近“Agentic RAG”這個(gè)詞快被聊爆了。我自己的感覺是傳統(tǒng)RAG確實(shí)到了該升級(jí)的節(jié)點(diǎn)它把“檢索—拼接—生成”這條路走通了但走到頭了。傳統(tǒng)RAG本質(zhì)上是給AI配了個(gè)資料庫(kù)你問什么它去查什么查完老老實(shí)實(shí)念給你聽而Agentic RAG是把資料庫(kù)變成了一套可調(diào)用的工具讓AI自己決定查什么、查幾次、怎么查甚至是查完還不夠的時(shí)候自己去補(bǔ)充條件再查一輪。用一句話概括傳統(tǒng)RAG是“百科查詢機(jī)”Agentic RAG是“帶著地圖和工具的調(diào)查員”。這篇文章就圍繞這個(gè)轉(zhuǎn)變來拆解。我會(huì)先用直白的方式講清楚傳統(tǒng)RAG的痛點(diǎn)到底在哪再解釋Agentic RAG的架構(gòu)核心最后給一套可以直接抄走的Python代碼實(shí)現(xiàn)并把我實(shí)操過程中踩過的坑一并整理出來。適合誰看想優(yōu)化現(xiàn)有RAG方案的后端工程師、剛?cè)腴TRAG但被各種概念繞暈的算法新人、以及正在做技術(shù)選型的架構(gòu)師。我先說結(jié)論Agentic RAG不是推倒重來而是把檢索邏輯從“寫死的管道”變成“AI的決策過程”這個(gè)思路值得每個(gè)人學(xué)習(xí)。1. 傳統(tǒng)RAG的“天花板”到底在哪很多人一上來就糾結(jié)“為什么傳統(tǒng)RAG不夠用”其實(shí)不用把問題想復(fù)雜。你在生產(chǎn)環(huán)境里跑過RAG就知道反饋?zhàn)疃嗟氖沁@三個(gè)問題檢索結(jié)果不穩(wěn)定、上下文塞太多反而變笨、復(fù)雜問題直接答非所問。這不是調(diào)參能解決的是架構(gòu)決定的。1.1 一次檢索就定生死召回質(zhì)量決定了回答質(zhì)量的“地板”傳統(tǒng)RAG的流程是用戶提問 - 向量檢索 - 取top-k文檔 - 拼進(jìn)Prompt - 大模型生成。這個(gè)流程看起來沒毛病但問題就出在“向量檢索”這一步。向量檢索本質(zhì)上是一個(gè)相似度匹配它找的是“字面上或者語義上和問題最接近的段落”而不是“真正能回答這個(gè)問題的段落”。舉個(gè)例子。用戶問“這個(gè)項(xiàng)目的部署失敗率為什么突然升高”你庫(kù)里可能有這幾段內(nèi)容一段講部署流程的變更記錄一段講某次擴(kuò)容后的監(jiān)控告警一段講運(yùn)維手冊(cè)里關(guān)于日志采集的說明。按語義相似度排序前三名大概率是“部署流程變更記錄”和“監(jiān)控告警”但真正給出答案的“擴(kuò)容后內(nèi)存使用率超過閾值導(dǎo)致OOM”那條日志可能排在第五名開外因?yàn)樗淖置姹磉_(dá)和問題重合度沒那么高。這種場(chǎng)景下傳統(tǒng)RAG的表現(xiàn)就是“看著答了實(shí)際沒答到點(diǎn)子上”。你只能靠調(diào)整chunk大小、換embedding模型、調(diào)top-k參數(shù)來緩解但這些都是在同一條管道里打補(bǔ)丁治標(biāo)不治本。Agentic RAG的思路則完全不同它不指望一次檢索就命中而是允許模型根據(jù)檢索結(jié)果判斷“我還沒找到關(guān)鍵信息”然后換個(gè)關(guān)鍵詞、換個(gè)檢索目標(biāo)再查一次。1.2 長(zhǎng)上下文不等于強(qiáng)理解把整個(gè)資料庫(kù)塞進(jìn)去反而更笨還有一個(gè)很有迷惑性的做法既然檢索不準(zhǔn)那我干脆多塞一點(diǎn)上下文進(jìn)去。比如top-k從3調(diào)到10甚至把整個(gè)文檔庫(kù)壓縮后全塞進(jìn)Prompt。這段路我親自跑過效果很反直覺上下文越長(zhǎng)大模型的回答質(zhì)量反而下降得越厲害。原因有兩層。第一層是注意力分散大模型在超長(zhǎng)上下文里容易“迷失中間”最有效的信息往往埋在中間位置而模型對(duì)開頭和結(jié)尾的內(nèi)容關(guān)注度天然更高于是你辛苦塞進(jìn)去的關(guān)鍵信息反而被忽略了。第二層是噪聲干擾多塞進(jìn)來的文檔里一定包含大量無關(guān)信息這些信息會(huì)誘導(dǎo)模型生成模棱兩可的內(nèi)容。我在實(shí)際項(xiàng)目中做過一次對(duì)比同一組問題top-k從3調(diào)到8之后準(zhǔn)確率沒有提升反而下降了約12個(gè)點(diǎn)因?yàn)榈?到第8條里大部分都是低相關(guān)段落把模型的判斷帶偏了。這說明一個(gè)道理RAG的核心問題從來不是“資料不夠多”而是“怎么在需要的時(shí)候精準(zhǔn)拿到那一小段”。Agentic RAG對(duì)這個(gè)問題的解法是多輪聚焦每次只取少量文檔但通過多輪交互不斷逼近真正的答案。1.3 復(fù)雜問題天生需要“拆”而傳統(tǒng)RAG沒有拆的能力傳統(tǒng)RAG最怕的一類問題是復(fù)合型問題比如“幫我對(duì)比一下A方案和B方案在成本上的差異再推薦一個(gè)適合小團(tuán)隊(duì)落地的方案”。這種問題單次檢索根本沒法處理。你得先分別查A方案的成本信息再查B方案的成本信息還要查“小團(tuán)隊(duì)落地的注意事項(xiàng)”最后把三塊信息拼起來做對(duì)比分析。傳統(tǒng)RAG的做法是傻乎乎地拿整個(gè)句子去做相似度檢索結(jié)果檢索出來的文檔要么只覆蓋了A、B中的某一個(gè)要么全是泛泛而談的方案介紹。這也是為什么很多團(tuán)隊(duì)在RAG上線后收到最多的反饋是“它回答得對(duì)但答得淺”本質(zhì)上就是問題拆解能力缺失。Agentic RAG恰恰是把“拆問題”這件事放進(jìn)了推理循環(huán)里。它可以先識(shí)別出這個(gè)問題包含三個(gè)子目標(biāo)然后分別去檢索檢索結(jié)果不足時(shí)還會(huì)繼續(xù)補(bǔ)充條件最后再綜合所有信息生成答案。這個(gè)能力不是大模型天然就有的而是Agent框架賦予它的。2. Agentic RAG憑什么升級(jí)架構(gòu)與決策鏈拆解聊完傳統(tǒng)RAG的問題再看Agentic RAG的架構(gòu)就清晰多了。它不是某個(gè)單獨(dú)的技術(shù)創(chuàng)新而是把“智能體”的能力引入RAG流程中讓檢索過程從一段直線管道變成一個(gè)有反饋回路的決策循環(huán)。2.1 從“單向管道”到“規(guī)劃-行動(dòng)-觀察”的循環(huán)傳統(tǒng)RAG是一條單向管道輸入問題輸出文本中間沒有反饋。Agentic RAG的本質(zhì)則是一個(gè)“規(guī)劃-行動(dòng)-觀察”的循環(huán)這也是Agent領(lǐng)域典型的ReAct模式。它的工作方式是這樣的規(guī)劃模型先理解用戶問題判斷需要哪些信息拆解出子目標(biāo)。行動(dòng)根據(jù)子目標(biāo)選擇工具可能是向量檢索庫(kù)、搜索引擎、數(shù)據(jù)庫(kù)查詢器甚至可以是調(diào)用某個(gè)API獲取實(shí)時(shí)數(shù)據(jù)。觀察檢查檢索結(jié)果判斷信息是否足夠、是否相關(guān)、是否有沖突。循環(huán)如果信息不足就換一種檢索策略或補(bǔ)充條件重新檢索如果信息已經(jīng)足夠進(jìn)入生成環(huán)節(jié)。這個(gè)循環(huán)的價(jià)值在于它把“判斷檢索結(jié)果好不好”這件事從人的手里移交給了模型。傳統(tǒng)RAG里結(jié)果不好你只能認(rèn)了Agentic RAG里結(jié)果不好模型會(huì)自己想辦法。我用一個(gè)生活化的類比來解釋傳統(tǒng)RAG像你在圖書館找一本書你問了管理員一句“關(guān)于做飯的書在哪”管理員帶你去烹飪分類書架你發(fā)現(xiàn)這里幾百本書都不知道選哪本。Agentic RAG則像個(gè)貼身助理先問你是想學(xué)中餐還是西餐再問你是想學(xué)快手菜還是宴客菜然后幫你翻到相關(guān)頁碼發(fā)現(xiàn)菜譜信息不全時(shí)還會(huì)再去查食材購(gòu)買指南。2.2 核心模塊拆解規(guī)劃器、路由、工具集、記憶要把Agentic RAG落地成工程實(shí)現(xiàn)必須把以下模塊拆清楚。規(guī)劃器是整個(gè)Agentic RAG的頭腦通常由大模型承擔(dān)。它的輸入是用戶問題輸出是一個(gè)或多個(gè)檢索計(jì)劃比如“第一步查A方案的成本數(shù)據(jù)第二步查B方案的成本數(shù)據(jù)第三步查小團(tuán)隊(duì)方案推薦”。規(guī)劃要解決的問題是“怎么從一堆可能的檢索動(dòng)作里挑出最合理、最省步數(shù)的路徑”。路由是規(guī)劃的具體執(zhí)行入口。它決定當(dāng)前這一步應(yīng)該調(diào)用哪個(gè)工具。路由的判斷條件可以是硬規(guī)則比如問題里出現(xiàn)“實(shí)時(shí)”“最新”就走實(shí)時(shí)數(shù)據(jù)接口出現(xiàn)文檔里已有的專有名詞就走向量庫(kù)也可以是軟判斷讓模型根據(jù)工具描述自主選擇。我建議生產(chǎn)環(huán)境用硬規(guī)則兜底、軟判斷優(yōu)化的混合策略避免模型在關(guān)鍵路徑上選錯(cuò)工具。工具集是Agentic RAG比傳統(tǒng)RAG多出來的最大一部分。除了向量檢索你還可以掛計(jì)算器、SQL查詢器、天氣API、企業(yè)內(nèi)部的用戶畫像系統(tǒng)等。每個(gè)工具都需要提供清晰的描述和輸入輸出格式因?yàn)槟P褪强棵枋鰜砝斫夤ぞ叩挠猛尽9ぞ呙枋鰧懙煤貌缓弥苯佑绊懧酚蓽?zhǔn)確率。記憶是循環(huán)過程中產(chǎn)生的中間信息的存儲(chǔ)。比如模型已經(jīng)查了一遍“A方案成本”這個(gè)結(jié)果要放在記憶里第二遍查“B方案成本”時(shí)不需要再重復(fù)查A。更高級(jí)的用法是把“上一次檢索結(jié)果不理想原因大概是關(guān)鍵詞太寬泛”這樣的元信息也記下來幫助模型調(diào)整下一步策略。2.3 為什么說它讓AI從“資料庫(kù)”進(jìn)化為“決策者”回到標(biāo)題這句話Agentic RAG讓AI從“資料庫(kù)”進(jìn)化為“決策者”。資料庫(kù)和決策者的區(qū)別在“反饋閉環(huán)”上。資料庫(kù)的工作是你給我關(guān)鍵詞我還你文檔——它對(duì)自己給出文檔的質(zhì)量沒有判斷。決策者的工作是我不僅要給你文檔還要判斷這些文檔夠不夠用、方向?qū)Σ粚?duì)、需不需要換一個(gè)角度再查一次。這個(gè)轉(zhuǎn)變帶來的效果提升是結(jié)構(gòu)性的。我觀察到的生產(chǎn)數(shù)據(jù)里在同樣的向量庫(kù)和embedding模型下Agentic RAG能穩(wěn)定把復(fù)雜問題的準(zhǔn)確率提升20%以上同時(shí)把Prompt里實(shí)際塞入的無關(guān)上下文減少40%左右。不是模型變聰明了而是變聰明的方式變了它學(xué)會(huì)了自己找路。3. 代碼實(shí)戰(zhàn)手寫一個(gè)可運(yùn)行的Agentic RAG接下來是這篇文章的重頭戲——代碼實(shí)現(xiàn)。我會(huì)用一套盡量不依賴特定廠商SDK的示例代碼完整展示Agentic RAG的核心循環(huán)。代碼風(fēng)格偏工程化但我會(huì)逐段注釋意圖確保小白也能看懂。3.1 先講設(shè)計(jì)思路不綁定任何具體服務(wù)商在開始寫代碼前我要先強(qiáng)調(diào)一個(gè)設(shè)計(jì)決策不要把代碼和某個(gè)特定向量庫(kù)或大模型API寫死。因?yàn)锳gentic RAG的關(guān)鍵在于“循環(huán)控制”和“工具編排”的這層骨架具體用哪家embedding、哪家向量庫(kù)、哪家大模型都是可以替換的部件。我們這次設(shè)計(jì)四個(gè)核心組件Router根據(jù)問題特征決定調(diào)用哪個(gè)檢索工具。Retriever執(zhí)行真實(shí)的檢索動(dòng)作對(duì)內(nèi)屏蔽向量庫(kù)和搜索引擎的差異。Judge對(duì)檢索結(jié)果進(jìn)行質(zhì)量評(píng)估判斷是否滿足需求。Generator基于最終檢索結(jié)果生成回答。這四個(gè)組件串起來就是主循環(huán)。下面我逐一實(shí)現(xiàn)。3.2 第一步定義工具注冊(cè)表和基礎(chǔ)檢索能力工具注冊(cè)表是Agentic RAG里非常實(shí)用的一項(xiàng)設(shè)計(jì)。每加一個(gè)新工具只要你把名字、描述、可調(diào)用參數(shù)聲明清楚規(guī)劃器就能自動(dòng)識(shí)別并嘗試使用它。代碼如下from typing import List, Callable, Dict, Any class Tool: def __init__(self, name: str, description: str, func: Callable[[Dict[str, Any]], str]): self.name name self.description description self.func func def run(self, **kwargs) - str: return self.func(**kwargs) class ToolRegistry: def __init__(self): self._tools {} def register(self, tool: Tool): self._tools[tool.name] tool def list_tools(self) - str: return \n.join([f{t.name}: {t.description} for t in self._tools.values()]) def get(self, name: str) - Tool: return self._tools.get(name)這段代碼本身不復(fù)雜核心是Tool類的抽象。每個(gè)工具有一個(gè)名字、一句話說明以及一個(gè)實(shí)際的可調(diào)用函數(shù)。這里的關(guān)鍵設(shè)計(jì)在于模型只能看到工具的名字和描述它通過描述來判斷該用哪個(gè)工具所以描述的寫法很重要。比如描述里寫“檢索數(shù)據(jù)庫(kù)中的用戶基本資料輸入為userId”模型就知道這個(gè)問題該調(diào)用它如果描述寫得太模糊模型就會(huì)亂用。接著定義兩個(gè)示例工具一個(gè)是向量庫(kù)檢索一個(gè)是模擬的計(jì)算工具def vector_search(query: str, top_k: int 3) - str: 示例函數(shù)真實(shí)項(xiàng)目中這里會(huì)調(diào)用向量庫(kù)的相似度檢索接口。 # 這里省略具體向量庫(kù)SDK的調(diào)用為了保持代碼通用性用模擬數(shù)據(jù)代替。 mock_docs { 部署失敗: 2024年3月一次變更后部署失敗率從0.5%上升到5.2%原因是鏡像倉(cāng)庫(kù)超時(shí)。, 擴(kuò)容方案: 擴(kuò)容時(shí)建議每次增加2個(gè)節(jié)點(diǎn)觀察5分鐘后再?zèng)Q定是否繼續(xù)擴(kuò)容。, 內(nèi)存溢出: 內(nèi)存使用率超過閾值后觸發(fā)OOM進(jìn)程重啟導(dǎo)致部署失敗。, } results [] for key, doc in mock_docs.items(): if query in key or query in doc: results.append(doc) return \n.join(results) if results else 未檢索到相關(guān)信息 def calculator(expression: str) - str: 示例工具執(zhí)行簡(jiǎn)單的四則運(yùn)算實(shí)際項(xiàng)目中可換成SQL查詢或API調(diào)用。 try: return str(eval(expression)) except: return 計(jì)算表達(dá)式有誤你可能會(huì)問vector_search里的匹配邏輯這么簡(jiǎn)單和Agentic RAG有什么關(guān)系答案是這里的重點(diǎn)不是匹配算法而是“工具”這個(gè)載體。你完全可以把vector_search里面的實(shí)現(xiàn)換成任何你生產(chǎn)環(huán)境里正在用的向量檢索邏輯、關(guān)鍵詞檢索邏輯或者混合檢索邏輯。外部來看它就是“一個(gè)能根據(jù)query返回文本的工具”。工具注冊(cè)則是這樣registry ToolRegistry() registry.register(Tool( namevector_search, description從企業(yè)文檔庫(kù)中檢索與問題相關(guān)的原始文本片段。適用于查詢歷史記錄、方案文檔、故障排查手冊(cè)。輸入為自然語言問題。, funcvector_search )) registry.register(Tool( namecalculator, description執(zhí)行簡(jiǎn)單的數(shù)學(xué)四則運(yùn)算。適用于成本計(jì)算、數(shù)值加總、百分比計(jì)算。輸入為計(jì)算表達(dá)式比如 100*0.250。, funccalculator ))3.3 第二步實(shí)現(xiàn)路由判斷路由是整個(gè)Agentic RAG里最體現(xiàn)“決策”的部分。我在這里用最簡(jiǎn)單的規(guī)則路由方便理解真實(shí)項(xiàng)目中你可以讓大模型來做路由判斷或者規(guī)則和大模型結(jié)合。路由的輸入是用戶問題輸出是應(yīng)該調(diào)用的工具名和參數(shù)class Router: def __init__(self, registry: ToolRegistry): self.registry registry def route(self, question: str) - Dict[str, Any]: 根據(jù)問題特征決定調(diào)用哪個(gè)工具。 # 規(guī)則一問題中包含數(shù)字運(yùn)算特征時(shí)優(yōu)先走計(jì)算工具。 if any(op in question for op in [成本, 計(jì)算, 多少, 百分比, 合計(jì)]) and any( ch.isdigit() for ch in question ): # 粗提取數(shù)字表達(dá)式生產(chǎn)環(huán)境建議用更穩(wěn)健的方式比如LLM解析。 import re nums re.findall(r\d(?:\.\d)?, question) expr *.join(nums) # 簡(jiǎn)化處理真實(shí)場(chǎng)景需要更合理提取 return {tool: calculator, args: {expression: expr}} # 規(guī)則二默認(rèn)走文檔檢索。 return {tool: vector_search, args: {query: question}}這段代碼的意圖非常明確先讓路由捕獲那些“明顯是數(shù)值計(jì)算”的問題其他問題一律交給文檔檢索。實(shí)際部署時(shí)你會(huì)遇到大量邊界情況比如“幫我算一下這兩種方案的總體成本差異”這句話里有“成本”也有數(shù)字但簡(jiǎn)單正則抽取會(huì)失敗。這種時(shí)候就需要升級(jí)路由的實(shí)現(xiàn)不再自己寫規(guī)則而是把工具列表交給大模型讓模型自己決定調(diào)用哪個(gè)工具、傳什么參數(shù)。我給出一版大模型路由的偽代碼不代表具體廠商接口核心思想是通用的class LLMRouter(Router): def route(self, question: str) - Dict[str, Any]: tools_schema self.registry.list_tools() prompt f 你是路由助手。只從以下工具中選擇一個(gè)最適合回答用戶問題的工具并輸出JSON格式 {{ tool: 工具名, arguments: {{ 對(duì)應(yīng)參數(shù)名: 參數(shù)值 }} }} 可用工具 {tools_schema} 用戶問題{question} 只輸出JSON不要多余解釋。 # 這里調(diào)用你的LLM接口解析返回的JSON。 # result your_llm_completion(prompt) # return json.loads(result) pass你可能會(huì)覺得把路由交給大模型難道不會(huì)比規(guī)則更慢嗎確實(shí)會(huì)更慢每次路由都是一次大模型調(diào)用會(huì)引入幾百毫秒延遲。所以我在生產(chǎn)里的做法是先用規(guī)則走快速路徑規(guī)則無法確定時(shí)再升級(jí)到大模型路由。這個(gè)“先便宜后貴”的策略在Agent系統(tǒng)里非常常用。3.4 第三步實(shí)現(xiàn)Agent主循環(huán)和結(jié)果裁決現(xiàn)在進(jìn)入核心中的核心Agent主循環(huán)。它做的事情是先執(zhí)行一次檢索然后讓Judge判斷結(jié)果夠不夠不夠就修改策略再查一輪直到滿足條件或達(dá)到最大輪數(shù)。class AgenticRAG: def __init__(self, router: Router, registry: ToolRegistry, max_rounds: int 3): self.router router self.registry registry self.max_rounds max_rounds self.memory [] # 記憶記錄歷史檢索內(nèi)容和判斷結(jié)果 def judge(self, question: str, retrieved_text: str) - bool: 判斷當(dāng)前檢索結(jié)果是否足夠回答問題。 這里用一個(gè)簡(jiǎn)化的規(guī)則實(shí)現(xiàn)命中關(guān)鍵主題詞就認(rèn)為信息完整。 if not retrieved_text or 未檢索到 in retrieved_text: return False # 簡(jiǎn)化校驗(yàn)如果問題中的關(guān)鍵詞出現(xiàn)在檢索結(jié)果里認(rèn)為方向正確。 # 生產(chǎn)環(huán)境更推薦用LLM來判斷或者用embedding相似度閾值。 key_terms [word for word in question.split( ) if len(word) 2] hit_count sum(1 for term in key_terms if term in retrieved_text) return hit_count 1 def run(self, question: str) - str: # 第1輪先按路由結(jié)果執(zhí)行一次。 for round_idx in range(1, self.max_rounds 1): route_result self.router.route(question) tool self.registry.get(route_result[tool]) retrieved tool.run(**route_result[args]) self.memory.append({round: round_idx, tool: route_result[tool], result: retrieved}) # 判斷結(jié)果是否足夠。 if self.judge(question, retrieved): # 把各輪檢索到的片段合并起來交給生成器。 final_context \n.join([item[result] for item in self.memory]) return self.generate(final_context, question) # 不足時(shí)改寫查詢?cè)~。示例里簡(jiǎn)單替換為“補(bǔ)充策略”。 # 生產(chǎn)環(huán)境這里會(huì)調(diào)用大模型做查詢改寫比如擴(kuò)展同義詞、拆分子問題。 question self.rewrite_query(question, round_idx) # 超過輪數(shù)則返回最后一次檢索的結(jié)果盡量兜底。 final_context \n.join([item[result] for item in self.memory]) return self.generate(final_context, question) def rewrite_query(self, question: str, round_idx: int) - str: 查詢改寫生產(chǎn)環(huán)境用大模型完成這里用規(guī)則做個(gè)示意。 # 簡(jiǎn)單策略補(bǔ)充“原因”“方案”等指示詞引導(dǎo)檢索變換角度。 if round_idx 1: return question 原因 return question 官方建議 def generate(self, context: str, question: str) - str: 根據(jù)合并后的上下文生成最終回答。 prompt f 你是知識(shí)庫(kù)問答助手。請(qǐng)基于以下檢索到的資料準(zhǔn)確回答用戶問題。 資料{context} 用戶問題{question} 如果資料不足以回答問題請(qǐng)明確說“當(dāng)前資料有限”不要編造。 # 這里對(duì)接真實(shí)的大模型生成接口。 # return your_llm_completion(prompt) return f[基于{len(self.memory)}輪檢索]\n{context}這段代碼雖然簡(jiǎn)化了很多東西但已經(jīng)把Agentic RAG的核心循環(huán)搭建出來了行動(dòng)-觀察-判斷-再行動(dòng)。第1輪檢索如果判為不足代碼會(huì)修改查詢?cè)~進(jìn)入第2輪第2輪的檢索結(jié)果會(huì)疊加到記憶里最終生成時(shí)把多輪結(jié)果合在一起。很多初次接觸Agentic RAG的人會(huì)覺得這不就是循環(huán)調(diào)用幾次檢索嗎是的形式上就是如此。但關(guān)鍵在于“判斷”這一步。第1輪結(jié)果是A你怎么知道A不夠第2輪改寫后的查詢?yōu)槭裁闯莻€(gè)方向改這些判斷和改寫邏輯的質(zhì)量決定了Agentic RAG是“瞎折騰幾輪”還是“越查越準(zhǔn)”。生產(chǎn)環(huán)境里判斷和改寫都應(yīng)該讓大模型來完成并且要給模型足夠好的提示詞。3.5 跑一個(gè)完整示例現(xiàn)在我們把所有組件串起來模擬一次運(yùn)行過程。router Router(registry) agent AgenticRAG(routerrouter, registryregistry, max_rounds3) question 部署失敗率升高是什么原因 answer agent.run(question) print(answer)執(zhí)行過程如下第1輪Router判斷問題里沒有數(shù)字運(yùn)算特征轉(zhuǎn)發(fā)給vector_search。檢索返回“部署失敗”相關(guān)文檔其中提到“鏡像倉(cāng)庫(kù)超時(shí)”。Judge判斷問題里的關(guān)鍵詞“部署失敗”命中了檢索結(jié)果認(rèn)為信息方向正確不再進(jìn)入第2輪。Generator匯總輸出結(jié)果。你可能覺得這個(gè)例子太順了沒有體現(xiàn)出多輪“決策”的價(jià)值。那就換一個(gè)更復(fù)雜的問題“對(duì)比一下擴(kuò)容方案和內(nèi)存優(yōu)化方案在成本上的差異”。這個(gè)問題同時(shí)涉及“擴(kuò)容方案”和“內(nèi)存優(yōu)化”兩個(gè)主題以及“成本差異”的數(shù)值對(duì)比。第1輪Router檢測(cè)到“成本”和數(shù)字但系統(tǒng)沒有真正的完整計(jì)算表達(dá)式先進(jìn)入vector_search。檢索只返回了擴(kuò)容相關(guān)片段沒覆蓋內(nèi)存優(yōu)化。Judge判斷關(guān)鍵詞“內(nèi)存優(yōu)化”沒有出現(xiàn)在結(jié)果里判定結(jié)果不足。第2輪改寫查詢?yōu)椤皟?nèi)存優(yōu)化 原因”再次檢索找到“內(nèi)存使用率超過閾值后觸發(fā)OOM進(jìn)程重啟導(dǎo)致部署失敗”等文檔。Memory里有了兩塊信息Generator綜合輸出對(duì)比。這就是“決策”的體現(xiàn)不是一次性搜完拉倒而是發(fā)現(xiàn)缺哪塊補(bǔ)哪塊。雖然示例里的Judge和Rewrite都很原始但擴(kuò)展成大模型版本之后效果會(huì)有質(zhì)的提升。4. 常見問題與排查技巧實(shí)錄代碼可以自己跑通但真要落地到生產(chǎn)坑還是不少的。我把自己實(shí)際踩過的問題整理一遍每一條都是真實(shí)的經(jīng)驗(yàn)不是文檔里能抄到的那種注意事項(xiàng)。4.1 路由判斷不準(zhǔn)工具選錯(cuò)導(dǎo)致結(jié)果全偏路由是Agentic RAG的第一道卡口選錯(cuò)工具的代價(jià)在后面幾輪都很難彌補(bǔ)。我遇到過一個(gè)典型場(chǎng)景用戶問“去年第四季度的營(yíng)收是多少”按規(guī)則路由它包含數(shù)字但沒包含“成本”“計(jì)算”這類詞于是走了文檔檢索??蓪?shí)際上用戶是想要一個(gè)精確數(shù)值最好走數(shù)據(jù)庫(kù)查詢工具直接從SQL里取數(shù)。這個(gè)問題的根源是規(guī)則路由無法窮盡所有表達(dá)。我建議的做法是路由層用大模型判斷為主規(guī)則只做省流量的快速路徑。同時(shí)把工具描述寫得更明確尤其要寫清楚“什么場(chǎng)景不要用這個(gè)工具”。比如calculator的描述可以改成“僅用于純數(shù)值運(yùn)算類問題比如根據(jù)給定數(shù)字求和、算百分比如果是查詢歷史財(cái)務(wù)數(shù)據(jù)請(qǐng)使用sql查詢工具”。大模型對(duì)“什么時(shí)候不該用”的理解比對(duì)“什么時(shí)候該用”更敏感這個(gè)技巧很有用。4.2 循環(huán)不終止Token消耗直接爆掉Agent循環(huán)設(shè)計(jì)不好最直接的問題就是每輪都調(diào)用大模型費(fèi)用翻倍式上升。我見過一個(gè)團(tuán)隊(duì)上了Agentic RAG之后單次的成本變成了原來的5倍因?yàn)橐粋€(gè)簡(jiǎn)單問題也要循環(huán)檢索3輪每輪還要做一次LLM判斷和一次查詢改寫再加上最終生成一次交互調(diào)用了7次大模型。控制成本有幾個(gè)抓手第一設(shè)置最大輪數(shù)max_rounds我建議默認(rèn)2輪就夠覆蓋大多數(shù)場(chǎng)景超過2輪的往往不是檢索能解決的第二記憶里去重同一輪檢索結(jié)果如果和之前的內(nèi)容高度重合直接判定為“本輪無效”避免把重復(fù)文本反復(fù)拼接進(jìn)Prompt第三Judge判斷用輕量模型比如只判斷“關(guān)鍵詞是否命中”或“embedding相似度是否超過閾值”不需要每次都把文本丟給大模型去理解。4.3 檢索結(jié)果互相沖突生成的回答前后搖擺多輪檢索帶來的新問題第1輪說“擴(kuò)容方案成本低”第2輪又說“內(nèi)存優(yōu)化更省錢”模型在生成時(shí)不知道怎么選最后生成了一段模棱兩可的話。這個(gè)問題在傳統(tǒng)RAG里很少見因?yàn)閭鹘y(tǒng)RAG只查一輪信息來源相對(duì)單一。Agentic RAG里解決沖突要分兩步。第一步在Judge階段就做質(zhì)檢如果兩輪檢索結(jié)果在關(guān)鍵結(jié)論上不一致再補(bǔ)一次驗(yàn)證檢索比如檢索“擴(kuò)容方案 vs 內(nèi)存優(yōu)化 對(duì)比”第二步在生成提示詞里明確要求模型“優(yōu)先采信更新時(shí)間較新、細(xì)節(jié)更具體的資料無法判斷時(shí)明確告知用戶信息存在分歧”。千萬不要讓模型強(qiáng)行整合矛盾信息否則就是生成幻覺的重災(zāi)區(qū)。4.4 Agentic RAG避坑清單最后整理一張實(shí)際項(xiàng)目里可以直接對(duì)照的清單幫助你快速排查問題。問題現(xiàn)象可能原因推薦解法路由頻繁選錯(cuò)工具工具描述不清晰缺少“不要用”說明重寫工具描述增加排除條件說明檢索結(jié)果永遠(yuǎn)“不足”循環(huán)空轉(zhuǎn)Judge判斷標(biāo)準(zhǔn)過嚴(yán)降低關(guān)鍵詞命中閾值或用大模型做語義判斷多輪檢索結(jié)果重復(fù)查詢改寫能力弱只換了表面詞改用大模型改寫要求拆解子問題并補(bǔ)充同義詞成本飆升每輪都調(diào)用大模型做判斷和改寫規(guī)則快速路徑兜底輕量模型做Judge最終回答張冠李戴上下文拼接順序混亂模型先看到舊信息按時(shí)間排序拼接并在Prompt中標(biāo)注檢索輪次局部問題越查越偏查詢改寫缺乏方向控制給改寫模型提供“已有信息摘要”讓它只補(bǔ)缺失部分這些坑不是理論推演都是我對(duì)著日志一遍一遍排查出來的。尤其最后一條“越查越偏”在純規(guī)則實(shí)現(xiàn)里非常常見第1輪查A沒查到第2輪把A改寫成A的近義詞還是沒查到第3輪可能已經(jīng)跑到完全不相干的主題上去了。解決思路是改寫時(shí)一定要參考已有檢索結(jié)果的缺失點(diǎn)而不是盲目擴(kuò)詞Referencing上一輪的缺失點(diǎn)這一點(diǎn)是Agentic RAG工程質(zhì)量的分水嶺。寫在最后的一點(diǎn)實(shí)戰(zhàn)體會(huì)Agentic RAG這個(gè)方向我自己的體會(huì)是它不像新技術(shù)那樣推翻舊知識(shí)反而把我以前積累的RAG經(jīng)驗(yàn)全部復(fù)用上了。向量庫(kù)沒換embedding模型沒換甚至Prompt風(fēng)格都基本沒變變的只是外面多套了一層“決策循環(huán)”。但這層循環(huán)帶來的體驗(yàn)提升非常明顯尤其是復(fù)雜問題場(chǎng)景下AI從“一本正經(jīng)地胡說”變成了“有邏輯地查證再回答”。如果你現(xiàn)在正在做RAG相關(guān)的項(xiàng)目我建議你先不要急著大改架構(gòu)。挑幾個(gè)當(dāng)前回答效果最差的問題類型用Agentic RAG的循環(huán)思路單獨(dú)跑一跑對(duì)比一下準(zhǔn)確率和成本。你會(huì)發(fā)現(xiàn)真正值得上Agent循環(huán)的問題沒那么多但一旦遇到復(fù)雜查詢它帶來的收益往往是顛覆性的。后續(xù)可以擴(kuò)展的玩法也很多把多Agent協(xié)作引進(jìn)來比如一個(gè)Agent負(fù)責(zé)查資料、一個(gè)Agent負(fù)責(zé)交叉驗(yàn)證、一個(gè)Agent負(fù)責(zé)對(duì)比分析又或者把外部API工具大量接入讓AI不僅能查文庫(kù)還能查庫(kù)存、查訂單、查監(jiān)控指標(biāo)。我一直覺得RAG從“資料庫(kù)”進(jìn)化到“決策者”只是第一步下一步是讓AI成為能自主完成跨系統(tǒng)信息整合的工作流執(zhí)行者那才是真正值錢的方向。