計:從模糊意圖到可執(zhí)行動作的工程實踐)
我印象很深的一次經(jīng)歷團(tuán)隊花了兩周時間把Agent助手從能聊天調(diào)到了能干活結(jié)果一上線就翻車——用戶說幫我把上周的銷售數(shù)據(jù)整理成周報系統(tǒng)愣是把整理數(shù)據(jù)理解成了生成一份PPT模板用戶當(dāng)場就炸了。問題不在模型在技能。我后來把整套agent-skills體系推翻重寫了一遍穩(wěn)定性和用戶滿意度才真正拉上來。過去一年里AI Agent從多輪對話進(jìn)化到自主完成任務(wù)核心分水嶺就是技能體系的成熟度。所謂技能不是模型本身的能力而是我們給Agent裝配的一組可復(fù)用、可編排、可評估的動作單元——查數(shù)據(jù)庫、調(diào)API、發(fā)消息、生成文檔、操作瀏覽器每一樣都是一項技能。會不會設(shè)計技能直接決定了Agent的上限也決定了它到底是個靠譜的同事還是個嘴上王者。這篇文章面向兩類讀者一是正在做Agent應(yīng)用、但被時好時壞折磨到失眠的開發(fā)者二是想從原理上理解為什么有的Agent真的好用、有的Agent像個智障的產(chǎn)品負(fù)責(zé)人和技術(shù)管理者。我會把技能的定義、注冊、調(diào)度、編排、評估整個鏈路拆開講穿插我這幾個月真實踩過的坑和最后沉淀下來的方案。先聲明一點(diǎn)下面所有代碼和字段設(shè)計都是我在生產(chǎn)環(huán)境驗證過的不是Demo級的玩具。1. Agent技能體系的本質(zhì)把模糊意圖變成可執(zhí)行動作1.1 一張會說話的能力清單勝過十頁提示詞很多團(tuán)隊做Agent第一步就是把系統(tǒng)提示詞越寫越長——你是一個智能助手你可以查詢數(shù)據(jù)庫數(shù)據(jù)庫中有用戶表、訂單表……查的時候注意……如果用戶問天氣你該怎么辦……。結(jié)果呢提示詞越長模型越記不住越容易在關(guān)鍵節(jié)點(diǎn)上自由發(fā)揮。技能體系的核心思路完全不同不要教模型怎么做只給它一張能力清單讓它知道有什么可以用、什么時候用、用完能得到什么。剩下的推理過程交給模型但動作邊界由技能體系卡死。打個比方提示詞像一份崗位職責(zé)說明書寫得再詳細(xì)員工也會有自己的理解偏差而技能體系像一套標(biāo)準(zhǔn)化的工具接口——螺絲刀就是擰螺絲的你不用擔(dān)心它被拿去當(dāng)錘子用。我最后的實際感受是把原來那份2000多字的系統(tǒng)提示詞壓縮成了不到300字的助手人設(shè)策略說明再把所有工具能力搬進(jìn)技能注冊表之后任務(wù)的完成率反而提升了三成。原因很簡單模型不需要在大量互相矛盾的指令里猜用戶意圖了它只需要做選擇。1.2 技能是LLM與工具之間的契約不是一段代碼我在團(tuán)隊內(nèi)部經(jīng)常強(qiáng)調(diào)一個觀點(diǎn)技能不是函數(shù)本身而是LLM與真實世界之間的一份契約。函數(shù)是給程序員看的技能是給模型看的。同一個工具如果它的技能描述寫得不好模型就不知道怎么調(diào)用、什么時候調(diào)用、傳什么參數(shù)。舉個例子你有一個get_weather(city, date)函數(shù)。如果技能描述只寫獲取天氣模型面對明天去上海開會有必要帶傘嗎這種問題時可能根本不會聯(lián)想到這個技能。但如果技能描述寫成根據(jù)城市和日期查詢天氣預(yù)報適用于出行規(guī)劃、活動安排、穿衣建議等場景返回包含降水概率、溫度、風(fēng)速的天氣信息模型就會非常明確地在合適時機(jī)調(diào)用它。所以我把技能定義拆成四個層次來看意圖層這個技能解決什么問題什么場景下該被觸發(fā)接口層參數(shù)是什么、類型是什么、返回值長什么樣約束層什么時候不該用、有什么副作用、需要什么權(quán)限示例層給一兩個典型的調(diào)用示例幫模型建立這個技能長這樣的直觀認(rèn)知一個成熟技能庫的維護(hù)工作大部分時間不是在寫業(yè)務(wù)代碼而是在打磨這四層定義。代碼寫錯了能編譯報錯技能定義寫錯了只會讓Agent在用戶面前抽風(fēng)。2. 技能清單設(shè)計一次定義處處調(diào)用的元數(shù)據(jù)規(guī)范2.1 技能定義的最小字段集照著抄就行我先給出一份我在生產(chǎn)環(huán)境里沉淀下來的技能定義模板這是經(jīng)歷過多個項目考驗的版本不是網(wǎng)上那種花架子{ name: sales_report_query, version: 2.3.1, description: 按日期范圍、區(qū)域、產(chǎn)品線等條件查詢銷售明細(xì)支持聚合統(tǒng)計。適用于周報月報生成、業(yè)績復(fù)盤、異常波動排查等場景。不適用于預(yù)測未來數(shù)據(jù)。, parameters: { type: object, properties: { start_date: {type: string, format: date, description: 起始日期格式Y(jié)YYY-MM-DD不能晚于end_date}, end_date: {type: string, format: date, description: 結(jié)束日期格式Y(jié)YYY-MM-DD}, region: {type: string, enum: [華東, 華北, 華南, 西南], description: 區(qū)域篩選條件不傳則返回全部區(qū)域}, dimension: {type: string, enum: [daily, weekly, monthly], description: 聚合粒度默認(rèn)daily} }, required: [start_date, end_date] }, returns: { type: array, items: { type: object, properties: { date: {type: string}, region: {type: string}, revenue: {type: number}, order_count: {type: integer} } }, summary: 返回按時間排序的銷售記錄數(shù)組字段包含日期、區(qū)域、營收、訂單數(shù) }, examples: [ { request: 查詢上個月華東區(qū)每天的銷售額, arguments: {start_date: 2025-01-01, end_date: 2025-01-31, region: 華東, dimension: daily} } ], capability: read, timeout_ms: 10000, scopes: [data_analyst, admin] }有幾個字段是一開始容易漏、后來發(fā)現(xiàn)必須加的version技能是會迭代的沒有版本號你就沒法做灰度回滾。我見過太多團(tuán)隊改了技能描述之后效果反而變差結(jié)果連改了什么都不知道。capability這個技能是讀還是寫操作。讀操作可以放心讓Agent自動執(zhí)行寫操作發(fā)消息、改數(shù)據(jù)、下訂單必須走額外的確認(rèn)流程。examples別看它小小一段這是幫助模型理解技能的最有效手段。模型對示例的模仿能力遠(yuǎn)比對抽象描述的理解能力要強(qiáng)。2.2 技能描述的質(zhì)量決定了模型調(diào)用的準(zhǔn)確率這是整個技能體系里性價比最高的優(yōu)化點(diǎn)。我做過一個控制變量實驗同一個技能只改描述文案不碰任何代碼邏輯模型的調(diào)用準(zhǔn)確率從61%漲到了89%。代價只是花了半小時重寫描述。怎么寫好技能描述我總結(jié)了三條硬規(guī)則第一描述里必須包含什么時候該用和什么時候不該用。模型踩坑最多的場景不是不知道該調(diào)哪個工具而是把A工具用在B場景。比如一個生成圖表的技能如果描述里不寫不適用于表格數(shù)據(jù)展示表格請用export_csv技能模型就可能在用戶要數(shù)據(jù)表的時候給畫個柱狀圖。第二用詞要具體避免抽象形容詞。高效查詢是廢話按時間范圍過濾才是有效信息。描述里出現(xiàn)的每一個概念都應(yīng)該是參數(shù)表里出現(xiàn)過的字段形成閉環(huán)。第三用動詞賓語的結(jié)構(gòu)開頭。我對比過很多描述寫法查詢銷售明細(xì)比該技能用于銷售數(shù)據(jù)的查詢和分析工作更容易被模型命中??赡芤驗閯釉~開頭的表達(dá)更接近模型在訓(xùn)練語料里見過的函數(shù)注釋風(fēng)格。2.3 權(quán)限與副作用安全底線必須在技能層卡死很多團(tuán)隊把權(quán)限控制放在對話層讓模型去判斷這個操作能不能做。這是非常危險的——模型天生傾向于滿足用戶請求尤其是當(dāng)用戶用請務(wù)必快點(diǎn)處理這樣帶有催促性的語言時。正確的做法是把權(quán)限下沉到技能層跟業(yè)務(wù)邏輯徹底解耦。在一個Agent技能注冊表里每一項技能都帶有明確的能力標(biāo)簽只讀、可寫、需要二次確認(rèn)、僅限特定角色。調(diào)度器在把技能放行給模型之前先根據(jù)當(dāng)前對話的安全等級做一次過濾。我踩過的一個真實教訓(xùn)早期版本里刪除客戶數(shù)據(jù)這個操作跟查詢客戶數(shù)據(jù)放在同一個技能里由模型自己判斷參數(shù)。結(jié)果測試人員在一次模擬對話里說了句把測試賬號的客戶記錄清理一下我們準(zhǔn)備切換環(huán)境了Agent二話不說就開始刪數(shù)據(jù)了。雖然當(dāng)時連的是測試庫但那次之后我把所有寫操作全部分裂成獨(dú)立技能并且強(qiáng)制要求帶confirm_operation的前置技能。代價是多了一次人機(jī)交互換來的卻是敢把Agent放到生產(chǎn)環(huán)境的底氣。3. 技能調(diào)度實戰(zhàn)上下文感知的選擇邏輯與失敗回退3.1 靜態(tài)清單還是動態(tài)發(fā)現(xiàn)取決于技能庫的規(guī)模技能怎么暴露給模型兩種主流思路一種是靜態(tài)注冊表把全部技能塞進(jìn)上下文另一種是動態(tài)發(fā)現(xiàn)按需檢索之后注入。我兩個方案都試過給你一個清晰的取舍標(biāo)準(zhǔn)維度靜態(tài)注冊表動態(tài)發(fā)現(xiàn)技能數(shù)量50個可接受50個必須用上下文開銷每個技能約200-500 token50個技能輕松吃掉1萬 token只注入Top K個開銷可控調(diào)用延遲低多一次檢索增加約100-300ms實現(xiàn)復(fù)雜度簡單需要額外維護(hù)檢索索引誤召回風(fēng)險無全部可見有召回不準(zhǔn)就調(diào)不到如果你的Agent技能少于30個老老實實用靜態(tài)注冊表就行。上下文窗口現(xiàn)在雖然大了但token就是錢而且技能描述越多模型的選擇注意力越分散。超過50個技能之后我強(qiáng)烈建議上動態(tài)檢索——把技能描述向量化根據(jù)用戶當(dāng)前的消息和已有的對話摘要做相似度搜索取出最相關(guān)的8-10個技能注入上下文。我這邊最終是混合方案高頻核心技能常駐長尾技能進(jìn)向量庫。常駐技能控制在15個以內(nèi)其余全部走檢索。線上效果很穩(wěn)上下文開銷壓縮了一大半。3.2 參數(shù)抽取從自然語言到函數(shù)簽名的翻譯官技能調(diào)用的另一大難題是參數(shù)抽取。用戶說幫我看看上個月華東的銷售怎么樣你得把它翻譯成{start_date: 2025-01-01, end_date: 2025-01-31, region: 華東}。模型干這活沒問題但有幾個隱藏的坑日期理解是重災(zāi)區(qū)。下個月最近三天今年Q3這種相對時間不同模型的理解差異很大甚至同一個模型在不同會話里都會給出不同結(jié)果。我的做法是在參數(shù)描述里強(qiáng)制要求模型輸出具體的日期范圍另外在技能內(nèi)部加一個日期解析器對模型給出的參數(shù)做二次校驗發(fā)現(xiàn)日期異常就主動追問用戶。枚舉值的兜底匹配也非常關(guān)鍵。參數(shù)表里定義了enum: [華東, 華北, 華南, 西南]但用戶可能說江浙滬北方區(qū)域除了北京以外的地區(qū)。如果模型直接把江浙滬塞進(jìn)region字段技能就崩了。我在技能框架里加了同義詞映射表把常見的口語化表達(dá)映射到標(biāo)準(zhǔn)枚舉值上映射不上就觸發(fā)澄清反問絕不猜測。這里要給所有做Agent的同行一個忠告參數(shù)的默認(rèn)值設(shè)計一定要謹(jǐn)慎。一個技能如果所有參數(shù)都可選模型就傾向于少傳參數(shù)最后執(zhí)行出來的結(jié)果和用戶預(yù)期相差十萬八千里。我的原則是核心參數(shù)必須必填拿不到就反問次要參數(shù)設(shè)置一個顯式的默認(rèn)值并且在返回值里標(biāo)注該結(jié)果基于默認(rèn)參數(shù)XX生成讓用戶知道權(quán)重在哪。3.3 失敗回退設(shè)計讓Agent學(xué)會承認(rèn)做不到技能調(diào)用不可能永遠(yuǎn)成功。網(wǎng)絡(luò)超時、參數(shù)校驗失敗、下游依賴返回異常這些在真實環(huán)境里天天發(fā)生。最可怕的不是失敗而是失敗之后Agent開始編造成功。我遇到過最離譜的一次一個查詢訂單狀態(tài)的技能超時了Agent居然跟用戶說您的訂單已發(fā)貨預(yù)計明天到達(dá)。后來一查那段話是模型根據(jù)上下文自己腦補(bǔ)的跟訂單狀態(tài)毫無關(guān)系。這事的根源在于技能框架沒有告訴模型調(diào)用失敗后該怎么辦模型只能用自己最擅長的方式——編——來維持對話的連貫性。我的解決方案是給每個技能配置顯式的失敗處理策略重試策略哪些錯誤碼可以自動重試最多重試幾次重試間隔多少我一般只對超時和5xx做1-2次重試其他錯誤一律不重試避免在錯誤方向上浪費(fèi)時間和token。降級路徑主技能掛了有沒有次選技能頂上比如查詢天氣失敗可以用獲取未來24小時降水預(yù)報頂上但要明確告訴用戶當(dāng)前展示的是預(yù)報數(shù)據(jù)。誠實上報所有技能調(diào)用必須在返回結(jié)構(gòu)里帶上status和error_message字段。模型發(fā)現(xiàn)status: failed時只允許做一件事——如實地告訴用戶這個操作目前沒成功原因是……建議換個方式或稍后再試。我把這個邏輯做成了框架層的硬約束不依賴模型自覺。在代碼里攔截模型生成的結(jié)果一旦檢測到技能調(diào)用失敗后模型還在編造成功就強(qiáng)制改寫回復(fù)內(nèi)容。這是個笨辦法但非常有效生產(chǎn)環(huán)境的幻覺率直接降了一個數(shù)量級。4. 復(fù)合技能編排原子技能如何組裝成高階工作流4.1 單技能能解決80%的簡單請求剩下20%靠編排用戶的真實需求很少是單次技能調(diào)用能搞定的。就拿開頭那個例子來說把上周的銷售數(shù)據(jù)整理成周報一次完整的執(zhí)行鏈路應(yīng)該是查詢銷售明細(xì)query_sales按周維度做聚合統(tǒng)計aggregate_sales識別數(shù)據(jù)波動和異常detect_anomalies生成周報文案generate_report_text排版成文檔create_document每一步都是一個獨(dú)立的原子技能而把它們串起來的邏輯就是編排。這里有個原則性問題編排邏輯應(yīng)該由代碼控制還是讓模型自由發(fā)揮我的答案是業(yè)務(wù)鏈路穩(wěn)定的部分交給代碼編排模型只負(fù)責(zé)鏈路易變的部分。舉個具體的分界線查數(shù)據(jù)—聚合—生成圖表—輸出報告這四步順序是固定的每一步的輸入輸出關(guān)系也是明確的完全可以用代碼寫死成一個復(fù)合技能但用戶中途說把圖表改成餅圖或把上一個季度的數(shù)據(jù)也加上這種動態(tài)需求就必須由模型來判斷該插入哪個步驟。4.2 狀態(tài)管理編排最容易翻車的地方復(fù)合技能編排里翻車率最高的不是步驟選擇而是狀態(tài)管理。一個多步驟任務(wù)執(zhí)行到第三步崩了怎么恢復(fù)Agent記不記得自己已經(jīng)執(zhí)行到哪一步了如果用戶中途改需求之前執(zhí)行的中間結(jié)果怎么處理我現(xiàn)在的做法是在編排器里維護(hù)一個顯式的任務(wù)狀態(tài)對象包含三個部分當(dāng)前步驟、已完成步驟的中間產(chǎn)物、待執(zhí)行步驟的隊列。每一步技能執(zhí)行完畢都把結(jié)果寫入中間產(chǎn)物緩存一旦任務(wù)中斷用戶可以說繼續(xù)Agent從斷點(diǎn)接著干不需要重新跑一遍。還有一個細(xì)節(jié)每一步技能調(diào)用的結(jié)果都附帶一個summary字段比如已成功獲取華東區(qū)1月1日至1月31日的銷售明細(xì)共返回247條記錄總營收1523萬元。這個summary不是給系統(tǒng)看的是給下一步的技能和給用戶看的。它讓整條鏈路里的每個環(huán)節(jié)都能快速理解前一步發(fā)生了什么也讓用戶在中途可以隨時打斷提問——等一下這個數(shù)字包含退款訂單嗎Agent可以基于summary快速回答而不是重新翻原始數(shù)據(jù)。4.3 人機(jī)協(xié)同的檢查點(diǎn)設(shè)計我給所有涉及寫操作的復(fù)合技能加入了一個通用機(jī)制關(guān)鍵動作前置確認(rèn)。流程執(zhí)行到某個節(jié)點(diǎn)比如刪除客戶記錄發(fā)送全員郵件提交訂單系統(tǒng)會自動插入一個人工確認(rèn)步驟把即將執(zhí)行的操作、涉及的參數(shù)、可能的影響范圍列給用戶確認(rèn)。這一步既是為了安全也是為了用戶體驗。用戶對Agent的信任是逐漸建立的前期給足確認(rèn)環(huán)節(jié)反而會讓他們更放心地放手讓Agent去干后面的活。等線上跑了一段時間、確認(rèn)率穩(wěn)定在90%以上之后再考慮對低風(fēng)險操作放開確認(rèn)。5. 技能評估與迭代沒有測試集支撐的技能庫是空中樓閣5.1 給技能建一個體檢中心很多團(tuán)隊做Agent上線之后的效果全靠用戶反饋感覺還行偶爾抽風(fēng)。這種狀態(tài)沒法持續(xù)優(yōu)化。如果把技能體系當(dāng)成一個軟件系統(tǒng)來對待那它必須有自己的單元測試和回歸測試。我給技能庫建了一套評估基線核心是四類指標(biāo)觸發(fā)準(zhǔn)確率該調(diào)的技能調(diào)對了沒有不該調(diào)的時候有沒有亂調(diào)參數(shù)正確率調(diào)用時機(jī)對了參數(shù)填得對不對日期、姓名、編號這些關(guān)鍵參數(shù)有沒有傳錯結(jié)果滿足率技能執(zhí)行成功了返回的結(jié)果是不是用戶真正想要的失敗恢復(fù)率失敗之后是否正確上報和降級還是開始編造成功每一類指標(biāo)都對應(yīng)一批預(yù)置的測試用例至少幾百條覆蓋正常場景、邊界場景和故意刁難的場景。每次技能定義有改動先跑一遍基線看各項指標(biāo)是漲是跌。沒有這套流程你根本不知道一次優(yōu)化是進(jìn)步還是倒退。5.2 最常見的三種失敗模式以及怎么快速定位我跑了半年的基線測試把失敗案例歸納成三類典型模式描述歧義型。技能描述寫得太寬泛模型理解出了偏差。定位方法把失敗案例里的模型調(diào)用日志打出來看它選的技能和實際該用的技能差在哪。解決辦法在描述里加不適用于條款或者調(diào)整技能名的表述。參數(shù)幻覺型。模型把用戶根本沒提到的信息填充進(jìn)了參數(shù)。最典型的就是日期——用戶說看一下銷售數(shù)據(jù)模型就默認(rèn)成今天其實用戶想知道的是整個季度。定位方法看調(diào)用日志里的參數(shù)和用戶原話的對應(yīng)關(guān)系。解決辦法收緊必填項沒有明確依據(jù)的參數(shù)必須觸發(fā)反問。編排斷裂型。復(fù)合技能執(zhí)行到中間某一步上一步的輸出沒接住下一步就沒法執(zhí)行。定位方法看任務(wù)狀態(tài)對象卡在哪個步驟。解決辦法為每對相鄰技能定義清晰的輸出-輸入映射并增加中間產(chǎn)物的格式校驗。5.3 迭代節(jié)奏小步快跑每版可回滾技能是活的必須持續(xù)迭代但迭代最怕失控。我的經(jīng)驗是兩條一是每次只改一個變量。不要同時改三個技能的描述再一起上線出了狀況你根本不知道是哪個改動導(dǎo)致的。二是版本灰度。新版本技能定義先在小流量上跑一天對比評估基線的各項指標(biāo)穩(wěn)定了再全量。我這邊現(xiàn)在保持著一個很實用的節(jié)奏每周固定花半天處理技能維護(hù)——新增需求帶來的新技能、用戶反饋暴露的描述問題、基線測試?yán)锊缓细竦拇媪考寄?。這個節(jié)奏看著慢但三個月下來技能庫的調(diào)用成功率和用戶滿意度都是穩(wěn)定向上的曲線。說到這我想起來一個收尾的小技巧也是我最近才沉淀下來的在技能庫的維護(hù)文檔里給每個技能都記一條血淚史記錄它在線上出過的最大一次事故和當(dāng)時的修復(fù)過程。這看起來像是自我檢討實際上特別有用——新人接手Agent項目的時候讀一遍這些記錄比看十遍設(shè)計文檔都管用。我團(tuán)隊的實習(xí)生靠這個文檔兩周就能獨(dú)立維護(hù)技能庫了。這個做法算是踩過這么多坑之后留給自己的一個還算體面的紀(jì)念品。