:從ReAct循環(huán)到工具調(diào)用與避坑指南)
看到這個標題我挺有感觸的。這幾年做技術分享見過太多人把 AI Agent 說成玄學好像不精通強化學習、不啃完幾篇頂會論文就玩不轉(zhuǎn)似的。但實際上對于已經(jīng)有編程基礎的程序員來說入門 AI Agent 的難度比想象中低得多它更考驗的是你對工程邊界的理解、對流程拆解的耐心而不是數(shù)學推導能力。這篇內(nèi)容不搞理論轟炸我會從一個小白程序員最容易上手、也最不容易跑偏的路徑切入先把 Agent 的神奇外衣剝掉看清它本質(zhì)上是什么再帶你從零跑通第一個能用的 Agent最后把我自己踩過的、以及帶人入門時最常見的一堆坑直接擺出來省得你再花一個月去趟。我盡量寫得直白點適合那種Python會寫點、HTTP知道是啥、但對 Prompt 和大模型只停留在用過ChatGPT的人。讀完你應該能建立一個立體的學習框架而不是收藏了一堆碎片。1. 先拆掉概念濾鏡AI Agent 到底是個什么東西很多教程開口就是Agent 是能感知環(huán)境、自主決策、執(zhí)行動作的智能體這句話一聽就頭大。咱換個說法Agent 就是一個懂得拆解任務、會調(diào)用工具、并且有短期記憶的代碼執(zhí)行流程。1.1 從聊天機器人到Agent的思維轉(zhuǎn)變你用過 ChatGPT 對吧你問一句它答一句這個過程本質(zhì)上是一個單輪或多輪的文本補全——它把我們的話拼接成上下文然后預測最合理的下一個字符。但當你讓它幫我查一下明天的天氣然后提醒我該穿什么衣服時它如果只是個聊天模型它只能給你一段編造的建議因為它不知道明天的天氣也沒有查的工具。Agent 的介入就是改變這個邏輯它先把你的目標存起來分解成查天氣和根據(jù)結(jié)果給穿衣建議兩個小任務。對大模型說你需要調(diào)用天氣查詢工具參數(shù)是地點和時間。大模型輸出一個結(jié)構(gòu)化的調(diào)用指令Agent 框架拿著這個指令去真的請求天氣 API拿到結(jié)果后再塞回給大模型最終由大模型輸出穿衣建議。你看Agent 不是一個全新的技術而是一種將大模型的推理能力和程序世界的確定性工具組合起來的工作流范式。1.2 你已經(jīng)在做的函數(shù)調(diào)用就是它的雛形如果你寫過代碼調(diào)過第三方 API其實你已經(jīng)具備理解 Agent 的基礎了。傳統(tǒng)程序里你寫if weather rain: print(帶傘)這是由你來決定邏輯分支。在 Agent 里這個邏輯分支變成了把當前狀態(tài)交給大模型讓大模型從你預設的工具列表里選一個function來調(diào)用。所以我給初學者的第一個建議是千萬別把 Agent 當人工智能來學把它當一個由大模型驅(qū)動路由的程序來學。這個視角一旦建立你的第一反應就不是恐懼未知而是去思考我的程序該提供哪些工具給模型選每個工具暴露什么參數(shù)模型什么時候該選工具什么時候該直接回答把握住這一點后面所有的技術選型、Prompt 設計、流程編排全都是在圍繞這個核心做優(yōu)化。2. 學習路線規(guī)劃小白入門的三個階段和兩件必做的功課網(wǎng)上那種 40 集視頻課我勸你別收藏了大概率吃灰。我給小白朋友推薦一條相對高性價比的路徑分三個階段每個階段都有明確的產(chǎn)出物。2.1 階段一會調(diào)模型讀懂結(jié)構(gòu)化輸出第一步不是學什么 LangChain而是先用 SDK 把大模型的接口調(diào)用跑通。比如你寫一個 Python 腳本調(diào)用任意一家大模型廠商的對話接口傳一個簡單的 Prompt拿到返回結(jié)果。這一步的關鍵是搞懂三件事system、user、assistant三種角色的消息如何配合temperature、top_p這些采樣參數(shù)對輸出風格的影響先無腦記住想穩(wěn)定就調(diào)低溫度想發(fā)散就調(diào)高如何請求模型輸出JSON 格式的結(jié)果比如讓它輸出{action: query_weather, args: {city: 北京}}。為什么強調(diào)結(jié)構(gòu)化輸出因為 Agent 和普通聊天的最大區(qū)別就是要讓代碼能解析模型的決策。如果模型的輸出是一段自由文本你的程序很難穩(wěn)定地根據(jù)它執(zhí)行動作。2.2 階段二實現(xiàn)一個 ReAct 循環(huán)理解思考-行動-觀察第二階段的里程碑是手動實現(xiàn)一個極簡的 ReAct 循環(huán)。ReAct 的全稱是 Reason and Act是目前絕大多數(shù) Agent 框架的工作內(nèi)核。這個循環(huán)只有四步思考Thought模型根據(jù)當前目標和已有信息決定下一步該干什么。行動Action模型輸出一個工具調(diào)用指令比如search_web(中國人口數(shù)量)。觀察Observation程序真的去執(zhí)行這個搜索把返回的頁面摘要作為文本返回給模型。循環(huán)模型看到觀察結(jié)果再次推理判斷是否已經(jīng)能回答用戶如果不能就繼續(xù)發(fā)起下一個行動。如果你能不用任何框架用純 Python 的while循環(huán) 大模型接口把上面這個流程寫出來你對 Agent 的理解就已經(jīng)超過 60% 的所謂傳道者了。我在帶人入門的時候強烈禁止他們一上來就碰現(xiàn)成的編排框架原因就在這框架封裝了太多細節(jié)會讓新手誤以為調(diào)個 Agent 類就能解決一切問題。2.3 階段三用框架做復雜任務管理有了徒手實現(xiàn)的基礎再上框架比如 LangChain、LlamaIndex 或者 Semantic Kernel 等你就完全看得懂它的文檔在講什么了??蚣軒湍憬鉀Q的是重復勞動維護對話記憶、管理多工具調(diào)用的路由、解析模型輸出、處理重試和降級等等。在框架階段建議練習用列表和表格記錄每類組件的用途比如組件類型核心用途小白必學Model I/O管理大模型調(diào)用和 Prompt 模板必須Memory短期和長期記憶存儲必須Tools封裝外部能力給模型調(diào)用必須Chains串聯(lián)多步驟流程進階Agents / Executor決策路由與動作執(zhí)行核心這個階段要戒掉代碼生成器思維——框架不是讓你復制粘貼跑通就完事你要能說清楚每個節(jié)點上的數(shù)據(jù)流動用戶輸入進來是一段字符串經(jīng)過格式化變成消息數(shù)組模型返回一個結(jié)構(gòu)體路由判斷是退出還是繼續(xù)。2.4 一個重要提醒英語比技術本身更卡你的瓶頸有一說一現(xiàn)在做大模型開發(fā)第一手信息基本都在英文世界??蚣艿奈臋n、官方示例、Changelog、Github 的 Issue 討論全是英文。哪怕你四級沒過也請養(yǎng)成先用英文關鍵詞搜、看不懂再用翻譯工具的習慣。這能讓你避開大量的用二手信息炒冷飯的中文博客效率至少翻倍。3. 動手實操從零實現(xiàn)一個帶工具的迷你 Agent理論說得再多不如親手寫一次。下面我分享的這套代碼幾乎是我?guī)巳腴T時的標準作業(yè)。它不依賴任何 Agent 框架只依賴一個大模型的 SDK完整展示了 ReAct 循環(huán)的核心邏輯。3.1 準備工作與工具選型你需要準備Python 3.9 以上的環(huán)境一個能調(diào)用的大模型 API 密鑰以常見的 OpenAI 兼容接口為例requests庫。我個人建議在小白階段模型就選市面上主流旗艦模型的最便宜版本比如gpt-4o-mini或者國產(chǎn)各家的大模型 lite 版。原因很簡單入門階段最重要的是把流程跑通而不是追求推理天花板。大參數(shù)模型雖然聰明但成本和響應速度在 Debug 階段會讓你很崩潰。3.2 定義工具與構(gòu)造提示詞假設我們做一個查詢?nèi)掌趯墓?jié)假日及適宜活動的 Agent。它有兩個工具get_holiday(date_str)返回當天是什么節(jié)日不內(nèi)置真實數(shù)據(jù)模擬查詢過程。get_movie_recommendation(holiday_name)根據(jù)節(jié)日返回一個電影推薦。關鍵的 Prompt 結(jié)構(gòu)是一個函數(shù)清單的描述用 JSON 模式告訴模型當前有哪些工具可用。我的模板通常長這樣你是一個智能助手。你可以根據(jù)用戶的目標選擇調(diào)用下面的工具來獲取信息并給出回答。 可用工具列表 { tools: [ { name: get_holiday, description: 查詢指定日期對應的節(jié)日名稱參數(shù)為日期格式Y(jié)YYY-MM-DD, parameters: {type: object, properties: {date_str: {type: string}}} }, { name: get_movie_recommendation, description: 根據(jù)節(jié)日名稱獲取適合觀看的電影推薦, parameters: {type: object, properties: {holiday_name: {type: string}}} } ] } 你的回答必須遵循以下格式 如果下一步需要調(diào)用工具請嚴格輸出JSON {action: get_holiday, action_input: 2027-08-15} 如果已經(jīng)獲取足夠信息并可以回答用戶請輸出 {action: final, action_input: 你的最終回答}這個 Prompt 一點都不高級但非常實用。它的核心是把路由決策收斂到一個嚴格的二選一要么繼續(xù)調(diào)用工具要么結(jié)束并回答。3.3 主流程代碼下面用一個 Python 文件展示核心調(diào)用邏輯。為了閱讀清晰我省略了 API 請求封裝細節(jié)只留 Agent 循環(huán)。要注意這只是一個教學演示版本生產(chǎn)環(huán)境需要加超時、重試和記憶截斷。import json import re import requests # 模擬工具函數(shù) def get_holiday(date_str): # 真實場景這里會查詢接口這里 mock 返回 if 05-01 in date_str: return 勞動節(jié) elif 10-01 in date_str: return 國慶節(jié) elif 08-15 in date_str: return 端午節(jié) return 普通工作日 def get_movie_recommendation(holiday_name): movie_map { 勞動節(jié): 《工薪階層的奮斗》, 國慶節(jié): 《家國萬里》, 端午節(jié): 《龍舟少年》, 普通工作日: 《下班后的寧靜》 } return movie_map.get(holiday_name, 《隨機彩蛋》) def call_llm(messages): # 偽代碼替換為實際的大模型調(diào)用 SDK 或 HTTP 請求 response requests.post( https://your-llm-api.example.com/chat/completions, headers{Authorization: Bearer YOUR_KEY}, json{model: your-model, messages: messages, temperature: 0.2} ) return response.json()[choices][0][message][content] def extract_json(text): # 容錯從模型輸出中提取第一個 JSON 對象 match re.search(r\{.*\}, text, re.S) if match: return json.loads(match.group()) raise ValueError(f無法解析模型輸出: {text}) def run_agent(user_goal): messages [ {role: system, content: SYSTEM_PROMPT}, {role: user, content: user_goal} ] # 最大迭代輪次避免死循環(huán) max_steps 5 for step in range(max_steps): # 1. 思考與決策 raw_output call_llm(messages) print(f\n[Step {step1}] 模型原始輸出: {raw_output}) # 2. 解析動作 action extract_json(raw_output) action_name action.get(action) action_input action.get(action_input) # 3. 執(zhí)行動作或退出 if action_name final: print(f最終回答: {action_input}) return action_input elif action_name get_holiday: observation get_holiday(action_input) elif action_name get_movie_recommendation: observation get_movie_recommendation(action_input) else: observation 未知工具請重試 # 4. 將觀察結(jié)果回傳給模型 messages.append({role: assistant, content: raw_output}) messages.append({role: user, content: f工具返回的觀察結(jié)果: {observation}}) # 超步數(shù)保護 fallback 循環(huán)次數(shù)超限停止繼續(xù)執(zhí)行。 print(fallback) return fallback if __name__ __main__: run_agent(今天是2027年8月15日有什么適合看的電影嗎)3.4 運行邏輯分步解讀讓我們手推一下當用戶輸入今天是 2027 年 8 月 15 日有什么適合看的電影嗎時程序會發(fā)生什么Step 1模型拿到用戶問題判斷自己不知道節(jié)日數(shù)據(jù)于是輸出{action: get_holiday, action_input: 2027-08-15}。你的程序解析 JSON調(diào)用get_holiday拿到端午節(jié)。Step 2程序把端午節(jié)作為觀察結(jié)果塞回給模型。模型發(fā)現(xiàn)還需要知道端午節(jié)看什么電影于是再次調(diào)用工具get_movie_recommendation(端午節(jié))拿到《龍舟少年》。Step 3此時模型有了全部信息輸出{action: final, action_input: 8月15日是端午節(jié)推薦你看《龍舟少年》}。循環(huán)結(jié)束。這個過程中每一步的打印輸出我建議你在本地跑的時候都留好它就是調(diào)試 Agent 時你唯一的眼睛。3.5 為什么這么設計從癱瘓到指揮的要點這個迷你 Demo 有三個設計要點值得你在后續(xù)學習里反復品味第一將工具描述嵌入系統(tǒng) Prompt讓模型知道自己擁有什么能力。模型的工具使用能力很大程度取決于你描述得清不清楚參數(shù)名必須和你代碼里的字段嚴格對應否則模型會臆造參數(shù)。第二把模型輸出當成數(shù)據(jù)而非代碼來執(zhí)行。我們只是解析 JSON 然后查表調(diào)用預設函數(shù)絕不可以用eval直接執(zhí)行模型返回的內(nèi)容。這一點是安全紅線很多新手一上來圖省事就直接 eval一旦模型輸出被惡意注入整個系統(tǒng)就淪陷了。第三最大步數(shù)保護是必需品。模型在復雜任務里非常容易出現(xiàn)思考中的循環(huán)——比如反復調(diào)用查詢工具卻不結(jié)束。沒有max_steps的兜底你的 Token 賬單會迅速爆炸。4. 小白避坑指南我見過的那些看起來正常但就是不對的報錯說實話我見過太多人在入門環(huán)節(jié)被坑到懷疑人生。技術本身不難難的是排錯經(jīng)驗不夠。我把最常見的四類坑直接列出來你對照著檢查能省很多事。4.1 結(jié)構(gòu)化輸出的震動模式模型偶爾不按 JSON 輸出怎么辦癥狀模型大多數(shù)時候輸出正常 JSON偶爾在復雜指令下輸出好的我將調(diào)用工具get_holiday(2027-08-15)這種帶解釋的文本你的json.loads直接崩潰。解決思路不要指望模型 100% 遵守 JSON 格式。建議在解析層加一層容錯用正則提取第一個花括號到最后一個花括號之間的內(nèi)容再做解析。如果解析失敗就把錯誤信息返回給模型讓它自我糾正重新輸出一次。這是 Agent 開發(fā)里最常見的兜底手段之一非常有效。底層邏輯讓錯誤成為對話上下文的一部分模型會根據(jù)新信息修正上一次的錯誤輸出比單純重試的成功率高得多。4.2 Prompt 里的參數(shù)雙寫工具描述和實際執(zhí)行不一致癥狀你寫工具函數(shù)時定義的參數(shù)是city但描述里為了讓人讀起來通順寫了查詢某個城市的天氣參數(shù)為城市名模型就會輸出{action: get_weather, action_input: 北京}而你代碼里找的是city字段結(jié)果取不到任何值。解決思路工具描述里的parameters里的屬性名必須和代碼參數(shù)名一字不差。而且建議在描述里附帶參數(shù)格式示例比如{date_str: 2027-08-15}模型會模仿示例來輸出穩(wěn)定性大幅提升。實操心得我后來養(yǎng)成的習慣是給模型看一個完整動作示例效果比單純描述好十倍。4.3 上下文爆炸對話輪次稍長請求體變得巨大隨著工具觀察結(jié)果的累積消息列表會越來越長。比如你把一篇 5000 字的搜索結(jié)果整個塞回上下文下一輪請求攜帶的歷史消息已經(jīng)消耗掉大量 Token 配額甚至超出模型的最大上下文窗口。解決方案對話記憶需要管理不能無腦追加。常見做法是只保留最近的 N 輪消息舊的系統(tǒng)級摘要替代工具返回的觀察結(jié)果進行文本截斷比如只取前 1000 個字符設置總的 Token 預算比如 8000 Token超過后自動裁剪最早的非關鍵消息。4.4 功能可以跑但效果爛工具路由失靈有一種很氣人的情況Agent 明明接入了好幾個搜索工具但模型就是死認某一個哪怕這個工具經(jīng)常返回空結(jié)果。這種情況的核心原因通常是工具 description 寫得不夠有區(qū)分度。你想想模型只能憑文字描述來決定該用哪個工具如果兩個工具的描述都寫著搜索信息它大概率無從選擇。加上一句優(yōu)先使用適合實時性要求高的查詢這類限定路由準確率會有一個明顯躍升。4.5 關于成本控制的一個忠告小白調(diào)試 Agent 時最肉疼的是花錢如流水。我建議你養(yǎng)成一個習慣先在代碼里打印完整的 message 列表再測試小規(guī)模交互。這樣可以清楚地看到每次請求攜帶了多少內(nèi)容有沒有做無用功。另外一個技巧是把temperature調(diào)低到 0不是為了質(zhì)量而是為了減少試錯時的隨機性——排查 BUG 時你希望模型輸出是可復現(xiàn)的而不是換個詞就換條路。5. 進階之路從玩具到可靠工具的四個關鍵轉(zhuǎn)身到這里你已經(jīng)能親手跑通一個最小閉環(huán)了接下來怎么繼續(xù)往前走我建議按照下面四個方向選一個深入鉆研。5.1 從單輪工具調(diào)用到多步驟任務編排迷你 Demo 里只有一兩步調(diào)用但真實需求往往需要更多步驟。比如幫我規(guī)劃一次三天旅行Agent 得先查機票、查酒店、查天氣、查景點再綜合這些信息做計劃。這個階段你需要學習任務分解和成果合并可以嘗試把一個大目標拆成多個子任務每個子任務的輸出作為下一個子任務的輸入。建議練習實現(xiàn)一個信息收集匯總 Agent讓它在多個工具間依次查詢最終生成一份結(jié)構(gòu)化根文。5.2 從短期記憶到持久記憶目前的 Agent 每次對話結(jié)束就忘了你是誰這在真實場景遠遠不夠。你可以嘗試引入一個向量數(shù)據(jù)庫把每次對話的核心結(jié)論或用戶偏好存儲起來下次對話時先做相似度檢索把歷史記憶作為上下文注入。這一步可以順帶把 RAG檢索增強生成入門了。5.3 從代碼觸發(fā)到事件驅(qū)動真實世界的系統(tǒng)不會等你手動調(diào)用它們可能通過 Webhook、消息隊列、定時任務來觸發(fā)。進階的方向是學會把 Agent 封裝成異步服務讓它能接收外部事件并自動決策。這需要你補一點消息隊列和任務調(diào)度的基本功但對工程能力的鍛煉價值很大。5.4 從我用框架到我懂框架用到一定程度你會發(fā)現(xiàn)框架為了通用性犧牲了靈活性。很多團隊最后都會基于自己的業(yè)務場景寫一套輕量的內(nèi)部 Agent 框架。如果你能理解現(xiàn)有框架的設計模式比如如何抽象工具、如何管理狀態(tài)、如何處理錯誤重試你就能自己動手裁剪一個適合自己業(yè)務場景的迷你框架到時候你在組里的話語權會完全不同。6. 學習資源與工具箱推薦不恰飯純自用最后分享一份我平時帶人用的資源清單沒有商業(yè)推廣都是實測過好用的。6.1 官方文檔是爸爸社區(qū)博客是參考不管用什么框架首選的永遠是該框架的官方文檔和官方 API 參考。社區(qū)博客的作用是幫你建立直覺但極容易過期——大模型領域基本每個月都在變一篇三個月前的教程可能已經(jīng)完全不適用了。官方教程永遠是最新且準確的一手信息看博客時學會看寫作日期太老的直接跳過盡量用英文原版翻譯會導致專有名詞混亂。6.2 必做練習場景清單我建議你在學習過程中至少親手完成下面三個練習每一個都能覆蓋大量核心概念難度場景考察點入門做一個果蔬識別推薦 Agent工具定義、結(jié)構(gòu)化輸出、基于觀察結(jié)果二次推理中等做一個文章撰寫流水線先搜索資料再生成大綱最后寫正文多步編排、上下文傳遞、長文本截斷進階做一個文件操作助手讓模型調(diào)用本地函數(shù)創(chuàng)建、重命名、整理文件安全性設計、路徑校驗、Action 白名單這三個項目做完你對 Agent 的工程應用基本就有底氣了。我個人的經(jīng)驗是學 Agent 不怕笨怕的是不求甚解地跑通別人寫好的腳本。你跟著跑通了十個 Demo不如親手從一行print開始搭出一個哪怕很蠢的 Agent。前者給你短暫的成就感后者給你排查任何問題時的冷靜感。最后一次建議把這篇文章收藏起來但別只是收藏。今天就用那節(jié) 3.3 的代碼哪怕是照抄一遍也比看十篇教程管用。等你跑通第一版再回頭來看前面那些概念你會發(fā)現(xiàn)自己已經(jīng)在山腰上了后面的風景遠比你想象的遼闊。