調用:Agent從“會聊天”到“能辦事”的核心實戰(zhàn))
1. 函數(shù)調用Agent從“會聊天”到“能辦事”的關鍵一躍做Agent開發(fā)這段時間我最大的一個感受是很多人把Agent等同于“一個接了大模型API的聊天機器人”但真正讓Agent變得有用的恰恰是函數(shù)調用Function Calling這一環(huán)。你可以讓模型寫詩、寫代碼、做翻譯但只要它不能去查天氣、查數(shù)據庫、發(fā)消息、操作文件它就始終是個“紙上談兵”的顧問而不是一個能幫用戶把事辦成的助手。函數(shù)調用的本質是讓大模型在生成文本的基礎上額外輸出一個結構化的“調用意圖”——包括要調用哪個函數(shù)、傳入什么參數(shù)——然后由我們的代碼去真正執(zhí)行這個函數(shù)再把執(zhí)行結果返回給模型模型基于結果繼續(xù)回答或進行下一步操作。這個機制解決的核心問題就是大模型本身不具備訪問外部世界的能力但通過函數(shù)調用我們可以把外部能力“接入”模型的推理循環(huán)讓模型成為指揮中樞而不是執(zhí)行終端。這篇文章適合誰看如果你正在做Agent開發(fā)或者剛接觸AI Agent、打算在自己的項目里給模型加上工具能力那么這份總結值得你完整讀一遍。我會從函數(shù)調用的設計思路、核心實現(xiàn)細節(jié)、完整的實操代碼到我在實際項目中踩過的坑和排查經驗一次性講清楚。文章里的代碼和方案都是我在真實項目里驗證過的你幾乎可以照著抄。2. 三種主流函數(shù)調用方式對比別一上來就選錯路子在開始寫代碼之前你需要先搞清楚一個事情函數(shù)調用并不是只有一種實現(xiàn)方式。市面上常見的方案有三大類每一類的適用場景和坑都不一樣。2.1 API原生函數(shù)調用最省心但綁定平臺第一種是模型服務商在API層面直接支持的原生函數(shù)調用。典型代表就是OpenAI的functions/tools參數(shù)、Anthropic的tool_use以及國內許多廠商現(xiàn)在跟進支持的類似接口。你只需要在請求里把函數(shù)的定義包括函數(shù)名、參數(shù)描述、類型以JSON Schema的形式傳進去模型在需要調用時就會在返回內容里多出一個tool_calls字段里面是結構化的調用請求。這個方案最大的優(yōu)點是不需要你費勁去“教”模型怎么輸出JSON——模型本身已經針對這個能力做過專門訓練輸出格式穩(wěn)定解析也容易。缺點也很明顯你被綁定在某一家廠商的接口風格上。換一個模型廠商或者用一些自部署的開源模型這個能力可能就不存在或者格式不兼容。不過如果你用的就是主流閉源模型API這依然是最值得推薦的第一選擇。2.2 提示詞式調用兼容性強但需要“調教”第二種方式是純提示詞方案。你不依賴任何API的原生能力而是在系統(tǒng)提示詞里跟模型約定“當需要查詢天氣時請輸出一個JSON格式為{tool: get_weather, params: {...}}”。然后你的代碼去解析模型輸出的文本匹配到對應的工具并執(zhí)行。這個方案的核心優(yōu)勢是兼容性極強——任何能聊天的模型都能用包括那些沒開放函數(shù)調用能力的開源模型。代價則是它的穩(wěn)定性完全依賴提示詞寫得夠不夠清楚以及模型本身的理解能力。模型可能今天老老實實輸出JSON明天換個措辭就加了點解釋性文字你的解析器就得跟著修。所以用這個方案可以再配合下一條要說的“結構化輸出”。2.3 結構化輸出與手動解析夾縫中的折中方案第三種方案介于前兩者之間利用模型API提供的JSON Mode或結構化輸出能力強制模型返回合法JSON但JSON的字段含義由我們自己定義再由我們手動解析并分發(fā)到對應的函數(shù)。這個方案的優(yōu)點在于既獲得了相對穩(wěn)定的輸出格式又保留了對“調用協(xié)議”的完全控制權——比如你可以在JSON里加入業(yè)務流水號、加入多個工具的同時調用請求這些在原生功能里往往受限。缺點則是你需要自己寫更多的解析和校驗代碼而且JSON Mode只能保證格式正確不能保證字段內容一定合理。為了讓你更直觀地做選擇我把三種方式的比較整理成一個表格對比維度API原生函數(shù)調用提示詞式調用結構化輸出手動解析輸出穩(wěn)定性高低依賴模型能力中高實現(xiàn)復雜度低低但調提示詞耗時中跨模型遷移性差好中多工具并行調用多數(shù)已支持看約定格式完全可控建議適用場景生產環(huán)境、主流API快速原型、開源模型需要高度自定義協(xié)議的場景我個人在生產環(huán)境里的經驗是能用API原生函數(shù)調用就用原生的這是性價比最高的路徑只有當模型沒有原生支持、或者你需要一個跨廠商的統(tǒng)一Agent底層時才去走后兩條路。3. 核心細節(jié)拆解一個高質量函數(shù)調用方案需要摳哪些細節(jié)很多人寫函數(shù)調用就直接把函數(shù)定義往參數(shù)里一塞跑通了就算完事。但真正到了Agent項目里調用成功率、參數(shù)準確性、異?;謴湍芰@些細節(jié)才是決定一個Agent“好用”還是“雞肋”的分水嶺。這部分我拆成三個小節(jié)逐一講透。3.1 工具定義你的函數(shù)簽名寫得越清楚模型就越不容易犯錯函數(shù)調用的第一步是把你代碼里的函數(shù)“翻譯”成模型能理解的語言。以OpenAI的tools參數(shù)為例一個函數(shù)定義長這樣tools [ { type: function, function: { name: get_weather, description: 查詢指定城市的實時天氣情況包括溫度、天氣狀況和風力。當用戶詢問天氣、氣溫、是否會下雨時使用。, parameters: { type: object, properties: { city: { type: string, description: 城市名稱例如北京、上海、廣州。必須是中文城市名。 }, unit: { type: string, enum: [celsius, fahrenheit], description: 溫度單位默認使用攝氏度。, default: celsius } }, required: [city] } } } ]這里有幾個細節(jié)是很多教程不會強調的。第一個是描述要“精確到使用時機”。description不只是給模型介紹這個函數(shù)是干嘛的更重要的是告訴模型“什么時候該用它”。我見過很多人寫“查詢天氣”四個字就完了結果模型在用戶說“今天適合穿什么”的時候完全沒想過可以調天氣接口。加上“當用戶詢問天氣、氣溫、是否會下雨時使用”這種觸發(fā)條件描述后調用率立刻上了一個臺階。第二個細節(jié)是參數(shù)描述里要寫取值范圍、格式約定、甚至默認行為。比如city參數(shù)如果你不寫明“必須是中文城市名”模型有可能會給你輸出“Beijing”而不是“北京”你的查詢接口很可能因此報錯。越細的約束越能減少下游解析的麻煩。第三個細節(jié)是required字段不能偷懶。必填的參數(shù)必須列出來否則模型偶爾會漏掉你其實必須要的參數(shù)。我在一個項目里就是因為沒把user_id設為必填導致后臺一連串的鑒權錯誤。3.2 參數(shù)解析與校驗別信模型輸出的每個字節(jié)都是金子模型不是數(shù)據庫它的輸出有一定概率出錯。函數(shù)調用返回的JSON里參數(shù)類型不對、字段缺失、甚至整個JSON無法解析都是常見情況。因此在真正執(zhí)行函數(shù)之前你要有一道校驗關卡。我的做法是寫一個通用的校驗器在分發(fā)之前做三層校驗第一層是JSON格式校驗判斷tool_calls里的arguments字符串能不能正常解析成字典。第二層是Schema校驗把解析出來的參數(shù)再用定義時的那套JSON Schema格式跑一遍確認類型合法、必填項都在。第三層是業(yè)務校驗這一步是校驗一些Schema管不了的東西比如日期格式是不是合理的、金額是不是大于0、用戶ID是不是存在。前兩層可以用現(xiàn)成的庫比如JSON Schema的Python實現(xiàn)jsonschema來做第三層就得自己在每個函數(shù)里寫。很多人覺得這太繁瑣但我實測下來加一道校驗至少能攔截掉約5%~10%的模型錯誤輸出在長期運行的Agent服務里這個比例足以避免大量線上事故。注意參數(shù)校驗失敗的時候不要直接拋異常終止整個對話循環(huán)。正確的做法是構造一條“工具執(zhí)行錯誤”的消息返回給模型告訴它“參數(shù)不合法請修改后重試”。模型通常會自動修正Agent的容錯能力就是這么一點點建立起來的。3.3 執(zhí)行分發(fā)與結果回流把函數(shù)返回變成模型能消化的“事實”校驗通過之后就進入執(zhí)行分發(fā)環(huán)節(jié)。我推薦用注冊表模式來管理函數(shù)與執(zhí)行器的映射。注意這里有一個新手很容易踩的坑**模型傳遞過來的只是函數(shù)名和參數(shù)字典而不是真正的Python函數(shù)對象。**你不能直接拿字符串去eval更不應該用動態(tài)import這種危險操作。正確的做法是維護一個“名稱到函數(shù)”的映射字典或者用裝飾器把函數(shù)注冊進一個全局注冊表。比如TOOL_REGISTRY {} def register_tool(nameNone): def decorator(func): registry_name name or func.__name__ TOOL_REGISTRY[registry_name] func return func return decorator register_tool() def get_weather(city: str, unit: str celsius): # 實際去調用天氣API return {temperature: 18, condition: 多云, city: city}執(zhí)行完函數(shù)得到結果之后還有一個關鍵步驟結果回流。你要把執(zhí)行結果組裝成一條tool角色的消息追加到對話上下文中然后再帶著這條消息去請求模型讓它繼續(xù)決定是給出最終回答還是發(fā)起下一輪函數(shù)調用。這里有個小技巧返回給模型的內容應該是“結構化的摘要”而不是原始API響應的整段JSON。比如天氣接口可能返回50個字段的氣象數(shù)據但模型做后續(xù)決策只需要其中的“溫度、天氣狀況、風力”。所以你在工具函數(shù)里就應該把結果裁剪、濃縮只保留對后續(xù)推理有意義的字段。4. 實操全流程從零寫一個帶函數(shù)調用的Agent循環(huán)理論講再多不如直接上一份能跑的代碼。這一節(jié)我會帶你從零寫一個最小可用的Agent它支持兩個工具查天氣和給指定郵箱發(fā)提醒郵件。等這個循環(huán)跑通了你就等于掌握了函數(shù)調用的全鏈路骨架之后換工具、加工具、上框架都只是往里面添磚加瓦。4.1 環(huán)境準備與模型選型這個示例我用的Python 3.10OpenAI的Python SDK模型用gpt-4o-mini。你如果用的是其他兼容OpenAI接口格式的服務商比如國內的一些廠商或自建的網關代碼邏輯完全不用改只要換掉base_url和api_key就行。pip install openai寫代碼之前先把API Key配置到環(huán)境變量里。我建議你創(chuàng)建項目根目錄下的.env文件然后用python-dotenv加載避免把Key硬編碼進代碼。密鑰這東西一旦提交到Git倉庫后面漏出去補救了基本就晚了。4.2 構建工具定義與Agent主循環(huán)下面是完整的核心代碼這個結構我建議你保存下來后面做任何Agent項目都可以在這個骨架上擴展import os import json from openai import OpenAI # 初始化客戶端 client OpenAI( api_keyos.getenv(OPENAI_API_KEY), # base_urlhttps://你的網關地址, # 如果用了代理網關放開這行 ) # ---------- 1. 工具定義 ---------- TOOLS [ { type: function, function: { name: get_weather, description: 查詢指定城市的實時天氣。當用戶問到天氣、氣溫、是否下雨、是否適合出行時必須調用此工具。, parameters: { type: object, properties: { city: {type: string, description: 中文城市名例如北京}, }, required: [city] } } }, { type: function, function: { name: send_reminder_email, description: 給指定郵箱發(fā)送一封提醒郵件。當用戶要求發(fā)送郵件、提醒事項、通知時調用。, parameters: { type: object, properties: { to_email: {type: string, description: 收件人郵箱地址}, subject: {type: string, description: 郵件標題}, body: {type: string, description: 郵件正文內容} }, required: [to_email, subject, body] } } } ] # ---------- 2. 真實函數(shù)實現(xiàn)模擬 ---------- def get_weather(city: str): # 實際項目里這里換成真實天氣API調用 return {city: city, temperature: 16, condition: 多云轉晴, humidity: 45} def send_reminder_email(to_email: str, subject: str, body: str): # 實際項目里這里換成郵件服務商API print(f[郵件發(fā)送] 收件人{to_email}, 主題{subject}) return {status: success, message: 郵件已進入發(fā)送隊列} TOOL_REGISTRY { get_weather: get_weather, send_reminder_email: send_reminder_email, } # ---------- 3. 主循環(huán)Agent的核心 ---------- def run_agent(user_input: str, max_steps: int 5): messages [{role: user, content: user_input}] for step in range(max_steps): response client.chat.completions.create( modelgpt-4o-mini, messagesmessages, toolsTOOLS, tool_choiceauto, ) msg response.choices[0].message # 判斷模型是否需要調用工具 if not msg.tool_calls: # 沒有工具調用說明模型已經準備好直接回答了 print(f最終回答: {msg.content}) return msg.content # 有工具調用先把assistant消息放入上下文 messages.append(msg) # 逐個處理工具調用 for tool_call in msg.tool_calls: fn_name tool_call.function.name fn_args json.loads(tool_call.function.arguments) print(f[調用工具] {fn_name}({fn_args})) # 在注冊表里查找并執(zhí)行 if fn_name in TOOL_REGISTRY: result TOOL_REGISTRY[fn_name](**fn_args) else: result {error: f未知工具: {fn_name}} # 把工具執(zhí)行結果作為tool角色消息追加進上下文 messages.append({ role: tool, tool_call_id: tool_call.id, content: json.dumps(result, ensure_asciiFalse), }) # 超過最大步數(shù)兜底 raise RuntimeError(fAgent執(zhí)行超過{max_steps}輪仍未結束) # ---------- 4. 執(zhí)行測試 ---------- if __name__ __main__: run_agent(北京今天天氣怎么樣順便幫我給 zhangsanexample.com 發(fā)個提醒郵件提醒他明天下午開會。)這個循環(huán)其實就是前面說的“五步循環(huán)”發(fā)給模型→模型決定調工具→執(zhí)行工具→結果回傳→再給模型。代碼跑起來后你會在日志里看到模型先調get_weather、再調send_reminder_email、最后根據兩個工具的返回結果生成一段完整的答案。這個時序是模型自主決策的不是你寫死的這正是Agent和傳統(tǒng)腳本最大的區(qū)別。4.3 解析為什么要設置max_steps上限一個Agent循環(huán)里如果模型不夠聰明或者工具返回的信息有誤導它有可能陷入“反復調工具、反復失敗”的死循環(huán)里一次對話消耗幾十次API調用。設置max_steps本質上是給整個循環(huán)加了一個“熔斷器”。我在生產環(huán)境里通常不會設太高3到5步足夠覆蓋絕大多數(shù)場景因為一個正常的任務最多也就連續(xù)調用兩三次工具。如果超過這個輪數(shù)還沒結果寧可返回“我無法完成這個任務”也不要讓用戶等半分鐘還看到一直在轉圈。4.4 多工具場景下的進階從手寫分發(fā)到裝飾器注冊上面的示例里用一個字典做分發(fā)夠用但工具一多就會很凌亂。我建議趁早改成裝飾器注冊的方式把“工具定義”和“業(yè)務函數(shù)”寫在一起減少維護成本import inspect import functools def tool(nameNone): def decorator(func): registry_name name or func.__name__ TOOL_REGISTRY[registry_name] func # 自動從函數(shù)簽名生成JSON Schema描述 TOOL_SCHEMAS.append({ type: function, function: { name: registry_name, description: func.__doc__ or , parameters: generate_schema_from_func(func), } }) return func return decorator tool() def get_weather(city: str): 查詢指定城市的實時天氣。當用戶問到天氣、氣溫、是否下雨時使用。 ...這個方案的好處是工具定義不再單獨維護一份而是從函數(shù)簽名和docstring里自動生成函數(shù)和Schema永遠同步不會出現(xiàn)“代碼里改了參數(shù)定義忘了更新”這種低級問題。等你的Agent工具數(shù)量超過10個你會發(fā)現(xiàn)這個設計幫你節(jié)省了大量精力。5. 進階能力擴展函數(shù)調用怎么跟Agent框架、記憶、多Agent編排結合掌握了基礎循環(huán)之后你會發(fā)現(xiàn)函數(shù)調用其實只是Agent系統(tǒng)的“執(zhí)行底座”。真正讓Agent強大的是在這個底座之上疊加記憶、技能Skill、多Agent協(xié)作等能力。這一節(jié)我結合當前Agent開發(fā)社區(qū)的熱門方向談談怎么把函數(shù)調用往更完整的Agent架構上延伸。5.1 從零散函數(shù)到Agent Skill給函數(shù)加“使用層”最近社區(qū)里“Agent Skill”這個概念很火包括Claude發(fā)布的Agent Skills本質上也是在解決一個問題單個函數(shù)的能力太弱應該把“實現(xiàn)同一目標的一組操作”打包成一個可復用的Skill。舉個例子單純一個get_weather(city)函數(shù)是單一能力但如果Agent接到的任務是“幫用戶規(guī)劃一次周末旅行”它可能先調天氣查詢、再調酒店搜索、再調地圖規(guī)劃這就是三個函數(shù)的組合。與其讓模型每次從頭一步步試探不如提前把這些函數(shù)的調用鏈封裝成一個高階工具比如plan_trip(city, date_range)內部去編排天氣、酒店、路線三個子調用。這個抽象層次的思想跟軟件工程里的“函數(shù)→模塊→服務”演進路徑是一模一樣的。我建議你做Agent時不要一上來就堆50個細粒度函數(shù)那會讓模型在選擇工具時無所適從。更好的策略是先做十幾個粗粒度的Skill每個Skill內部再封裝細粒度的函數(shù)。5.2 函數(shù)調用的結果如何寫入記憶與工作區(qū)Agent在調用函數(shù)執(zhí)行任務的過程中會產生大量中間結果。這些結果如果每次都只在上下文里流轉一方面浪費Token另一方面下次對話就全丟了。這就是“Agent記憶”和“工作區(qū)”要解決的問題。我目前比較推薦的做法是函數(shù)調用的結構化結果除了返回到對話上下文之外同時寫入到一個Agent工作區(qū)里。這個工作區(qū)可以是一個本地的JSON文件、一個向量數(shù)據庫、或者干脆就是目標項目的文件目錄。比如Agent執(zhí)行完save_document工具后文件寫入了本地路徑返回給模型的是一條{status: saved, path: /data/doc_123.md}這樣的消息而不是整個文件內容。模型知道文件已經存到哪兒了但不會把大段文件內容塞進上下文這樣就同時兼顧了可追溯性和Token開銷。5.3 多Agent編排中的函數(shù)調用誰調用結果歸誰多Agent系統(tǒng)現(xiàn)在也是一個熱門話題。這里要特別注意函數(shù)調用不再只是“一個Agent調用一堆工具”而是“多個Agent各自擁有不同的工具集”。架構上要做的是給每個Agent配置獨立的tools列表和獨立的工具注冊表不能讓Agent A調用Agent B的私有工具否則就失去了隔離的意義。還有一個容易出錯的地方是狀態(tài)歸屬Agent A調用了寫文件工具Agent B隨后要讀這個文件如果兩個Agent跑在不同的進程甚至不同的機器上A的寫和B的讀之間就需要一個共享的存儲層。我建議小規(guī)模項目直接用一個Redis或者數(shù)據庫表來當共享工作區(qū)而不是讓Agent B直接去讀Agent A的內存變量。在多Agent的編排下函數(shù)調用的結果還需要帶上“執(zhí)行者”和“時間戳”信息否則日志排查時你會瘋掉。折騰過一兩次就知道這種跨Agent的調用鏈路一旦出問題沒有元信息根本定位不到是誰調錯了參數(shù)。6. 常見問題與排查技巧實錄這些坑我不希望你重踩一遍這一部分是全文最“貴”的內容全部來自我實際開發(fā)Agent時被折磨過的問題。我把它們整理成速查表和分析優(yōu)先看那些跟你癥狀匹配的。6.1 模型該調用函數(shù)卻不調用癥狀用戶明確問了“北京天氣怎么樣”模型卻自己編了一段“根據我的了解北京今天晴……”的幻覺答案完全沒走工具調用。排查順序先看tools參數(shù)是否真的傳進去了、tool_choice是否被誤設成了none。這兩個是低級錯誤檢查完基本能排除。然后再看函數(shù)描述里有沒有寫清楚“觸發(fā)時機”如果只寫了“查詢某個城市的天氣”這種靜態(tài)描述模型確實容易把工具調用當成可選項。解決辦法把描述改成帶觸發(fā)條件的動態(tài)表述。比如“當用戶詢問道市天氣、氣溫、是否下雨、是否適合戶外活動時必須調用此工具來獲取實時數(shù)據不得自行編造天氣信息?!奔右痪洹安坏米孕芯幵臁睂τ谝种苹糜X很有效。6.2 參數(shù)類型不對傳了字符串而不是數(shù)字癥狀函數(shù)定義里days參數(shù)是integer類型模型卻傳了3天這種值導致類型校驗報錯。原因模型的Token化過程對中文和數(shù)字的混合輸入很不敏感。你定義的是JSON Schema但模型是在做文本生成它不一定嚴格遵循類型約定。解決辦法除了在Schema里聲明類型還要在參數(shù)描述里寫清楚“只傳數(shù)字不要帶單位”。更穩(wěn)妥的辦法是在工具函數(shù)的入口做一次顯式類型轉換比如def set_reminder(days): days int(str(days).replace(天, ).strip()) ...這屬于防御式編程寧可代碼里多兩行也不要讓一個參數(shù)錯誤導致整條鏈路崩潰。6.3 并發(fā)場景下函數(shù)調用狀態(tài)串了癥狀Agent服務上線后一旦同時服務多個用戶就出現(xiàn)A用戶的工具調用結果跑到了B用戶的上下文里或者函數(shù)執(zhí)行時用錯了參數(shù)。原因絕大多數(shù)情況是消息列表被設計成了共享變量。記住一個原則Agent的對話上下文是強隔離的每一個用戶會話必須有自己獨立的messages列表不能有全局共享的上下文。解決方案生產環(huán)境里用會話ID做維度管理上下文。每一次請求都從會話存儲里讀取屬于該會話的消息列表函數(shù)執(zhí)行結果也按會話ID寫回。如果需要并發(fā)執(zhí)行多個工具調用還記得給每個工具調用的結果帶上tool_call_id確保模型能正確匹配。6.4 工具返回結果太大上下文爆炸癥狀跑了一陣之后發(fā)現(xiàn)每次請求的Token消耗越來越大后來一查是某個工具把一份5000行的數(shù)據全量返回給了模型。原因工具返回結果進入了一直累積的對話上下文不會被自動清理大結果反復出現(xiàn)Token消耗自然飆升。解決辦法三個字截、摘、引。截是只返回前N行摘是讓工具內部先做一次摘要只把摘要返回給模型引是對于極大的數(shù)據量把數(shù)據存到數(shù)據庫/文件只返回一個可以檢索的ID或路徑。我在生產項目里這三招組合使用Token成本直接降了60%以上。6.5 安全邊界函數(shù)調用的權限必須收口癥狀有開發(fā)者在Agent里暴露了一個執(zhí)行任意Shell命令的工具結果模型在某個輸入誘導下執(zhí)行了一串危險命令。原因沒有做權限管控。函數(shù)調用的本質是把你系統(tǒng)里已有的能力暴露給模型而模型又會聽用戶的。用戶輸入是無限的模型的判斷不是萬無一失的所以你必須假設“用戶正在嘗試攻擊這個Agent”。解決辦法這幾點我建議作為Agent開發(fā)的紅線第一高危工具刪除文件、執(zhí)行命令、轉賬、發(fā)短信一律不暴露給模型改成由Agent生成“待確認操作卡片”由用戶在前端手動確認后再執(zhí)行。第二所有工具函數(shù)的參數(shù)要做白名單校驗比如文件路徑只能落在指定目錄內。第三復雜操作增加一個“審計日志”記錄每次工具調用的完整參數(shù)和結果。這三條都做到你基本就擋住了90%的常規(guī)攻擊路徑。7. 函數(shù)調用排查速查表與最后的經驗總結把前面幾節(jié)的內容壓縮成一張速查表直接在排查時對號入座癥狀常見原因優(yōu)先排查項模型不調用工具tools參數(shù)未傳、描述缺少觸發(fā)條件tool_choice設置、description觸發(fā)詞參數(shù)格式錯誤類型不符、單位混淆參數(shù)描述明確類型、函數(shù)入口防御轉換調用結果不生效tool_call_id不匹配、上下文漏追加消息assistant消息和tool消息的順序、id匹配Token消耗暴漲工具返回全量數(shù)據、上下文無限累積結果截斷/摘要、消息列表窗口管理并發(fā)結果串線會話上下文全局共享按會話ID隔離messages列表函數(shù)報錯中斷執(zhí)行異常未捕獲try/except包裹工具執(zhí)行、異常轉為錯誤消息回傳模型模型越權調用高危函數(shù)權限未收斂高危操作人工確認、參數(shù)白名單我個人在實際操作中的體會是函數(shù)調用的實現(xiàn)其實不難難的是圍繞它的整套工程化設計——容錯、隔離、審計、成本控制這些才是Agent能否長期穩(wěn)定跑下去的關鍵。很多人覺得Agent開發(fā)就是“調一個API就完事了”但真正沉淀下來的能力恰恰是在這些細節(jié)里。最后再分享一個小技巧給你的每個工具寫一句話的“使用時機說明”。這句話不是給用戶看的也不是給代碼看的而是給模型看的。模型決定調不調用工具主要依據就是工具描述和當前對話意圖之間的匹配度。這一句描述寫得好不好直接影響工具調用率值得你花時間反復打磨。我通常是跑一批真實用戶日志看看哪些工具調用率低、哪些工具被誤調用針對性調描述調完再跑一輪一般兩三輪之后工具選擇準確率就能穩(wěn)定在95%以上。這個經驗希望你下次做個帶工具的Agent時能幫你少走不少彎路。