
1. 項目概述從“玩具”到“工具”的AI工程化之路最近和不少做AI應用的朋友聊天發現一個挺有意思的現象大家用大模型API搞個Demo做個聊天機器人或者文本總結工具速度都很快效果乍一看也挺唬人。但一旦想把東西拿給真實用戶用或者想把它嵌入到自己的核心業務流里問題就全冒出來了。比如你讓模型幫你分析一份最新的行業報告它可能給你編造幾個根本不存在的數據你想讓它根據公司內部知識庫回答客戶問題它要么說“我不知道”要么就開始一本正經地胡說八道。這感覺就像你買了一臺號稱“全能”的工程機械結果發現它既不會挖坑也不會吊裝離了說明書就寸步難行只能當個擺設。這背后的核心矛盾其實就是當前大模型能力與真實世界需求之間的“最后一公里”問題。大模型尤其是那些千億、萬億參數的基礎模型本質是一個基于海量互聯網文本訓練出來的“概率預測大師”。它擅長續寫、概括、翻譯這些模式相對固定的任務但對于需要精確、實時、私有化知識的場景就顯得力不從心了。它不知道你公司上周剛更新的產品定價表也讀不懂你數據庫里那些結構復雜的客戶記錄更無法主動去調用一個外部API來查詢今天的天氣或股票價格。于是兩個概念在AI工程化的實踐中被推到了前臺RAG檢索增強生成和智能體Agent。這倆不是什么遙不可及的學術概念而是我們這些一線開發者用來給大模型“打補丁”、“裝手腳”的實用工具箱。簡單來說RAG解決的是“知識”問題讓模型能說“正確”的話智能體解決的是“行動”問題讓模型能做“有用”的事。這篇文章我就結合自己趟過的坑和做過的項目來拆解一下為什么在今天的AI應用開發中你幾乎繞不開這兩項技術以及它們是如何協同工作把一個“聊天玩具”變成真正可用的“生產力工具”的。2. 核心困境拆解大模型的“無知”與“無能”在深入RAG和智能體之前我們必須先搞清楚我們到底在解決什么問題。把大模型直接當“萬能大腦”來用通常會撞上以下幾堵南墻。2.1 知識幻覺與事實性謬誤這是最頭疼的問題沒有之一。你問模型“我們公司旗艦產品Arix Pro的最大續航是多少” 盡管你的產品文檔里白紙黑字寫著“18小時”但模型可能會基于它在訓練數據里看到的其他筆記本信息自信地回答“根據公開信息大約是12小時”。這種現象被稱為“幻覺”Hallucination。模型并不是在“撒謊”它只是在基于概率生成最“流暢”和“合理”的文本而這個文本可能與事實相去甚遠。在金融、法律、醫療等對準確性要求極高的領域這種幻覺是致命的。我曾參與一個金融合規審核的項目初期直接調用模型分析交易條款它偶爾會“創造”出一些不存在的監管條例編號差點引發誤判。這讓我們意識到不能讓模型在“未知”或“私有”領域自由發揮。它需要一個可靠的知識來源作為依據。2.2 知識陳舊與缺乏實時性大模型的訓練數據是有截止日期的。比如GPT-4的知識截止到2023年4月。這意味著它不知道這之后發生的任何事件、發布的新產品、變更的法律法規。你無法用它來查詢今天的股價、分析剛出爐的財報、或者解答關于某個昨天才爆出新聞的科技事件。對于需要實時響應的應用如客服、新聞摘要、市場分析這是一個硬傷。模型需要一種機制能動態地獲取并理解最新的信息。2.3 無法訪問私有與結構化數據企業的核心價值往往沉淀在內部CRM里的客戶記錄、Confluence里的項目文檔、數據庫中的訂單信息、GitLab里的代碼庫。這些數據要么是私有的從未出現在公開互聯網上要么是高度結構化的如JSON、SQL表與模型訓練時見到的自然文本相差甚遠。模型對此一無所知就像一個被關在圖書館門外的人空有理解能力卻無書可讀。2.4 缺乏執行與交互能力模型本質上是一個“思考者”和“表達者”而不是一個“行動者”。它可以寫出一段完美的代碼但不能自己點擊“運行”按鈕它可以描述如何發送一封郵件但不能真的調用SMTP接口它可以分析是否需要查詢數據庫但不能自己建立連接并執行SQL。在很多自動化流程中我們需要AI不僅能“想”和“說”還要能“做”。這就需要為模型賦予調用工具Tools、執行任務Tasks的能力。注意很多人容易混淆“自動化腳本”和“智能體”。一個簡單的、按固定流程運行的腳本不是智能體。智能體的核心在于“決策”——它能夠根據模型的推理動態地決定下一步調用哪個工具、傳入什么參數、如何處理返回結果。這個決策環路是智能體價值的關鍵。3. RAG詳解為模型構建“外部記憶體”面對“無知”的困境最直觀的想法就是給模型“喂資料”。RAG就是一套系統化的“喂資料”方法論。它的核心思想不是去改變模型本身那需要昂貴的微調而是在模型之外構建一個動態的、可檢索的“知識庫”在生成答案前先把相關的參考資料找出來和問題一起交給模型。這樣模型就能基于你提供的“上下文”來生成答案大幅提高準確性和可控性。3.1 RAG的核心工作流程一個典型的RAG系統可以分為“索引”和“檢索-生成”兩個主要階段。階段一索引Indexing這個階段是離線的目的是把你的原始知識文檔、網頁、數據庫表等處理成便于快速檢索的格式。加載與切分首先你需要把各種格式的文檔PDF、Word、Markdown、HTML等加載進來并切割成大小合適的“塊”Chunks。這里有個關鍵技巧切分不是簡單按字數。好的切分要兼顧語義完整性。比如按段落切分比按固定500字切分更好對于Markdown可以按標題層級切分。使用像LangChain的RecursiveCharacterTextSplitter或Semantic Splitter能獲得更好的效果。向量化這是RAG的“魔法”步驟。使用一個嵌入模型Embedding Model將每一個文本塊轉換成一個高維度的向量比如1536維。這個向量就像是這段文本的“數學指紋”語義相近的文本其向量在空間中的距離也會很近。常用的嵌入模型有OpenAI的text-embedding-3系列、開源的BGE、Voyage等。存儲將這些向量以及對應的原始文本塊存儲到一個專門的“向量數據庫”中。常見的向量數據庫有Pinecone、Weaviate、Qdrant、Milvus以及PGVectorPostgreSQL的擴展。選擇哪個取決于你的規模、性能要求和運維能力。階段二檢索與生成Retrieval Generation這個階段是在線響應用戶查詢時發生的。問題向量化當用戶提出一個問題Query系統使用同樣的嵌入模型將這個問題也轉化為一個向量。相似性檢索在向量數據庫中尋找與“問題向量”最相似的若干個“文本塊向量”。這個過程通常使用余弦相似度或點積來計算。系統會返回相似度最高的前k個文本塊例如top-5。上下文構建與提示將這k個檢索到的文本塊作為“參考上下文”與用戶的原始問題一起構造成一個詳細的提示Prompt發送給大語言模型。一個經典的提示模板可能是“請基于以下上下文信息回答問題。如果上下文信息不足以回答問題請直接說‘根據提供的信息我無法回答這個問題’。上下文{檢索到的文本}。問題{用戶問題}”。生成最終答案大語言模型基于這個富含上下文的提示生成最終答案。由于答案的“素材”來源于你提供的可靠文檔其產生幻覺的概率大大降低。3.2 RAG實戰中的關鍵技巧與避坑指南搭建一個能跑的RAG原型很簡單但要讓它在生產環境穩定、準確、高效需要注意大量細節。技巧一分塊策略是成敗的第一步切忌無腦固定長度分塊512個字符的塊可能會把一個完整的概念攔腰截斷。對于技術文檔可以按章節/子章節切分對于對話記錄按對話輪次切分。重疊Overlap是必要的在切分時讓相鄰的文本塊有少量重疊比如100個字符。這能確保一些關鍵信息如出現在段落末尾的定義不會因為恰好被切在邊界而丟失提高了檢索的召回率。實操心得我通常會先用不同的分塊策略按段落、按標題、固定長度重疊對一小部分數據做索引然后用一組標準問題去測試檢索質量選擇效果最好的策略。沒有銀彈必須結合數據特性實驗。技巧二檢索不是“一錘子買賣”簡單相似性檢索的局限如果用戶問“蘋果公司最新財報怎么樣”而你的知識庫里只有一篇題為“Apple Q4 2023 Financial Results”的PDF由于“蘋果”和“Apple”的語義雖然一樣但向量表示可能因語言不同而有差異可能導致檢索失敗。引入查詢重寫與擴展在檢索前可以先讓大模型對原始查詢進行重寫或擴展。例如將“蘋果財報”重寫為“Apple financial report earnings Q4 2023”。這能顯著提升檢索的魯棒性。LangChain中的MultiQueryRetriever就是這個思路的自動化實現?;旌蠙z索Hybrid Search除了向量檢索還可以結合傳統的關鍵詞檢索如BM25。關鍵詞檢索在精確匹配術語、縮寫、產品型號等方面有優勢。將兩者的結果進行加權融合如 Reciprocal Rank Fusion能兼顧語義相似性和字面匹配度效果通常比單一方法好。技巧三提示工程是質量的“放大器”檢索到了對的文檔但如果提示沒寫好模型依然可能忽略上下文或生成糟糕的答案。明確指令在提示中強烈要求模型“必須且僅能”依據提供的上下文回答。可以設置“引用”格式要求模型在答案中標注出處來自哪個文檔塊這既方便用戶溯源也便于我們后期評估RAG鏈路的質量。處理“未找到”場景一定要在提示中告訴模型如果上下文不相關或不足應該如何回應。一個友好的“我暫時沒有找到相關信息您可以嘗試……”比一個胡編亂造的答案體驗好得多。上下文排序與過濾檢索到的top-k個塊其相關性可能差異很大。可以在送入最終提示前用一個更輕量的模型或規則對它們進行重排序或過濾只保留最相關的幾個避免無關信息干擾模型也節省了令牌Token消耗。注意RAG不是微調的替代品而是互補。對于需要模型深度理解特定領域術語、風格或推理模式的任務如生成特定格式的法律文書微調Fine-tuning可能更有效。RAG更擅長處理需要大量、動態、事實性知識支撐的問答場景。在實際項目中我經??吹健癛AG 輕量微調”的組合拳用RAG解決知識問題用微調讓模型更“懂行”。4. 智能體詳解為模型安裝“手腳”與“調度中心”解決了“知識”問題我們來看“行動”問題。智能體Agent的本質是賦予大語言模型使用工具Tools、進行規劃Planning、并持續執行Execution的能力。你可以把它想象成一個項目的“高級主管”它理解老板用戶的最終目標“做一份競品分析報告”知道自己手下可用工具有哪些人搜索工具、文檔分析工具、圖表生成工具然后自己制定步驟先搜索最新信息再總結各自優缺點最后生成報告草稿并一步步指揮協調直到任務完成。4.1 智能體的核心架構推理-行動循環智能體的工作遵循一個經典的“感知-思考-行動”循環在LLM語境下常被稱為ReActReasoning Acting框架。觀察智能體接收到用戶的目標或當前任務狀態。思考大語言模型作為“大腦”分析當前狀況。它需要決定任務完成了嗎如果沒完成下一步該做什么應該調用哪個工具調用時需要什么參數行動根據思考的結果智能體調用相應的工具如執行一段代碼、調用一個API、查詢數據庫并獲取工具執行的結果。再觀察將工具執行的結果成功或失敗以及返回的數據作為新的觀察反饋給“大腦”。循環模型基于新的觀察再次進行思考決定下一步行動如此循環直至任務完成或無法繼續。4.2 智能體中的關鍵角色工具Tools工具是智能體能力的延伸。一個工具本質上是一個函數它有明確的名稱、描述、參數格式。智能體通過提示詞學習這些工具的功能。常見的工具有網絡搜索如SerpAPI或DuckDuckGo Search讓智能體能獲取實時信息。代碼執行如Python REPL讓智能體能進行數學計算、數據處理。文件操作讀寫本地文件。API調用連接企業內部或第三方服務如發送郵件、查詢數據庫、操作CRM。專屬工具你為特定業務編寫的任何函數比如“查詢本月銷售數據”、“為客戶生成保單號”。定義工具的技巧名稱和描述要清晰模型的“思考”依賴于你對工具的描述。search_web就不如search_web_for_latest_news明確。描述要寫清楚“這個工具是干什么的”以及“在什么情況下使用它”例如“使用此工具在維基百科上搜索關于歷史人物或事件的摘要信息。”處理好復雜參數如果工具參數是一個復雜對象最好在描述中給出清晰的JSON結構示例幫助模型正確格式化請求。4.3 主流智能體框架與平臺選擇現在有很多優秀的框架和平臺能幫助我們構建智能體降低開發門檻。LangChain / LangGraph這是目前生態最豐富、最靈活的框架之一。LangChain提供了構建智能體所需的所有基礎組件工具、記憶、鏈。而LangGraph更進一步允許你以“圖”的形式可視化地定義智能體的工作流特別適合構建有復雜狀態轉移和分支邏輯的多步驟智能體。它給了開發者極大的控制權但學習曲線相對陡峭。AutoGen由微軟推出專注于多智能體協作。你可以創建多個角色化的智能體如程序員、測試員、產品經理讓它們通過對話來協作解決復雜任務。這在需要多角度評審或分工的場景下非常強大比如協同代碼開發、方案辯論等。Dify / Coze這類屬于低代碼/無代碼AI應用平臺。它們提供了可視化的界面讓你可以通過拖拽組件的方式快速組裝包含RAG、智能體、工作流在內的復雜應用。Dify的“工作流”畫布和Coze的“Bot”創建界面都非常直觀。它們的優勢在于極快的原型開發和部署速度特別適合產品經理、業務人員或需要快速驗證想法的開發者。但深度定制能力可能不如純代碼框架。如何選擇如果你是研究者或需要極度定制復雜邏輯的工程師LangGraph是你的不二之選。如果你想探索多智能體社會的交互與協作AutoGen提供了絕佳的試驗場。如果你的目標是快速將AI能力轉化為可用的業務應用且團隊中不一定有資深AI工程師Dify或Coze這類平臺能讓你在幾小時內就看到成果。我個人在為企業做內部效率工具PoC時經常先用Dify快速搭出可演示的版本驗證價值后再考慮用代碼重構。4.4 智能體開發中的常見陷阱與調試心得智能體開發聽起來很酷但調試起來可能讓人抓狂因為它涉及非確定性的LLM推理。陷阱一智能體陷入“死循環”或“無效行動”這是最常見的問題。智能體可能反復調用同一個工具而不推進或者在幾個無關工具間來回切換。對策設置明確的停止條件Stop Condition和最大迭代次數。在提示詞中清晰地告訴模型“如果你認為已經獲得了足夠的信息來回答問題或者連續三次嘗試都無法取得進展請直接輸出最終答案。” 在代碼層面務必設置循環上限比如10次防止無限循環消耗資源。調試技巧開啟智能體的詳細日志觀察它的“思考”過程??纯此诿恳徊降降资侨绾谓馕鲇^察、決定行動的。很多時候問題出在工具的描述不夠清晰或者上一步工具返回的結果格式讓模型產生了誤解。陷阱二工具調用參數錯誤模型可能會誤解你的要求給工具傳入錯誤類型或格式的參數。對策強化工具描述中的示例。對于復雜參數使用JSON Schema進行嚴格定義。一些高級框架支持“工具調用”Function Calling模式模型會輸出結構化的調用請求這比從自然語言文本中解析參數要可靠得多。此外可以在工具函數內部增加健壯的類型檢查和錯誤處理當參數錯誤時返回清晰的錯誤信息幫助模型進行修正。陷阱三處理開放域任務的不可控性你告訴智能體“幫我策劃一個周末旅行”這個目標非常開放。智能體可能會調用搜索工具查找“旅行”然后開始預訂機票和酒店如果它有這些工具權限這顯然存在風險和成本。對策為智能體設定明確的邊界和權限。在提示詞開頭就定義它的角色和限制例如“你是一個旅行信息咨詢助手只能提供信息查詢和方案建議不能執行任何實際的預訂、支付或更改數據的操作。” 同時在工具層面進行權限控制對于高風險工具如寫數據庫、發郵件需要額外的確認機制或根本不對智能體開放。實操心得開發智能體時我習慣采用“由簡入繁”的策略。先實現一個只有一個核心工具的簡單智能體確保它能穩定運行。然后逐步添加更多工具和更復雜的推理邏輯。每加一個新功能都用一組測試用例去驗證觀察智能體的決策是否符合預期。把智能體想象成一個需要“訓練”和“引導”的新員工清晰的指令提示詞和規范的流程工具設計至關重要。5. RAG與智能體的融合構建下一代AI應用單獨使用RAG或智能體已經能解決很多問題但當我們把兩者結合起來時就能構建出真正強大、自主的AI應用。我稱之為“有記憶、會思考、能行動”的智能系統。5.1 融合的典型模式模式一智能體驅動RAG在這種模式下智能體作為總控RAG作為它手下的一個“專家工具”。場景用戶問“對比一下我們產品A和競爭對手產品B在能耗方面的表現并給出建議?!绷鞒讨悄荏w“思考”要回答這個問題我需要兩份資料我們產品A的規格書以及競爭對手產品B的公開評測或官網數據。智能體“行動”調用“內部知識庫查詢工具”即RAG系統傳入查詢“產品A 規格書 能耗”。獲取結果。智能體“行動”調用“網絡搜索工具”傳入查詢“產品B 評測 能耗 2024”。獲取結果。智能體“思考”現在我有了兩份資料我需要分析對比并生成建議。智能體“行動”調用“文本分析與報告生成工具”可以是另一個LLM調用傳入兩份資料和對比指令生成最終答案。這里RAG負責解決獲取內部精確知識的任務而智能體負責規劃整個任務流并協調RAG和搜索引擎等多個工具。模式二RAG增強智能體在這種模式下智能體在每一步決策時都能獲得來自RAG系統的背景知識支持使其決策更精準。場景一個用于處理IT運維工單的智能體。流程用戶提交工單“服務器X的磁盤空間報警了?!敝悄荏w在決定采取什么行動前先通過RAG查詢“服務器X的歷史運維記錄”、“磁盤清理標準操作流程SOP”、“與該服務器相關的應用清單”。RAG返回相關文檔歷史記錄顯示該服務器上周剛清理過日志SOP指出先檢查/var/log目錄應用清單顯示上面跑著核心數據庫。智能體“思考”根據SOP我應該先建議檢查日志目錄。但歷史記錄顯示剛清理過可能問題復發??紤]到是核心數據庫服務器操作需謹慎。智能體“行動”生成回復“根據SOP建議先檢查/var/log目錄占用情況可使用du -sh /var/log/*命令。請注意該服務器上周已進行日志清理且運行核心數據庫建議在業務低峰期操作并先確認數據庫日志配置。是否需要我為您生成更詳細的檢查腳本”在這個例子里RAG在智能體決策的“思考”環節提供了關鍵的背景信息使得智能體的建議不再是泛泛而談而是具有針對性和歷史上下文。5.2 融合架構的設計考量當你決定融合兩者時架構設計需要仔細權衡。記憶管理智能體有自己的工作記憶Working Memory來存儲多輪對話和中間結果而RAG擁有長期的知識記憶。需要設計好兩者之間的信息交換通道。例如智能體可以將本輪對話的摘要或關鍵結論作為新的文檔存入RAG知識庫實現知識的積累。流程編排復雜的任務可能涉及多次RAG檢索和多個工具調用。使用像LangGraph這樣的框架可以清晰地編排這些步驟定義條件分支例如如果RAG檢索結果為空則轉向網絡搜索。評估與監控融合系統更復雜評估點也更多。需要監控RAG的檢索相關性、智能體的工具調用成功率、任務完成度、最終答案的用戶滿意度等。建立一套評估體系對迭代優化至關重要。6. 從概念到落地構建你的第一個AI智能助理理論說了這么多我們來看一個具體的、可落地的例子構建一個“技術文檔智能問答助理”。它既能回答關于你公司技術產品的具體問題用RAG又能根據問題幫你生成代碼片段或執行簡單的系統診斷命令用智能體。6.1 技術棧選擇與環境準備為了平衡靈活性和開發效率我們選擇以下技術棧后端框架LangChainLangGraph。它提供了我們所需的所有基礎模塊且LangGraph能很好地描述智能體的工作流。向量數據庫Chroma。它輕量、易用適合原型和中小規模項目無需單獨部署服務。嵌入模型OpenAI text-embedding-3-small。在效果和成本間取得良好平衡API調用方便。大語言模型OpenAI GPT-4o。作為智能體的“大腦”其推理和指令跟隨能力較強。前端簡單的Gradio界面用于快速演示。首先安裝必要的Python包pip install langchain langchain-openai langchain-chroma langgraph gradio6.2 核心模塊實現第一步構建RAG索引模塊我們創建一個類來處理文檔的加載、切分和索引。from langchain_community.document_loaders import DirectoryLoader, TextLoader from langchain_text_splitters import RecursiveCharacterTextSplitter from langchain_openai import OpenAIEmbeddings from langchain_chroma import Chroma import os class KnowledgeBaseIndexer: def __init__(self, persist_directory./chroma_db): self.embeddings OpenAIEmbeddings(modeltext-embedding-3-small) self.persist_dir persist_directory self.text_splitter RecursiveCharacterTextSplitter( chunk_size1000, chunk_overlap200, separators[\n\n, \n, 。, , , , , , ] ) def index_documents(self, docs_directory): 加載指定目錄下的所有文本文檔并創建索引 loader DirectoryLoader(docs_directory, glob**/*.txt, loader_clsTextLoader) documents loader.load() print(f已加載 {len(documents)} 個文檔) # 切分文檔 splits self.text_splitter.split_documents(documents) print(f切分為 {len(splits)} 個文本塊) # 創建向量存儲 vectordb Chroma.from_documents( documentssplits, embeddingself.embeddings, persist_directoryself.persist_dir ) vectordb.persist() print(f索引已創建并保存至 {self.persist_dir}) return vectordb def get_retriever(self, k4): 獲取檢索器 vectordb Chroma(persist_directoryself.persist_dir, embedding_functionself.embeddings) return vectordb.as_retriever(search_kwargs{k: k})第二步定義智能體可用的工具我們定義三個工具RAG檢索工具、代碼執行工具、系統命令工具需謹慎使用。from langchain.tools import tool from langchain.utilities import BashProcess import subprocess # 工具1: RAG檢索工具 tool def search_knowledge_base(query: str) - str: 當用戶詢問關于產品、API、配置等內部技術文檔問題時使用此工具從知識庫中查找相關信息。 # 這里需要接入第一步創建的檢索器 # 假設我們有一個全局的 retriever 對象 docs retriever.invoke(query) context \n\n.join([doc.page_content for doc in docs]) return f從知識庫中檢索到以下相關信息\n{context} if context else 知識庫中未找到相關信息。 # 工具2: 安全的代碼執行工具僅限Python tool def execute_python_code(code_snippet: str) - str: 當用戶請求生成或驗證Python代碼片段時使用此工具在安全沙箱中執行代碼并返回結果。輸入必須為純Python代碼字符串。 try: # 使用exec在局部作用域中執行避免污染全局環境 local_vars {} exec(code_snippet, {}, local_vars) # 嘗試獲取一個顯式的結果如果沒有則說明代碼可能已打印輸出 # 更健壯的做法是重定向stdout這里為簡化示例 output str(local_vars.get(result, 代碼執行完成無返回值請檢查是否有打印輸出。)) return f代碼執行成功。輸出{output} except Exception as e: return f代碼執行出錯{str(e)} # 工具3: 受限的系統命令工具示例生產環境需極度小心 bash BashProcess() tool def run_safe_system_command(command: str) - str: 當用戶需要檢查系統狀態如磁盤空間、進程且命令安全時使用此工具。僅允許執行白名單內的命令。 SAFE_COMMANDS [df -h, uptime, whoami, date] if command.strip() not in SAFE_COMMANDS: return 錯誤該命令不在允許的安全命令列表中。 try: result bash.run(command) return result except Exception as e: return f命令執行失敗{str(e)}第三步使用LangGraph構建智能體工作流我們設計一個簡單的圖智能體先嘗試用RAG回答問題如果RAG結果不理想或用戶要求執行操作則調用相應工具。from langgraph.graph import StateGraph, END from langchain_openai import ChatOpenAI from typing import TypedDict, Annotated import operator # 定義狀態結構 class AgentState(TypedDict): question: str rag_answer: str tool_calls: list final_answer: str # 初始化模型和工具 llm ChatOpenAI(modelgpt-4o, temperature0) tools [search_knowledge_base, execute_python_code, run_safe_system_command] llm_with_tools llm.bind_tools(tools) # 1. 路由節點決定使用RAG還是調用工具 def router(state: AgentState): question state[question] # 簡單的啟發式路由如果問題包含“如何實現”、“代碼”、“執行”、“命令”等傾向于調用工具 tool_keywords [代碼, 執行, 命令, 運行, 寫一個, 如何實現, debug] if any(kw in question for kw in tool_keywords): return call_tools else: return use_rag # 2. RAG處理節點 def rag_node(state: AgentState): docs retriever.invoke(state[question]) context \n\n.join([doc.page_content for doc in docs]) prompt f基于以下上下文信息回答問題。如果信息不足請如實告知。 上下文 {context} 問題 {state[question]} 答案 response llm.invoke(prompt) return {rag_answer: response.content} # 3. 工具調用節點 def tool_node(state: AgentState): messages [(user, state[question])] # 讓模型決定調用哪個工具 ai_msg llm_with_tools.invoke(messages) tool_calls ai_msg.tool_calls if not tool_calls: return {final_answer: 我無法處理這個請求因為它需要我執行不支持的特定操作。} results [] for tool_call in tool_calls: tool_name tool_call[name] tool_to_call next(t for t in tools if t.name tool_name) result tool_to_call.invoke(tool_call[args]) results.append(f{tool_name} 結果{result}) # 讓模型根據工具結果總結最終答案 summary_prompt f用戶問題{state[question]}\n工具執行結果{ .join(results)}\n請根據以上結果給出清晰完整的最終回答。 final_msg llm.invoke(summary_prompt) return {tool_calls: tool_calls, final_answer: final_msg.content} # 4. 構建圖 workflow StateGraph(AgentState) workflow.add_node(rag_node, rag_node) workflow.add_node(tool_node, tool_node) workflow.set_conditional_entry_point( router, { use_rag: rag_node, call_tools: tool_node, } ) workflow.add_edge(rag_node, END) workflow.add_edge(tool_node, END) agent workflow.compile()第四步集成與測試最后我們將索引器、智能體和一個簡單的Web界面集成起來。import gradio as gr # 初始化 indexer KnowledgeBaseIndexer() # 假設文檔已放在 ./docs 目錄下首次運行需要創建索引 # vectordb indexer.index_documents(./docs) retriever indexer.get_retriever(k4) def chat_with_agent(message, history): 處理用戶消息 result agent.invoke({question: message}) final_answer result.get(final_answer) or result.get(rag_answer) return final_answer # 創建Gradio界面 demo gr.ChatInterface( fnchat_with_agent, title技術文檔智能助理, description可以回答技術文檔問題也能執行簡單的代碼和系統命令安全限制內。 ) if __name__ __main__: demo.launch()6.3 部署與優化注意事項安全性是重中之重上述示例中的run_safe_system_command工具極度簡化絕對不要在生產環境中直接開放Bash命令執行。必須建立嚴格的命令白名單、參數校驗、權限控制和沙箱環境。RAG檢索質量定期評估和優化你的分塊策略、嵌入模型和檢索器??梢砸胫嘏判蚰P蛠硖嵘齮op-k結果的精度。智能體穩定性為智能體的推理循環設置超時和最大步數限制避免成本失控或死循環。記錄完整的交互日志便于分析和調試。成本控制RAG的嵌入調用和智能體的多次LLM調用都會產生費用??梢酝ㄟ^緩存常見的檢索結果、對智能體的思考步驟進行適當限制比如在簡單問題上不使用ReAct來優化成本。這個項目示例展示了如何將RAG作為知識來源將智能體作為決策和執行中樞構建出一個能理解私有文檔、又能進行簡單操作的實用AI助手。你可以在此基礎上擴展更多的工具如連接Jira、查詢數據庫、優化路由邏輯、并為其添加記憶功能使其能處理更復雜的多輪對話任務。