Agent工程實踐:從狀態(tài)管理到失敗恢復(fù)的關(guān)鍵設(shè)計)
做長任務(wù) Agent 的工程人應(yīng)該都經(jīng)歷過這種深夜瞬間凌晨三點日志里躺著一行timeout after 30000ms任務(wù)卡在 70% 的位置前幾個小時的 Token 全部白燒你還得爬起來手工清理狀態(tài)、回滾數(shù)據(jù)、重新觸發(fā)。那一刻你會產(chǎn)生一個很現(xiàn)實的疑問我們費這么大勁到底是讓 AI 幫我們省時間還是讓我們陪它熬夜過去兩年我做了不少 Agent 原型。單輪問答型的最開心兩三百行代碼模型一調(diào)用、答案出來、任務(wù)結(jié)束效果肉眼可見。但當(dāng) Agent 從“回答一個問題”變成“完成一項工作”——比如連續(xù)跑一個月的市場分析、自動整理幾百個文件、無人值守地調(diào)度外部系統(tǒng)——工程問題的性質(zhì)就徹底變了。這不是把 Prompt 寫長一點能解決的。從單次回答走向長任務(wù)執(zhí)行真正發(fā)生變化的是Agent 不再是一個無狀態(tài)的函數(shù)而是一個需要持續(xù)運行、重復(fù)讀寫狀態(tài)、隨時應(yīng)對失敗、被監(jiān)控和限制的長期進程。它從一個“會說話的模型”變成了一個“必須被托管的工作負載”。于是在這個轉(zhuǎn)變背后問題就浮出來了工程工作到底發(fā)生在哪里我的結(jié)論很明確工程重心不在模型這一側(cè)而在圍繞任務(wù)構(gòu)建的整個執(zhí)行框架里——狀態(tài)管理、編排控制、失敗恢復(fù)、成本護欄、可觀測性。下面我就基于自己做過長任務(wù) Agent 的真實項目把這幾個核心位置分別拆開講清楚每一處到底在做什么以及為什么這些工作沒有辦法用“模型智能”替代。1. 單次回答的思維慣性為什么扛不住長任務(wù)執(zhí)行1.1 無狀態(tài)回答與有狀態(tài)進程之間的鴻溝當(dāng)我們調(diào)用大模型聊天接口時本質(zhì)上在做一件非常簡單的事把一段文本發(fā)給模型模型基于上下文生成一段新文本返回。整個過程是無狀態(tài)的——輸入、輸出、結(jié)束。即使是多輪對話也不過是每次把歷史拼接進去模型自己并不維護真正的“記憶”。長任務(wù)執(zhí)行則完全不同。假設(shè)一個 Agent 被要求“每天早晨抓取行業(yè)新聞生成分析摘要發(fā)送到項目群并歸檔到知識庫”這個任務(wù)要連續(xù)運行 30 天。這時候真正重要的問題變成了今天幾點運行跑完沒有上次生成了什么摘要和昨天的內(nèi)容重復(fù)了嗎抓取接口突然掛了是跳過今天還是持續(xù)重試消息發(fā)送成功但歸檔寫失敗算完成還是部分完成用戶中途改了目標是重啟任務(wù)還是斷點續(xù)跑這些問題沒有一個是模型“聰明”一點就能自己解決的它們幾乎全部依賴工程層面對任務(wù)狀態(tài)的建模。我把這個差距概括為“函數(shù)式思維”與“進程式思維”的區(qū)別。單次回答是函數(shù)輸入 X返回 Y。長任務(wù)是進程啟動、運行、暫停、恢復(fù)、終止中間還要持久化大量中間產(chǎn)物。1.2 長任務(wù) Agent 的狀態(tài)到底包含什么實際落地時至少要維護這幾類狀態(tài)狀態(tài)類別典型字段說明任務(wù)目標狀態(tài)objective、current_phase、status當(dāng)前做到哪一步目標有沒有變化執(zhí)行歷史狀態(tài)events、step_results、checkpoints每一步做了什么、結(jié)果如何依賴資源狀態(tài)credentials、config、外部系統(tǒng)連接信息運行環(huán)境是否就緒預(yù)算與配額狀態(tài)token_budget、cost、remaining_retry防失控的核心數(shù)據(jù)人工協(xié)同狀態(tài)waiting_for_human、decision_log哪些節(jié)點在等人審批用結(jié)構(gòu)化存儲來維護這些狀態(tài)而不是全部塞進對話歷史里讓模型去“記住”。模型再聰明狀態(tài)也必須滿足三個硬性要求可查詢、可恢復(fù)、可審計。對話歷史天然不具備這些能力。你很難從一個聊天記錄里回答“上一步到底用了哪個工具參數(shù)”但你能從一張結(jié)構(gòu)化事件表里兩秒查出來。1.3 工程視角的轉(zhuǎn)變從提示設(shè)計到執(zhí)行設(shè)計早幾年聊 Agent 工程大多數(shù)人默認核心是 Prompt。在單輪場景下Prompt 確實能決定 90% 的效果。但長任務(wù)場景跑下來你會發(fā)現(xiàn)“提示語”的作用在快速衰減——因為失敗的環(huán)節(jié)往往不是“模型理解不了指令”而是“任務(wù)跑到中段狀態(tài)臟了”“外部系統(tǒng)返回了完全意外的數(shù)據(jù)”“超時重試把業(yè)務(wù)做重復(fù)了”。我做過的幾個長任務(wù)項目基本都經(jīng)歷了三個階段。第一階段是興奮期覺得只要描述清楚目標Agent 就能自己搞定一切于是把目標塞進 Prompt直接while true循環(huán)調(diào)用。第二階段是翻車期任務(wù)跑兩天后各種問題接踵而至上下文爆炸、死循環(huán)、重復(fù)執(zhí)行、任務(wù)中斷后無法恢復(fù)、賬單嚇人所有人開始瘋狂打補丁。第三階段才是工程期老老實實把任務(wù)當(dāng)成一個分布式系統(tǒng)來設(shè)計狀態(tài)落庫、步驟冪等、失敗分級、預(yù)算截斷、全程可觀測之后穩(wěn)定性才真正上來。這個歷程太典型了。所以先給一個結(jié)論放在這里**長任務(wù) Agent 的工程工作不在提示詞里而在任務(wù)運行時的工程容器里。**接下來的幾個章節(jié)就是對這個容器核心模塊的逐層拆解。2. 任務(wù)不是對話把目標拆解成可落庫的狀態(tài)圖2.1 拆解層級目標、階段、步驟長任務(wù)管控的第一步是把“一個模糊的大目標”拆成計算機能管理的最小單元。我常采用四層結(jié)構(gòu)Objective目標一句話定義最終成果例如“完成 Q3 競品分析報告”。Milestone階段大目標的分隔點例如“收集數(shù)據(jù)”“分析數(shù)據(jù)”“生成報告”。Task任務(wù)具體工作項例如“抓取 5 個競品官網(wǎng)的更新動態(tài)”。Step步驟一次原子操作例如“調(diào)用瀏覽器工具抓取 xx 網(wǎng)頁并解析正文”。這里有個實操經(jīng)驗拆得越細可控性越強但編排成本也越高。比較合適的粒度是“一個 Step 對應(yīng)一次外部動作或一次模型決策”比如一次搜索、一次讀取、一次寫入、一次需要判斷的節(jié)點。這樣既能保證恢復(fù)粒度又不會讓狀態(tài)表爆炸。拆解之后并不是所有任務(wù)都是線性執(zhí)行的。很多長任務(wù)天然包含分支、等待、循環(huán)。所以底層建議用狀態(tài)機或任務(wù)圖來建模。每個 Step 可以有前置條件、執(zhí)行動作、后置校驗前置條件不滿足就阻塞等待。但我也要潑一盆冷水**千萬不要一上來就搞流程引擎。**在任務(wù)還只有十幾個 Step 時用數(shù)據(jù)庫狀態(tài)表加循環(huán)調(diào)度就足夠了甚至可以是最樸素的while switch。等真正需要處理并發(fā)和復(fù)雜依賴時再上圖形化編排引擎。否則你會有一種“為了裁一棵樹買了整個家具工廠”的荒謬感。2.2 狀態(tài)存儲與檢查點跑一半掛了不能從頭再來任務(wù)一旦開始運行最重要的工程動作是“隨時落狀態(tài)”。每完成一個 Step就把這個 Step 的輸入、輸出摘要、Token 消耗、開始結(jié)束時間寫進持久化存儲。模型調(diào)用是昂貴的而落庫是廉價的不要省這筆錢。我習(xí)慣用這樣一組表來承載任務(wù)狀態(tài)task_instance表task_idobjectivestatuscurrent_phaseinput_snapshot任務(wù)啟動時的完整輸入?yún)?shù)JSON 快照created_atupdated_atlast_heartbeattask_event表event_idtask_idstep_idevent_typedatacreated_atstep_result表idtask_idstep_namestatusinput_summaryoutput_summarytoken_usagemodellatency_msstarted_atfinished_attask_event尤其重要。它是一條只增不減的事件流每一次決策、每一次工具調(diào)用、每一次失敗都追加為一條事件。為什么要用事件流而不是只維護一份“最新狀態(tài)”第一事件流天然可回放排查問題時能看到完整決策路徑第二事件流支持冪等重建即使?fàn)顟B(tài)表損壞也能從事件記錄里恢復(fù)第三做審計和成本分析時只需要掃這一張表。檢查點checkpoint則是為了恢復(fù)。在任務(wù)的生命周期里每完成一個安全節(jié)點就寫入檢查點。所謂安全節(jié)點通常是“所有前置操作已完成且結(jié)果已確認”的位置。進程重啟后調(diào)度器讀取最近檢查點把目標、當(dāng)前階段、近期事件摘要重新組裝進 Prompt讓 Agent 接著跑而不是重頭再來。2.3 冪等設(shè)計長任務(wù)最常見的翻車現(xiàn)場長任務(wù)工程中重復(fù)執(zhí)行是頭號事故來源而且越簡單的場景越容易翻車。舉一個我踩過的例子某個文檔處理任務(wù)流程是“讀取文件 → 解析內(nèi)容 → 調(diào)用模型總結(jié) → 寫入輸出 → 發(fā)通知”。某次模型總結(jié)步驟超時了程序判定失敗并重試整個任務(wù)。重啟后從第一步重新執(zhí)行結(jié)果文件被寫入了兩份下游同事收到了兩次重復(fù)通知。單次回答場景里根本不會有這種問題——回答失敗就重新問一次模型沒有副作用。但長任務(wù)里每一步都可能對外部系統(tǒng)產(chǎn)生真實影響重放操作就是真實的生產(chǎn)事故。解決思路其實不復(fù)雜為每個外部寫操作生成一個冪等鍵。比如“數(shù)據(jù)導(dǎo)入”任務(wù)中每條導(dǎo)入記錄都帶上source_batch_id record_no作為唯一鍵寫入時數(shù)據(jù)庫按唯一鍵去重。批量工具調(diào)用、發(fā)消息、生成文件、提交工單全部做同樣的處理。另一個常用做法是把“命令是否已執(zhí)行”記錄在步驟狀態(tài)里重試時先查狀態(tài)如果該步驟已完成就直接跳過、進入下一步。實際中還需要給外部 API 調(diào)用編號每次模型說要調(diào)用工具先分配一個invocation_id執(zhí)行結(jié)果和invocation_id綁定重試同一決策時帶上相同 ID外部系統(tǒng)就能識別重復(fù)。這不是新發(fā)明就是把分布式系統(tǒng)里的“冪等消費”概念搬進 Agent 任務(wù)的每一個副作用操作上。3. 編排的核心戰(zhàn)場兩次模型調(diào)用之間發(fā)生了什么3.1 Agent 循環(huán)的完整拆分很多人理解的 Agent 循環(huán)是三行偽代碼讓模型思考、讓模型調(diào)用工具、把結(jié)果還給模型。真正工程化后的循環(huán)要復(fù)雜得多我把它拆成六個連續(xù)階段讀取狀態(tài)從任務(wù)存儲中取出當(dāng)前階段、目標、前置結(jié)果。組裝上下文按預(yù)算裁剪相關(guān)歷史、檢索必要信息、拼接當(dāng)前指令。模型決策模型產(chǎn)出下一步動作可能是調(diào)用某個工具、指定參數(shù)或給出中間結(jié)論。動作執(zhí)行工具層執(zhí)行外部操作可能是 HTTP 請求、文件讀寫、數(shù)據(jù)庫查詢、發(fā)送消息。結(jié)果驗證用確定性規(guī)則或獨立校驗器評估動作結(jié)果是否符合預(yù)期。狀態(tài)回寫將動作結(jié)果、驗證結(jié)論、Token 消耗追加到事件流推進狀態(tài)機。在這六步里最容易被忽略也最容易出事的是第 5 步“結(jié)果驗證”。大量 Agent 實現(xiàn)是“模型說調(diào)用就調(diào)用工具返回什么直接塞回 Prompt”完全不做結(jié)果層面的判斷。于是會出現(xiàn)這種情況搜索接口返回了 500 錯誤頁模型卻把錯誤內(nèi)容當(dāng)成正常搜索結(jié)果繼續(xù)分析發(fā)送郵件接口返回“成功”但實際發(fā)到了測試郵箱表格寫入說完成實際只有表頭。我的工程要求很簡單每一個工具動作都配一個驗證模塊能驗證就寫確定性斷言。比如“響應(yīng) status 必須是 200 且 body 包含 title 字段”“寫庫后必須能 SELECT 回來”。模型可以判斷語義但工程上一定要有規(guī)則兜底。3.2 上下文管理長任務(wù)的慢性病這是長任務(wù) Agent 區(qū)別于單次回答最折磨人的地方。任務(wù)跑到第 20 輪如果每輪都把全部歷史塞回模型Prompt 會迅速膨脹。我第一次做長任務(wù)時跑到第 15 輪就發(fā)現(xiàn)模型開始“忘記”用戶最初的目標經(jīng)常答非所問。排查時一查 Token 用量發(fā)現(xiàn)每輪上下文已經(jīng)超過 5 萬 Token。根因根本不是模型不行而是工程上沒有做上下文治理。目前常用的手段有以下幾種滾動摘要早期步驟的原始結(jié)果每隔幾輪被模型壓縮成一個摘要后續(xù)只保留摘要摘要過長再壓縮成摘要的摘要。結(jié)構(gòu)化外部化工具調(diào)用的完整輸出存進數(shù)據(jù)庫Prompt 里只放“該步驟已完成結(jié)果見附件 ID x”這類引用信息需要細節(jié)時再用檢索工具去取。裁剪與召回對歷史事件按重要度打分保留高價值信息低價值信息裁剪后存外部記憶需要時通過語義搜索召回。上下文預(yù)算每輪進入模型前計算 Token 預(yù)算比如單輪限制 16K歷史太長就先做摘要再進 Prompt。核心原則是**長期記憶是數(shù)據(jù)庫的職責(zé)不是 Prompt 的職責(zé)。**模型只需要拿到“眼前這一步需要的最小信息集”。這也是長任務(wù) Agent 和大模型單輪對話在工程上最本質(zhì)的差異之一。3.3 人工確認節(jié)點與等待語義不是所有動作都應(yīng)該讓 Agent 自己說了算。在長任務(wù)里凡是“成本高、影響大、不可逆”的動作都應(yīng)該插入人工確認節(jié)點。比如對外發(fā)送通知、提交訂單、刪除數(shù)據(jù)、發(fā)布內(nèi)容。如果沒有審批機制Agent 一旦決策失誤后果會被放大成真實事故。工程實現(xiàn)上我一般讓狀態(tài)機支持waiting_for_human這個狀態(tài)。當(dāng) Agent 走到審批節(jié)點時不是繼續(xù)瞎猜而是把待確認內(nèi)容、影響范圍、下一步選項推送給用戶可以通過 IM、郵件或控制臺任務(wù)卡展示然后任務(wù)掛起、存儲狀態(tài)、開啟超時計時。用戶在控制臺點擊“批準 / 拒絕 / 修改參數(shù)”任務(wù)恢復(fù)。等待語義必須顯式設(shè)計否則很容易出現(xiàn)“任務(wù)看似活著但卡住了”的現(xiàn)象。我給每個waiting_for_human節(jié)點也設(shè)置超時。比如策略是超過 6 小時未確認則再次推送超過 24 小時未確認則自動掛起并通知管理員。這樣人工環(huán)節(jié)本身也不會成為靜默堵點。這套等待恢復(fù)機制的技術(shù)含量不算高但它直接決定了 Agent 能否被放心地投入真實業(yè)務(wù)流程。沒有人工確認節(jié)點的長任務(wù) Agent本質(zhì)上是一個沒有剎車的自動駕駛性能和風(fēng)險成正比。4. 失敗分類與重試設(shè)計長任務(wù)工程里最殘酷的一課4.1 失敗的層次不是所有失敗都該重試長任務(wù)跑久了失敗是必然的問題只在于系統(tǒng)怎么應(yīng)對失敗。如果對所有失敗一視同仁統(tǒng)一走“重試”那最直接的后果是有些重試毫無意義有些重試把問題放大。我把運行中遇到的失敗分成四類失敗類型典型例子處理策略瞬時錯誤網(wǎng)絡(luò)超時、限流、服務(wù) 5xx指數(shù)退避重試上限約 3-5 次確定性錯誤參數(shù)不合法、權(quán)限不足、文件不存在不重試讓模型修正參數(shù)或改變策略業(yè)務(wù)校驗失敗工具執(zhí)行成功但結(jié)果不符合預(yù)期規(guī)則返回校驗信息模型換方案限次數(shù)不可收斂模型反復(fù)嘗試同樣的失敗動作強制暫停轉(zhuǎn)入人工這個分類看起來簡單實際意義很大。瞬時錯誤重試是正確的因為外部服務(wù)抖一下會自己恢復(fù)確定性錯誤重試一百次也是同樣結(jié)果不如把錯誤信息回傳給模型讓模型重新思考動作參數(shù)業(yè)務(wù)校驗失敗不能簡單重試要讓模型理解“這次動作雖然執(zhí)行成功但結(jié)果不滿足驗收條件”。我還會給每個 Step 加一個retry_policy字段不同步驟掛不同的策略而不是走全局同一套。比如“調(diào)用外部接口”默認指數(shù)退避重試 3 次“寫入數(shù)據(jù)庫”默認失敗即停、需要人工處理“發(fā)送通知”默認失敗后轉(zhuǎn)入補償流程。策略掛在步驟上恢復(fù)邏輯才有針對性。4.2 重試的放大效應(yīng)從單點抖動到任務(wù)雪崩這里想單獨講一個高頻翻車場景重試疊加導(dǎo)致的雪崩。假設(shè)一個 Agent 任務(wù)是批量調(diào)用某個第三方平臺的開放接口每個批次要處理 2000 條數(shù)據(jù)。某次對方接口開始限流如果重試策略是無腦重試會發(fā)生什么第一次調(diào)用失敗等 0.5 秒重試失敗等 1 秒所有 Worker 同步重試瞬間把第三方接口打到更嚴重的限流形成惡性循環(huán)。其結(jié)果不僅是任務(wù)失敗還可能因為觸發(fā)對方封禁策略導(dǎo)致整個賬號被限制。這類問題的解法在傳統(tǒng)后端領(lǐng)域已經(jīng)很成熟直接平移過來就好退避抖動 熔斷 并發(fā)限制。我在任務(wù)調(diào)度層加了一個熔斷開關(guān)當(dāng)某個外部服務(wù)的連續(xù)失敗率達到閾值比如 30%就把對這個服務(wù)的調(diào)用暫時降級或暫停讓任務(wù)先處理其他分支等冷卻窗口過了再恢復(fù)。加了熔斷之后第三方抖動造成的連帶傷害肉眼可見地減少了。關(guān)鍵轉(zhuǎn)變在于Agent 的任務(wù)框架不能把“模型調(diào)用”和“外部工具調(diào)用”混為一談。模型調(diào)用失敗可以用重試解決但外部工具調(diào)用失敗要考慮業(yè)務(wù)語義。拿捏不住這一點所謂的智能 Agent 會在最簡單的問題上暴露最原始的架構(gòu)缺陷。4.3 部分完成與補償任務(wù)不是非黑即白長任務(wù)里最難處理的不是“完全失敗”而是“部分完成”。比如一個任務(wù)的目標是“把 10 份文件從 A 系統(tǒng)同步到 B 系統(tǒng)”跑到第 7 份時 A 系統(tǒng)接口掛了。這時候整任務(wù)標記為failed不公平前 7 份白干了標記為success更不對還有 3 份沒完成。我建議的做法是讓任務(wù)狀態(tài)機支持“部分完成 斷點續(xù)跑”。任務(wù)本身維持running或升級為suspended狀態(tài)但明確記錄已完成步驟列表、失敗步驟列表、剩余步驟列表。重啟恢復(fù)后調(diào)度器從剩余步驟開始而不是重新執(zhí)行所有步驟。這就是檢查點機制真正的價值。另外還要考慮補償compensation。有些操作一旦部分執(zhí)行必須做回滾或?qū)_。比如批量提交審批的任務(wù)提交到一半失敗這時候如果直接續(xù)跑可能造成重復(fù)提交正確方式是先查詢未審批單據(jù)補提交缺失的單據(jù)并清除重復(fù)記錄。補償邏輯最好在任務(wù)編排階段就定義好而不是等事故發(fā)生了再來打補丁。4.4 防死循環(huán)別讓 Agent 進入“螺旋加班”狀態(tài)長任務(wù)自動循環(huán)跑著跑著出現(xiàn)死循環(huán)是真實存在的。我自己就遇過Agent 被要求整理一批 PDF其中一份文件解析失敗。模型試圖重試解析失敗換一種解析方法還是失敗換個標題再看一次還是失敗。整個任務(wù)卡在同一個文件上循環(huán)了將近 30 輪消耗大量 Token毫無進展。為什么會出現(xiàn)這種情況因為模型在開放場景下天然傾向于“繼續(xù)嘗試”而沒有一個“我應(yīng)該停下來求助”的機制。這個傾向在單次問答里無所謂在長任務(wù)里就是燒錢黑洞。工程上的防死循環(huán)主要有三條。一是全局循環(huán)上限比如每個階段最多執(zhí)行 N 輪模型決策超過就強制暫停。二是失敗模式識別連續(xù) M 次收到同一類錯誤自動標記“此處需要人工判斷”。三是給模型顯式的退出信號在系統(tǒng)提示詞中聲明“如果同一目標嘗試多次仍失敗請生成 NEED_HELP 決策而不是繼續(xù)嘗試?!边@個設(shè)計本質(zhì)上是在給“自由意志”裝剎車。沒有剎車的 Agent 不是效率工具是風(fēng)險工具。5. 護欄越界即事故成本、權(quán)限與完成度驗證5.1 成本預(yù)算Token 不是自來水是真金白銀做單次回答 Agent 的人通常對 Token 成本不敏感一次問答幾毛錢無所謂。但長任務(wù) Agent 的成本模型完全不一樣任務(wù)持續(xù)運行、頻繁循環(huán)幾十輪調(diào)用是常態(tài)。如果任務(wù)里還包含文本處理、PDF 解析、批量搜索每天的 Token 消耗可能是單次回答的幾十倍甚至上百倍。我有一次跑一個“批量總結(jié)競品文檔”的任務(wù)沒有做預(yù)算限制結(jié)果一個晚上跑了 128 輪模型調(diào)用消耗超過 120 萬 Token。賬單出來的時候我整個人是清醒的智能的代價是真的貴。工程上必須做三重預(yù)算控制任務(wù)級預(yù)算給每個 Task 設(shè)置最大 Token 數(shù)和最大金額超限自動暫停。階段級預(yù)算每個 Milestone 分配額度防止單階段耗盡全局可用資源。實時監(jiān)控每次模型調(diào)用后累計消耗超過閾值觸發(fā)告警超過硬頂強制凍結(jié)任務(wù)。預(yù)算告警要接人工通知。比如消耗超過 50% 時 IM 通知超過 80% 時再次通知超過 100% 自動掛起。不要讓一個無人值守任務(wù)悄悄把預(yù)算燒完。5.2 最小權(quán)限與高風(fēng)險動作審批長任務(wù)的執(zhí)行環(huán)境比單次回答更需要權(quán)限管控。原因很簡單單次回答沒有外部副作用但長任務(wù)會調(diào)用真實工具、真實接口、真實寫操作。如果你給 Agent 配了一個“管理員”級別的賬號它一旦被惡意注入的指令誤導(dǎo)后果就是整個系統(tǒng)范圍內(nèi)的事故。我的經(jīng)驗法則是**給 Agent 進程分配最小的專用憑證只開放任務(wù)必需的 API 權(quán)限所有刪除、覆蓋、發(fā)送、支付、發(fā)布類動作默認走人工審批節(jié)點絕不賦予 Agent 自動執(zhí)行權(quán)限。**工具調(diào)用使用允許名單模式Agent 只能調(diào)用名單內(nèi)的工具遇到未授權(quán)工具直接返回“無權(quán)限”而不是繼續(xù)嘗試。有人會說這樣 Agent 就不“自主”了。我的回答是自主是工程給的邊界內(nèi)的自主不是無限自由。真實的自動化系統(tǒng)從來都是“被約束的自動化”不約束的 Agent 只能待在 Demo 里。5.3 完成度驗證Agent 說做完了不等于真的做完了長任務(wù)“跑完”并不等于“做好”。模型的自我評估天然帶有樂觀傾向它傾向于認為自己已經(jīng)完成了用戶目標。工程上必須對完成度做獨立驗證。我在任務(wù)結(jié)尾會加一個“完成校驗階段”做三件事讓 Agent 輸出執(zhí)行報告完成了哪些步驟、每步結(jié)果的依據(jù)是什么、哪些地方和原目標有偏差。通過自動化規(guī)則核查關(guān)鍵產(chǎn)出比如“報告文件存在且非空”“所有源文件已歸檔”“目標系統(tǒng)記錄數(shù)已核對”。人工抽查或獨立評審對關(guān)鍵任務(wù)把執(zhí)行報告和核心產(chǎn)物發(fā)給一個獨立評審者可以是人也可以是另一個模型實例評審?fù)ㄟ^后才允許把任務(wù)標記為success。這套“做完 驗完 完成”的語義看起來保守但在真實業(yè)務(wù)里非常有用。它讓任務(wù)從“跑完流程”升級為“交付結(jié)果”用戶對 Agent 的信任也是這么一點點建立起來的。6. 長任務(wù)的可觀測性Debug 從“猜謎”變成“回放”6.1 事件流打底每輪決策都值得留痕長任務(wù) Debug 和單次回答 Debug 完全兩碼事。單次回答出問題直接看模型輸出就完了。長任務(wù)出問題你需要回答的是這個 Agent 為什么在昨天下午三點做了一個奇怪的操作它當(dāng)時看到了什么上下文哪個步驟觸發(fā)了它做這個決策要回答這些問題必須讓執(zhí)行過程可回放。前面說的task_event表就是基礎(chǔ)每個模型調(diào)用記錄當(dāng)時組裝后的完整 Prompt 摘要、模型、輸入輸出 Token、耗時每次工具調(diào)用記錄工具名、參數(shù)、輸出摘要、返回狀態(tài)每個錯誤記錄錯誤類型、錯誤信息、重試次數(shù)。所有事件按時間順序落庫。注意一個實操細節(jié)不要把完整 Prompt 都存進每個事件會很占空間。我通常存“Prompt 摘要 關(guān)鍵上下文片段 完整調(diào)用 ID”調(diào)用 ID 指向獨立日志存儲。這樣既保證可回放又不至于把數(shù)據(jù)庫撐爆。6.2 任務(wù)時間線與回放模式有了事件流就可以給每個任務(wù)生成一條時間線從啟動、規(guī)劃、每一步?jīng)Q策、工具調(diào)用、失敗重試、人工等待到最終完成或終止。界面按時間展示每條事件包含時間、類型、關(guān)鍵摘要?;胤拍J绞俏易鲩L任務(wù)最依賴的 Debug 手段。所謂回放就是按時間順序把事件重新走一遍重現(xiàn) Agent 的決策路徑。遇到“任務(wù)結(jié)果完全不符合預(yù)期”的情況第一件事就是打開回放看它在哪一步開始偏離目標是哪條上下文讓它做出了錯誤判斷。答案比盲改 Prompt 快得多??梢怨蚕硪粋€經(jīng)驗數(shù)據(jù)我修長任務(wù)問題的耗時分布大約 40% 在“定位哪一步出了問題”30% 在“理解當(dāng)時為什么會這么決策”20% 在“制定修復(fù)方案”10% 在“實施修復(fù)”。如果沒有事件流和回放前兩項會無限膨脹Debug 基本靠猜。6.3 心跳與靜默告警長任務(wù)的“活人監(jiān)護”長任務(wù)系統(tǒng)最危險的一個狀態(tài)是“靜默死亡”進程還在線程還在但循環(huán)不再推進也沒有任何報錯。這個狀態(tài)比崩潰更可怕。崩潰會立刻觸發(fā)告警靜默死亡不會。防止靜默死亡的辦法是給任務(wù)加心跳。每次狀態(tài)變更都更新last_heartbeat字段調(diào)度器或定時巡檢定期掃描如果某個running狀態(tài)的任務(wù)心跳超過閾值比如 10 分鐘沒有變化就判定“可能卡住”觸發(fā)告警并把任務(wù)掛起。實際中我還加過“看護進程”如果任務(wù)在一個階段內(nèi)沒有任何事件產(chǎn)出看護進程會主動詢問模型“當(dāng)前還在處理嗎”如果模型連續(xù)兩次未正確響應(yīng)就強制重啟該階段或轉(zhuǎn)入人工。有人擔(dān)心這樣會誤傷正在慢速運行的任務(wù)。會所以心跳閾值要結(jié)合任務(wù)特點設(shè)置耗時長的外部等待可以明確標記為“等待外部系統(tǒng)”不算靜默。這里的原則是寧可偶爾誤告警也不要讓異常靜默吞掉生產(chǎn)事故。6.4 復(fù)盤跑完任務(wù)后把日志變成經(jīng)驗最后想提一個工作習(xí)慣每個長任務(wù)跑完一版后花時間做一次“任務(wù)復(fù)盤”。我會讓系統(tǒng)自動生成執(zhí)行摘要總耗時、總 Token 消耗、失敗最多的階段、人工介入次數(shù)、成本分布。然后人工查看這些數(shù)據(jù)找出最值得優(yōu)化的環(huán)節(jié)??赡苁悄硞€工具調(diào)用頻繁失敗可能是某類 Prompt 總引導(dǎo)模型走彎路也可能是上下文裁剪策略過于激進導(dǎo)致模型丟失關(guān)鍵信息。這一步看似不是核心功能但實際上對長任務(wù) Agent 的持續(xù)優(yōu)化至關(guān)重要。沒有復(fù)盤你就是反復(fù)踩同一個坑有復(fù)盤每跑一周任務(wù)系統(tǒng)就變聰明一點。工程上的長期主義往往就是從這些不起眼的例行流程里長出來的。做長任務(wù) Agent 這一年多我最大的體會是當(dāng) Agent 從“單次回答”走向“長任務(wù)執(zhí)行”工程上真正發(fā)生工作的位置幾乎全都在模型外部——狀態(tài)怎么存、失敗怎么處理、成本怎么限制、行為怎么審計、結(jié)果怎么驗證。模型的智能負責(zé)做決策工程框架負責(zé)讓決策安全落地。這兩者之間不存在替代關(guān)系Prompt 寫得再好也替代不了冪等鍵、檢查點和預(yù)算熔斷。所以如果你正打算開始做長任務(wù) Agent我的建議很直白先別急著調(diào) Prompt。先把任務(wù)狀態(tài)表設(shè)計好、把失敗分類定義清楚、把可觀測性底子打牢然后再去追求 Agent 的自由度??蚣芊€(wěn)了Agent 才能真正跑得遠。