戰(zhàn):從原理到生產(chǎn)環(huán)境落地)
1. 為什么“會(huì)調(diào)API”和“能跑Agent”之間隔了一整個(gè)Runtime這兩年做大模型應(yīng)用的人都有一個(gè)共同感受寫一個(gè)能對(duì)話的Demo半天就夠了但要讓一個(gè)Agent在真實(shí)業(yè)務(wù)里連續(xù)跑上幾十步、調(diào)用七八個(gè)工具、中途還能自我糾錯(cuò)最后穩(wěn)定交付結(jié)果難度直接上兩個(gè)數(shù)量級(jí)。問題不在于模型不夠聰明而在于大多數(shù)團(tuán)隊(duì)把“Agent”當(dāng)成了一個(gè)Prompt問題而它本質(zhì)上是一個(gè)Runtime問題。我見過太多項(xiàng)目卡在同一個(gè)地方單輪對(duì)話效果驚艷一旦進(jìn)入多步任務(wù)就開始“發(fā)瘋”——工具調(diào)用參數(shù)漂移、上下文越滾越長導(dǎo)致關(guān)鍵信息被淹沒、某一步失敗后整個(gè)流程直接崩掉、并發(fā)一上來狀態(tài)就串了。這些都不是靠改幾句提示詞能解決的它們屬于編排層和運(yùn)行時(shí)層的工程問題。所謂Agent Runtime你可以把它理解成Agent的“操作系統(tǒng)”。模型是CPU負(fù)責(zé)推理和決策工具是外設(shè)負(fù)責(zé)和外部世界交互而Runtime負(fù)責(zé)調(diào)度、內(nèi)存管理、錯(cuò)誤恢復(fù)、狀態(tài)持久化、并發(fā)控制。沒有這層你的Agent就是一個(gè)裸奔的while循環(huán)能跑通一次是運(yùn)氣跑通一萬次才是工程。這篇文章我想把Agent編排這件事從底層原理到工程落地完整拆一遍。核心回答三個(gè)問題編排到底在編排什么、一個(gè)真正好用的Runtime需要哪些核心模塊、以及在實(shí)際落地時(shí)那些文檔里不會(huì)寫的坑。適合已經(jīng)寫過基礎(chǔ)Agent、正準(zhǔn)備把它推向生產(chǎn)環(huán)境的開發(fā)者也適合想理解這層抽象到底解決什么問題的人。全文會(huì)給出可參考的模塊劃分、參數(shù)設(shè)計(jì)思路和排查方法你可以直接對(duì)照自己的項(xiàng)目做體檢。2. 編排的本質(zhì)把“不確定性”關(guān)進(jìn)“確定性”的籠子里2.1 編排到底在編排什么很多人對(duì)“Agent編排”的理解停留在“把幾個(gè)步驟串起來”。這個(gè)理解太淺了。編排真正要處理的是模型輸出的不確定性與業(yè)務(wù)流程的確定性要求之間的矛盾。模型每次輸出都是概率采樣同一個(gè)輸入可能給出不同的工具選擇、不同的參數(shù)格式、不同的推理路徑。但業(yè)務(wù)要求是確定的這筆訂單必須查到、這個(gè)文件必須生成、這個(gè)審批必須走完。編排層的全部工作就是在這兩者之間架一座橋——用確定性的控制流去包裹不確定性的模型調(diào)用。具體來說編排要管四件事控制流下一步做什么是繼續(xù)推理、調(diào)用工具、還是終止。這對(duì)應(yīng)循環(huán)、分支、并行、條件跳轉(zhuǎn)。數(shù)據(jù)流上一步的輸出怎么傳給下一步哪些進(jìn)上下文、哪些落盤、哪些丟棄。這對(duì)應(yīng)狀態(tài)管理和上下文工程。錯(cuò)誤流某一步失敗了怎么辦是重試、降級(jí)、換路徑、還是上報(bào)人工。這對(duì)應(yīng)容錯(cuò)策略。資源流Token預(yù)算、時(shí)間預(yù)算、工具調(diào)用次數(shù)、并發(fā)額度怎么分配。這對(duì)應(yīng)成本與限流控制。把這四件事想清楚你的編排設(shè)計(jì)就成型了一半。剩下的才是選框架、寫代碼。2.2 三種主流編排范式的取舍目前工程上主流的編排范式大致三類各有明確的適用邊界選錯(cuò)了會(huì)非常痛苦。范式核心機(jī)制優(yōu)勢(shì)代價(jià)適用場(chǎng)景固定流程編排預(yù)定義DAG/狀態(tài)機(jī)可控、可測(cè)、可審計(jì)靈活性差流程穩(wěn)定的業(yè)務(wù)自動(dòng)化自主循環(huán)編排ReAct式思考-行動(dòng)循環(huán)靈活、能處理開放任務(wù)易跑偏、成本不可控探索型、研究型任務(wù)混合編排外層流程內(nèi)層自主兼顧可控與靈活設(shè)計(jì)復(fù)雜度高生產(chǎn)級(jí)Agent系統(tǒng)我個(gè)人的經(jīng)驗(yàn)是生產(chǎn)環(huán)境里純自主循環(huán)幾乎不可用。原因很簡單你沒法給一個(gè)完全自主的循環(huán)做SLA承諾。它可能三步搞定也可能三十步還在繞圈成本和延遲都不可預(yù)測(cè)。真正能上線的基本都是混合編排——外層用狀態(tài)機(jī)框住關(guān)鍵節(jié)點(diǎn)內(nèi)層在單個(gè)節(jié)點(diǎn)里允許模型自主決策。舉個(gè)具體例子。一個(gè)“合同審核Agent”外層流程是確定的讀取合同→提取條款→比對(duì)規(guī)則庫→生成風(fēng)險(xiǎn)報(bào)告→人工復(fù)核。這五步用狀態(tài)機(jī)固定死。但“提取條款”這一步內(nèi)部模型可以自主決定先看哪部分、要不要調(diào)用OCR、要不要二次確認(rèn)。這樣既保證了流程可控又保留了處理復(fù)雜文檔的靈活性。2.3 狀態(tài)機(jī)為什么比“鏈?zhǔn)秸{(diào)用”更適合生產(chǎn)早期大家喜歡用LangChain那種鏈?zhǔn)紺hain結(jié)構(gòu)A的輸出喂給BB的輸出喂給C。簡單任務(wù)沒問題但一旦涉及分支和回退就露餡了。鏈?zhǔn)浇Y(jié)構(gòu)的致命傷是沒有全局狀態(tài)。每一步只能看到上一步的輸出看不到整個(gè)任務(wù)的進(jìn)展。當(dāng)Agent需要“回到第三步重新做”時(shí)鏈?zhǔn)浇Y(jié)構(gòu)根本表達(dá)不了這個(gè)語義。而狀態(tài)機(jī)天然支持當(dāng)前處于哪個(gè)狀態(tài)、可以從哪些狀態(tài)跳轉(zhuǎn)過來、失敗后回到哪個(gè)檢查點(diǎn)。更關(guān)鍵的是狀態(tài)機(jī)讓可觀測(cè)性成為可能。你可以清楚地知道Agent現(xiàn)在卡在哪個(gè)狀態(tài)、這個(gè)狀態(tài)已經(jīng)停留了多久、歷史上走過哪些路徑。這些信息在排查問題時(shí)價(jià)值巨大。鏈?zhǔn)浇Y(jié)構(gòu)下你只能看到一串日志根本不知道整體進(jìn)度。所以我的建議很直接只要你的Agent任務(wù)超過三步且可能失敗重試就上狀態(tài)機(jī)。別圖省事用鏈?zhǔn)胶竺嬷貥?gòu)的成本遠(yuǎn)高于一開始就設(shè)計(jì)好。3. Runtime的六大核心模塊拆解一個(gè)能扛住生產(chǎn)流量的Agent Runtime我傾向于拆成六個(gè)模塊。這不是標(biāo)準(zhǔn)答案但經(jīng)過幾個(gè)項(xiàng)目驗(yàn)證這個(gè)劃分的邊界比較清晰模塊間耦合低便于獨(dú)立演進(jìn)。3.1 調(diào)度器Agent的大腦皮層調(diào)度器負(fù)責(zé)決定“下一步執(zhí)行什么”。它接收當(dāng)前狀態(tài)和模型輸出輸出下一個(gè)動(dòng)作指令。聽起來簡單但這里藏著幾個(gè)關(guān)鍵設(shè)計(jì)決策。第一個(gè)決策是調(diào)度邏輯放模型里還是放代碼里。放模型里就是讓模型輸出“下一步調(diào)用XX工具”放代碼里就是用規(guī)則判斷。我的做法是關(guān)鍵路徑用代碼邊緣路徑用模型。比如“必須調(diào)用支付接口”這種硬性步驟用代碼強(qiáng)制而“要不要先查一下用戶歷史”這種軟性判斷交給模型。第二個(gè)決策是單步還是批量。有些Runtime允許模型一次輸出多個(gè)動(dòng)作并行執(zhí)行這能提速但會(huì)帶來狀態(tài)沖突。我的經(jīng)驗(yàn)是只讀操作可以并行寫操作必須串行。查詢類工具并行調(diào)用沒問題但涉及狀態(tài)變更的必須排隊(duì)否則會(huì)出現(xiàn)競(jìng)態(tài)。調(diào)度器還需要一個(gè)步數(shù)上限保護(hù)。我一般設(shè)成任務(wù)預(yù)期步數(shù)的3倍。比如一個(gè)任務(wù)正常5步完成上限設(shè)15步。超過就強(qiáng)制終止并上報(bào)避免Agent陷入死循環(huán)燒錢。3.2 上下文管理器最容易被低估的模塊如果說調(diào)度器是大腦上下文管理器就是記憶系統(tǒng)而且是最容易出問題的那個(gè)。我見過太多Agent跑著跑著就“失憶”或者“精神分裂”根子都在這里。上下文管理的核心矛盾是模型需要足夠的信息做決策但上下文窗口有限塞太多反而降低效果。這不是簡單的截?cái)鄦栴}而是信息優(yōu)先級(jí)排序問題。我的做法是分層管理系統(tǒng)層角色定義、核心規(guī)則、工具清單。這部分永遠(yuǎn)保留不參與裁剪。任務(wù)層當(dāng)前任務(wù)目標(biāo)、關(guān)鍵約束、已完成步驟摘要。用滾動(dòng)摘要維護(hù)保留最近N步的詳細(xì)內(nèi)容更早的壓縮成一句話。臨時(shí)層工具返回的原始數(shù)據(jù)、中間結(jié)果。這部分最容易膨脹我的策略是用完即棄只保留結(jié)論。比如查數(shù)據(jù)庫返回100行上下文里只留“查到3條符合條件的記錄ID分別是...”原始數(shù)據(jù)落盤備查。這里有個(gè)反直覺的經(jīng)驗(yàn)上下文不是越長越好。實(shí)測(cè)下來當(dāng)上下文超過窗口的60%時(shí)模型的指令遵循能力會(huì)明顯下降容易忽略系統(tǒng)提示里的關(guān)鍵約束。所以我會(huì)主動(dòng)控制上下文水位寧可多輪摘要也不讓窗口塞滿。3.3 工具網(wǎng)關(guān)Agent的手腳和安全閥工具調(diào)用是Agent和外部世界交互的唯一通道也是風(fēng)險(xiǎn)最集中的地方。工具網(wǎng)關(guān)要解決三個(gè)問題怎么調(diào)、調(diào)得對(duì)不對(duì)、調(diào)出問題怎么辦。“怎么調(diào)”是協(xié)議問題?,F(xiàn)在主流是用JSON Schema描述工具模型輸出結(jié)構(gòu)化參數(shù)。但這里有個(gè)坑模型輸出的參數(shù)經(jīng)常不符合Schema比如該傳數(shù)字傳了字符串、該傳數(shù)組傳了單個(gè)值。網(wǎng)關(guān)必須做參數(shù)校驗(yàn)和類型轉(zhuǎn)換不能直接透?jìng)鹘o下游?!罢{(diào)得對(duì)不對(duì)”是權(quán)限問題。不是所有工具在任何狀態(tài)下都能調(diào)。比如“刪除數(shù)據(jù)”這種危險(xiǎn)操作必須要求前置狀態(tài)是“已確認(rèn)”。網(wǎng)關(guān)要維護(hù)一張狀態(tài)-工具權(quán)限矩陣在調(diào)用前做檢查?!罢{(diào)出問題怎么辦”是容錯(cuò)問題。工具調(diào)用失敗分三類可重試的網(wǎng)絡(luò)超時(shí)、不可重試的參數(shù)錯(cuò)誤、需要降級(jí)的依賴服務(wù)掛了。網(wǎng)關(guān)要根據(jù)錯(cuò)誤類型走不同策略。我的默認(rèn)配置是網(wǎng)絡(luò)類錯(cuò)誤重試2次間隔指數(shù)退避參數(shù)類錯(cuò)誤直接把錯(cuò)誤信息回灌給模型讓它修正依賴類錯(cuò)誤觸發(fā)降級(jí)路徑。提示工具網(wǎng)關(guān)一定要做調(diào)用審計(jì)。每次調(diào)用記錄誰調(diào)的、什么參數(shù)、返回什么、耗時(shí)多少。出問題時(shí)這是唯一的真相來源。3.4 記憶系統(tǒng)短期、長期與工作記憶記憶系統(tǒng)經(jīng)常和上下文管理器混為一談但它們是兩個(gè)層次的東西。上下文管理器管的是“當(dāng)前這次推理能看到什么”記憶系統(tǒng)管的是“跨任務(wù)、跨會(huì)話能記住什么”。我把它分成三類工作記憶當(dāng)前任務(wù)內(nèi)的臨時(shí)狀態(tài)任務(wù)結(jié)束即銷毀。存在內(nèi)存里追求快。短期記憶最近幾次會(huì)話的摘要用于保持對(duì)話連貫。存Redis之類的高速存儲(chǔ)帶TTL。長期記憶用戶偏好、歷史決策、領(lǐng)域知識(shí)。存向量庫或關(guān)系庫需要檢索。這里的關(guān)鍵設(shè)計(jì)是寫入策略。不是所有信息都值得記。我的原則是只記結(jié)論和偏好不記過程。比如“用戶偏好簡潔回復(fù)”值得記“用戶第三次問天氣”不值得記。寫入前讓模型做一次“是否值得記憶”的判斷能大幅降低記憶庫的噪音。檢索策略同樣重要。長期記憶檢索不能只靠向量相似度還要結(jié)合時(shí)間衰減和重要性權(quán)重。一個(gè)三個(gè)月前的偏好權(quán)重應(yīng)該低于昨天的。我一般用score 相似度 * 0.7 時(shí)間衰減 * 0.2 重要性 * 0.1這樣的加權(quán)公式具體系數(shù)根據(jù)業(yè)務(wù)調(diào)。3.5 可觀測(cè)性沒有它等于閉眼開車Agent的可觀測(cè)性和傳統(tǒng)服務(wù)完全不同。傳統(tǒng)服務(wù)你看QPS、延遲、錯(cuò)誤率就夠了Agent你得看推理鏈路每一步的輸入輸出、工具調(diào)用序列、Token消耗、決策依據(jù)。我要求所有Agent項(xiàng)目必須埋三類數(shù)據(jù)Trace完整的執(zhí)行鏈路每個(gè)span對(duì)應(yīng)一個(gè)步驟包含輸入輸出和耗時(shí)。這是排查問題的骨架。Metric聚合指標(biāo)比如平均步數(shù)、工具調(diào)用成功率、Token消耗分布、任務(wù)完成率。這是監(jiān)控告警的依據(jù)。Log詳細(xì)日志包括模型的原始輸出、工具的原始返回。這是深度排查的素材。特別強(qiáng)調(diào)一點(diǎn)模型的原始輸出一定要存。很多人只存解析后的結(jié)構(gòu)化結(jié)果出問題時(shí)根本不知道模型到底說了什么。我吃過這個(gè)虧后來強(qiáng)制要求原始輸出落盤保留至少7天。3.6 安全與護(hù)欄最后一道防線Agent能自主調(diào)用工具就意味著它能自主造成破壞。護(hù)欄模塊負(fù)責(zé)在關(guān)鍵時(shí)刻踩剎車。護(hù)欄分三層輸入護(hù)欄用戶輸入進(jìn)來先過一遍攔截明顯惡意的指令。過程護(hù)欄每一步執(zhí)行前檢查比如“這個(gè)工具在當(dāng)前狀態(tài)允許調(diào)用嗎”“參數(shù)在合理范圍內(nèi)嗎”。輸出護(hù)欄結(jié)果返回給用戶前檢查比如“有沒有泄露敏感信息”“格式是否符合要求”。過程護(hù)欄里最實(shí)用的是預(yù)算護(hù)欄。給每個(gè)任務(wù)設(shè)Token預(yù)算、時(shí)間預(yù)算、工具調(diào)用次數(shù)預(yù)算任一超限就終止。這個(gè)簡單機(jī)制能攔住絕大多數(shù)失控的Agent。我一般設(shè)Token預(yù)算按任務(wù)復(fù)雜度給時(shí)間預(yù)算5分鐘工具調(diào)用上限20次。4. 從零搭一個(gè)最小可用Runtime的實(shí)操路徑理論講完了這部分講怎么落地。我不會(huì)給一個(gè)完整的代碼倉庫而是給出關(guān)鍵模塊的實(shí)現(xiàn)思路和參數(shù)設(shè)計(jì)你可以對(duì)照自己的技術(shù)棧實(shí)現(xiàn)。4.1 狀態(tài)定義與持久化設(shè)計(jì)第一步是定義狀態(tài)結(jié)構(gòu)。一個(gè)Agent任務(wù)的狀態(tài)至少包含class AgentState: task_id: str status: str # pending/running/waiting_human/completed/failed current_step: str # 當(dāng)前狀態(tài)機(jī)節(jié)點(diǎn) step_history: list # 走過的路徑 context: dict # 上下文數(shù)據(jù) budget: dict # 剩余預(yù)算 created_at: float updated_at: float持久化選型上我的建議是開發(fā)期用SQLite生產(chǎn)期用PostgreSQL。別一上來就上復(fù)雜的分布式存儲(chǔ)狀態(tài)數(shù)據(jù)量沒那么大關(guān)系庫完全夠用而且事務(wù)支持好狀態(tài)更新不會(huì)丟。關(guān)鍵設(shè)計(jì)是狀態(tài)快照。每完成一個(gè)步驟就存一次快照這樣失敗后可以從最近的快照恢復(fù)不用從頭跑??煺疹l率別太高否則寫放大嚴(yán)重也別太低否則恢復(fù)代價(jià)大。我的經(jīng)驗(yàn)是每個(gè)狀態(tài)轉(zhuǎn)換存一次這個(gè)粒度剛好。4.2 調(diào)度循環(huán)的骨架實(shí)現(xiàn)調(diào)度循環(huán)是整個(gè)Runtime的心臟骨架大概長這樣def run_agent(state): while state.status running: if check_budget(state) is False: state.status failed break # 1. 構(gòu)建上下文 context build_context(state) # 2. 調(diào)用模型 response call_model(context) # 3. 解析動(dòng)作 action parse_action(response) # 4. 護(hù)欄檢查 if not guard_check(action, state): state.context[last_error] guard rejected continue # 5. 執(zhí)行動(dòng)作 result execute_action(action, state) # 6. 更新狀態(tài) state update_state(state, action, result) save_snapshot(state) return state這個(gè)骨架看著簡單但每一步都有講究。比如第2步調(diào)用模型要處理超時(shí)和重試第3步解析動(dòng)作要處理格式錯(cuò)誤第5步執(zhí)行動(dòng)作要處理工具異常。這些細(xì)節(jié)決定了Runtime的健壯性。4.3 工具注冊(cè)與參數(shù)校驗(yàn)的落地工具注冊(cè)我推薦用裝飾器模式把Schema定義和實(shí)現(xiàn)綁在一起tool( namequery_order, description根據(jù)訂單號(hào)查詢訂單狀態(tài), params{ order_id: {type: string, required: True}, fields: {type: array, items: string, default: []} } ) def query_order(order_id, fieldsNone): # 實(shí)現(xiàn) pass參數(shù)校驗(yàn)用JSON Schema庫自動(dòng)做別手寫if-else。校驗(yàn)失敗時(shí)把具體的錯(cuò)誤信息哪個(gè)字段、期望什么類型、實(shí)際什么類型回灌給模型讓它重新生成。實(shí)測(cè)這樣比直接報(bào)錯(cuò)讓模型重試的成功率高很多。這里有個(gè)細(xì)節(jié)參數(shù)默認(rèn)值要顯式聲明。模型經(jīng)常漏傳可選參數(shù)如果你在Schema里寫了default校驗(yàn)層能自動(dòng)補(bǔ)上省一輪交互。4.4 上下文壓縮的具體策略上下文壓縮是門手藝。我的策略是分級(jí)壓縮最近3步完整保留包括工具返回的原始數(shù)據(jù)。4到10步保留動(dòng)作和結(jié)論工具原始數(shù)據(jù)替換成摘要。10步以上只保留一句話總結(jié)比如“已完成訂單查詢和庫存校驗(yàn)”。壓縮用模型做但要用一個(gè)專門的壓縮提示詞明確要求“保留關(guān)鍵實(shí)體和數(shù)值丟棄過程性描述”。壓縮后的Token數(shù)控制在原來的20%以內(nèi)。注意壓縮會(huì)丟信息所以關(guān)鍵數(shù)值必須單獨(dú)存一份結(jié)構(gòu)化數(shù)據(jù)不能只存在上下文里。比如訂單號(hào)、金額這些壓縮時(shí)可能被模型“概括”掉但后續(xù)步驟還要用。我的做法是維護(hù)一個(gè)key_facts字典專門存這類硬信息不參與壓縮。4.5 錯(cuò)誤恢復(fù)與重試的工程細(xì)節(jié)錯(cuò)誤恢復(fù)的核心是分類處理。我一般把錯(cuò)誤分成四類錯(cuò)誤類型典型場(chǎng)景處理策略瞬時(shí)錯(cuò)誤網(wǎng)絡(luò)抖動(dòng)、限流指數(shù)退避重試最多3次參數(shù)錯(cuò)誤Schema校驗(yàn)失敗錯(cuò)誤回灌模型重新生成邏輯錯(cuò)誤工具返回業(yè)務(wù)異常觸發(fā)降級(jí)路徑或上報(bào)系統(tǒng)錯(cuò)誤依賴服務(wù)宕機(jī)熔斷轉(zhuǎn)人工重試要設(shè)總預(yù)算不能無限重試。我的配置是單個(gè)工具調(diào)用最多重試3次整個(gè)任務(wù)的重試總次數(shù)不超過10次。超過就判定任務(wù)失敗。還有個(gè)容易忽略的點(diǎn)重試要冪等。如果工具調(diào)用有副作用比如發(fā)消息、扣款重試前必須確認(rèn)上一次是否真的失敗了。我的做法是給每個(gè)寫操作帶一個(gè)冪等鍵下游根據(jù)冪等鍵去重。5. 生產(chǎn)環(huán)境踩過的坑與排查手冊(cè)這部分是全文最值錢的部分。下面這些坑每一個(gè)都是我或身邊同行真實(shí)踩過的文檔里基本不會(huì)寫。5.1 上下文污染Agent為什么會(huì)“精神分裂”現(xiàn)象Agent跑到第8步突然開始重復(fù)第3步的操作或者引用了根本不存在的“之前查到的數(shù)據(jù)”。根因上下文里混入了過期的中間結(jié)果。比如第3步查到的庫存是100第6步庫存已經(jīng)變成50了但上下文里還留著100模型基于舊數(shù)據(jù)做決策。排查方法把每一步的上下文快照打出來對(duì)比模型實(shí)際看到的內(nèi)容和它引用的內(nèi)容。如果發(fā)現(xiàn)它引用了不該存在的數(shù)據(jù)就是污染。解決給上下文數(shù)據(jù)打時(shí)間戳和版本號(hào)。同一類數(shù)據(jù)只保留最新版本舊版本主動(dòng)清理。工具返回?cái)?shù)據(jù)時(shí)帶上as_of時(shí)間模型決策時(shí)優(yōu)先用最新的。5.2 工具幻覺模型編造不存在的工具和參數(shù)現(xiàn)象模型調(diào)用了一個(gè)你沒注冊(cè)的工具或者給工具傳了一個(gè)Schema里沒有的參數(shù)。根因工具描述寫得不夠清晰或者工具數(shù)量太多導(dǎo)致模型混淆。排查看Trace里模型輸出的原始動(dòng)作對(duì)比注冊(cè)的工具清單。解決三個(gè)措施。第一工具描述要寫清楚“什么時(shí)候用”和“什么時(shí)候不用”光寫功能不夠。第二工具數(shù)量控制在15個(gè)以內(nèi)超過就分組用兩級(jí)選擇。第三在系統(tǒng)提示里明確列出可用工具名并強(qiáng)調(diào)“只能調(diào)用列表中的工具”。5.3 死循環(huán)Agent在兩步之間反復(fù)橫跳現(xiàn)象Agent在“查詢→發(fā)現(xiàn)數(shù)據(jù)不對(duì)→重新查詢”之間無限循環(huán)。根因沒有進(jìn)展檢測(cè)機(jī)制。Agent不知道自己在原地打轉(zhuǎn)。排查看step_history如果發(fā)現(xiàn)最近5步的動(dòng)作模式高度重復(fù)就是死循環(huán)。解決加一個(gè)狀態(tài)指紋機(jī)制。每步計(jì)算一個(gè)指紋動(dòng)作類型關(guān)鍵參數(shù)如果連續(xù)3步指紋相同強(qiáng)制中斷并上報(bào)。另外給每個(gè)狀態(tài)設(shè)最大停留次數(shù)比如“查詢狀態(tài)”最多進(jìn)入3次超過就換路徑。5.4 并發(fā)下的狀態(tài)串?dāng)_現(xiàn)象兩個(gè)用戶同時(shí)用AgentA的結(jié)果串到了B的會(huì)話里。根因狀態(tài)沒有按會(huì)話隔離或者用了全局變量。排查檢查狀態(tài)存儲(chǔ)的key是否包含會(huì)話ID檢查有沒有共享的可變對(duì)象。解決所有狀態(tài)必須帶會(huì)話ID作為命名空間。存儲(chǔ)key用{session_id}:{task_id}格式。代碼里禁止使用模塊級(jí)可變變量所有狀態(tài)通過參數(shù)傳遞。這個(gè)坑一旦踩了排查起來極其痛苦因?yàn)樗歉怕市缘谋镜販y(cè)試根本復(fù)現(xiàn)不了。5.5 Token消耗失控的排查思路現(xiàn)象賬單突然暴漲或者單個(gè)任務(wù)消耗了幾十萬Token。排查順序先看單步Token分布找出消耗最大的步驟。通常是工具返回?cái)?shù)據(jù)太長或者上下文沒壓縮。再看步數(shù)分布找出異常長的任務(wù)。通常是死循環(huán)或反復(fù)重試。最后看并發(fā)數(shù)確認(rèn)是不是流量突增。解決設(shè)硬預(yù)算單任務(wù)Token上限、單用戶日Token上限。超限直接拒絕別心軟。另外工具返回?cái)?shù)據(jù)在進(jìn)上下文前先做一次長度截?cái)喑^2000字符的只保留摘要。5.6 常見問題速查表癥狀可能原因快速驗(yàn)證處理重復(fù)執(zhí)行同一步狀態(tài)未更新/死循環(huán)看step_history加狀態(tài)指紋檢測(cè)引用錯(cuò)誤數(shù)據(jù)上下文污染對(duì)比上下文快照數(shù)據(jù)加版本號(hào)調(diào)用不存在的工具工具描述不清看原始輸出精簡工具列表任務(wù)卡住不動(dòng)工具超時(shí)無處理看工具調(diào)用耗時(shí)加超時(shí)和重試結(jié)果串會(huì)話狀態(tài)未隔離檢查存儲(chǔ)key加會(huì)話命名空間Token暴漲上下文膨脹看單步Token加壓縮和預(yù)算6. 編排框架選型自研還是用現(xiàn)成的6.1 現(xiàn)成框架的能力邊界市面上主流的Agent框架能力大致分三檔基礎(chǔ)檔提供工具調(diào)用和簡單循環(huán)適合做Demo。這類框架的問題是缺乏狀態(tài)管理和錯(cuò)誤恢復(fù)上生產(chǎn)要自己補(bǔ)很多。中間檔提供狀態(tài)機(jī)、持久化、可觀測(cè)性能支撐中等復(fù)雜度應(yīng)用。選這類要看它的抽象是否合理會(huì)不會(huì)限制你的定制。重型檔提供完整的編排、部署、監(jiān)控能力適合大團(tuán)隊(duì)。代價(jià)是學(xué)習(xí)曲線陡且容易被框架綁定。我的判斷標(biāo)準(zhǔn)很簡單如果你的Agent邏輯能用狀態(tài)機(jī)清晰表達(dá)且不需要復(fù)雜的分布式協(xié)調(diào)中間檔框架夠用。如果邏輯高度動(dòng)態(tài)、需要運(yùn)行時(shí)生成流程那可能得自研調(diào)度層。6.2 什么情況下必須自研三種情況我建議自研第一業(yè)務(wù)邏輯高度定制。比如你的Agent需要和內(nèi)部十幾個(gè)系統(tǒng)深度集成每個(gè)系統(tǒng)有特殊的鑒權(quán)和數(shù)據(jù)格式用通用框架反而處處受限。第二性能要求極高。通用框架為了兼容性做了很多抽象這些抽象在高并發(fā)下會(huì)成為瓶頸。如果你的Agent要支撐每秒上千次調(diào)用自研精簡版更合適。第三需要深度可觀測(cè)。通用框架的埋點(diǎn)粒度可能不夠你需要精確到每次模型調(diào)用的Token分布、每個(gè)工具的P99延遲自研能完全掌控。自研不等于從零寫。我的做法是核心調(diào)度自研周邊能力用開源。比如狀態(tài)存儲(chǔ)用現(xiàn)成的數(shù)據(jù)庫可觀測(cè)性用OpenTelemetry只把調(diào)度循環(huán)和上下文管理自己寫。這樣既保證核心可控又不重復(fù)造輪子。6.3 混合架構(gòu)的實(shí)踐建議大多數(shù)團(tuán)隊(duì)的最優(yōu)解是混合用框架搭骨架用自研補(bǔ)關(guān)鍵模塊。具體來說框架負(fù)責(zé)工具注冊(cè)、基礎(chǔ)循環(huán)、日志輸出這些標(biāo)準(zhǔn)化程度高的部分。自研負(fù)責(zé)上下文壓縮策略、錯(cuò)誤恢復(fù)邏輯、預(yù)算控制這些和業(yè)務(wù)強(qiáng)相關(guān)的部分。這樣做的關(guān)鍵是定義好接口邊界??蚣芎妥匝心K之間通過明確的數(shù)據(jù)結(jié)構(gòu)通信比如狀態(tài)對(duì)象、動(dòng)作對(duì)象、結(jié)果對(duì)象。接口定好了兩邊可以獨(dú)立演進(jìn)將來換框架也不用重寫自研部分。7. 性能與成本優(yōu)化的實(shí)戰(zhàn)參數(shù)7.1 延遲優(yōu)化的三個(gè)杠桿Agent的延遲主要來自三塊模型推理、工具調(diào)用、狀態(tài)讀寫。優(yōu)化要分清楚瓶頸在哪。模型推理通常是大頭。優(yōu)化手段用更小的模型做簡單決策只在關(guān)鍵步驟用大模型開啟流式輸出讓用戶先看到部分結(jié)果緩存常見決策的結(jié)果。工具調(diào)用的優(yōu)化在于并行。只讀工具能并行就并行我實(shí)測(cè)過三個(gè)查詢并行能把這一步的延遲從3秒降到1.2秒。但要注意并行度別太高否則下游服務(wù)扛不住。狀態(tài)讀寫容易被忽略。如果每步都同步寫數(shù)據(jù)庫延遲會(huì)累積。我的做法是異步寫定期快照關(guān)鍵狀態(tài)同步寫非關(guān)鍵狀態(tài)異步寫。7.2 Token成本控制的硬手段Token成本是Agent規(guī)模化最大的攔路虎。幾個(gè)硬手段上下文壓縮前面講過能省30%到50%的輸入Token。結(jié)果緩存相同或相似的查詢直接返回緩存省掉模型調(diào)用。模型分級(jí)簡單任務(wù)用小模型復(fù)雜任務(wù)才用大模型。我一般設(shè)一個(gè)路由規(guī)則按任務(wù)類型分流。輸出限制給模型設(shè)max_tokens別讓它無限輸出。工具調(diào)用的輸出尤其要限制很多模型喜歡在調(diào)用工具前長篇大論。實(shí)測(cè)下來這幾招組合能把單任務(wù)成本壓到原來的三分之一左右。7.3 并發(fā)與限流的配置參考并發(fā)配置沒有萬能值但有個(gè)估算方法先測(cè)單任務(wù)的P95延遲再用目標(biāo)QPS反推需要的并發(fā)數(shù)。比如單任務(wù)P95是10秒目標(biāo)QPS是5那至少需要50個(gè)并發(fā)槽位。限流要分層用戶級(jí)限制單用戶并發(fā)防止一個(gè)用戶占滿資源任務(wù)級(jí)限制單任務(wù)步數(shù)防止死循環(huán)系統(tǒng)級(jí)限制總并發(fā)保護(hù)下游。我的默認(rèn)配置供參考單用戶并發(fā)3單任務(wù)最大步數(shù)20系統(tǒng)總并發(fā)按下游承載能力的70%設(shè)置。留30%余量應(yīng)對(duì)突發(fā)。8. 寫在最后一些不那么技術(shù)但很重要的體會(huì)做了幾個(gè)Agent項(xiàng)目之后我最大的體會(huì)是Agent的工程質(zhì)量80%取決于你對(duì)失敗的處理而不是對(duì)成功的處理。Demo演示的是成功路徑生產(chǎn)考驗(yàn)的是失敗路徑。你的重試策略、降級(jí)方案、人工兜底這些才是決定Agent能不能上線的關(guān)鍵。另一個(gè)體會(huì)是別追求全自主。很多團(tuán)隊(duì)一開始都想做一個(gè)“什么都能干”的通用Agent結(jié)果做出來什么都不精。反而是那些把邊界劃得很清楚、在特定場(chǎng)景里做深做透的Agent真正產(chǎn)生了價(jià)值。編排的意義不是讓Agent無所不能而是讓它在該自主的地方自主該受控的地方受控。最后一個(gè)實(shí)操建議從最簡單的狀態(tài)機(jī)開始別一上來就搞復(fù)雜架構(gòu)。我見過太多項(xiàng)目在架構(gòu)設(shè)計(jì)上花了兩個(gè)月結(jié)果核心業(yè)務(wù)邏輯還沒跑通。先用最樸素的方式把主流程跑通遇到具體問題再針對(duì)性加模塊。Runtime是長出來的不是設(shè)計(jì)出來的。