用落地關(guān)鍵:Agent Skills技能封裝與工程實(shí)踐)
這些年做大模型應(yīng)用我越來越覺得Agent 能不能真正落地干活關(guān)鍵不在模型本身多聰明而在于你往它手里塞了多少“趁手的家伙”。模型是大腦Agent Skills 就是手和腳——這句話我常跟團(tuán)隊(duì)講。今天借“agent-skills”這個項(xiàng)目把我在這塊積累的設(shè)計思路、實(shí)現(xiàn)細(xì)節(jié)和踩過的坑一次性攤開聊聊。這個項(xiàng)目做的是一套面向 Agent 的模塊化技能體系通俗點(diǎn)說就是把你希望 Agent 能做的事——比如查天氣、讀文檔、操作數(shù)據(jù)庫、調(diào)內(nèi)部 API——封裝成一個個標(biāo)準(zhǔn)化的技能包讓 Agent 在跑任務(wù)時按需調(diào)用。它解決的痛點(diǎn)是沒有技能體系的 Agent 每次對話都在“自由發(fā)揮”結(jié)果飄忽不定有了 Skills 之后Agent 的行為可預(yù)期、可復(fù)用、可維護(hù)尤其適合做垂直場景的落地。適合誰看正在做 Agent 應(yīng)用開發(fā)、做 AI 自動化工具、或者準(zhǔn)備把大模型接進(jìn)業(yè)務(wù)流程的開發(fā)者這篇內(nèi)容應(yīng)該能幫你少走不少彎路。1. 項(xiàng)目整體設(shè)計與思路拆解1.1 為什么 Agent 需要一套“技能封裝”我先說一個觀察。很多人做 Agent 的第一版就是把 API Key 一接扔給模型一個 system prompt 就開始聊天。Demo 階段確實(shí)能跑但一旦進(jìn)入真實(shí)業(yè)務(wù)問題馬上冒出來模型給出的回答時對時錯工具調(diào)用邏輯混亂同一個功能換個場景就不能用了。根本原因在于你沒有給 Agent 提供結(jié)構(gòu)化的能力邊界它根本不知道自己“會什么”。Agent Skills 的本質(zhì)是把“能力”從模型參數(shù)里外置出來。舉個例子你讓 Agent 讀一份 PDF 報告并總結(jié)如果你不提供任何技能模型只能瞎猜——它可能嘗試直接讀文件路徑可能編造一個不存在的 API也可能干脆拒絕。但如果你給它注冊一個read_pdf技能把文件解析、文本提取、摘要生成整個流程封裝好Agent 拿到任務(wù)就知道“先調(diào)這個技能把文本取出來再交給我的語言能力去總結(jié)”。這就好比新員工入職你不給他流程手冊和工具清單他再多聰明才智也使不出來。而且這套設(shè)計有個額外好處——模型無關(guān)。今天你用的是 GPT 系明天換成國產(chǎn)開源模型只要 Skills 層的接口沒變Agent 的核心邏輯就不用動。我在項(xiàng)目里把技能定義和模型調(diào)用完全解耦底層模型換過三輪上層業(yè)務(wù)代碼一行沒改過。1.2 技能分層架構(gòu)從“原子操作”到“業(yè)務(wù)流程”在設(shè)計 agent-skills 的初期我見過很多項(xiàng)目把技能設(shè)計成“一個大函數(shù)”——比如一個run_business_process包含幾十步邏輯。這種設(shè)計的維護(hù)成本相當(dāng)高任何一個環(huán)節(jié)改動都可能引發(fā)連鎖問題。我采用的拆分原則是分層、原子化、可組合。最底層是“原子技能”相當(dāng)于工具函數(shù)職責(zé)單一比如http_get、db_query、send_email。往上一層是“任務(wù)技能”把多個原子技能按固定流程編排起來比如generate_report內(nèi)部要調(diào)用數(shù)據(jù)查詢、模板渲染、文件導(dǎo)出三個原子技能。最頂層才是“流程技能”通常對應(yīng)一個完整的業(yè)務(wù)場景比如“周報自動生成”“客戶投訴處理”它內(nèi)部可以編排多個任務(wù)技能并且允許 Agent 根據(jù)上下文動態(tài)決定調(diào)用順序。分層設(shè)計帶來的直接好處是復(fù)用性。原子技能是全局共享的任務(wù)技能是可配置的流程技能才是綁定具體業(yè)務(wù)的。我統(tǒng)計過后續(xù)新增業(yè)務(wù)場景時大約 60% 的場景不需要從零開發(fā)用現(xiàn)有技能組合就能覆蓋。這種設(shè)計還讓排查問題變得簡單——出錯時你能快速定位到具體是哪一層出了問題而不是對著一個幾百行的“萬能函數(shù)”發(fā)呆。1.3 為什么選“描述驅(qū)動”而不是“硬編碼驅(qū)動”技能描述這件事我糾結(jié)過很長時間。最開始的版本是硬編碼每個技能寫死函數(shù)簽名Agent 通過 model function calling 直接調(diào)用。但這個方案有坑第一模型輸出的參數(shù)經(jīng)常不符合預(yù)期格式你不得不在代碼里堆大量校驗(yàn)邏輯第二技能的“可用條件”“副作用”“邊界限制”這些東西硬編碼沒法描述清楚模型經(jīng)常在錯誤的場景調(diào)用錯誤的技能。項(xiàng)目最終采用的是“Schema 描述 模型理解”的混合方案。每個技能都有一份結(jié)構(gòu)化的描述文件包含技能名稱、用途說明、輸入?yún)?shù)定義、輸出格式、約束條件和適用場景。這份描述會被注入到模型的上下文里模型基于描述來決定是否調(diào)用、如何調(diào)用。硬編碼只負(fù)責(zé)最終的參數(shù)校驗(yàn)和執(zhí)行不負(fù)責(zé)“判斷”。這個設(shè)計跑下來效果很明顯。模型對技能的理解準(zhǔn)確率提升了不止一個檔次——原因其實(shí)簡單你在描述里明確寫出“該技能只適用于處理用戶明確提供文件路徑的場景如果未提供路徑請先調(diào)用文件檢索技能”模型就不會在缺條件時硬上了。描述驅(qū)動還有一個好處就是新技能上線不需要改代碼只要新增一份描述文件Agent 次日就能學(xué)會使用它。2. 核心細(xì)節(jié)解析與實(shí)操要點(diǎn)2.1 技能 Schema 的設(shè)計演進(jìn)從 JSON 到 JSON Schema技能描述文件我不建議用單純的 JSON強(qiáng)烈推薦直接用 JSON Schema 標(biāo)準(zhǔn)。原因很現(xiàn)實(shí)JSON Schema 本身就是為描述數(shù)據(jù)結(jié)構(gòu)和約束設(shè)計的工具鏈成熟很多模型在訓(xùn)練時就見過大量 JSON Schema 樣本理解門檻低。我的 schema 設(shè)計經(jīng)歷了三個版本。v1 版本只定義參數(shù)類型和必填字段結(jié)果模型經(jīng)常把字符串傳進(jìn)整數(shù)字段——調(diào)教成本很高。v2 版本補(bǔ)上了描述信息給每個字段加了 verbose 說明情況好了很多但仍然存在格式報錯。v3 版本引入了枚舉約束、條件約束和示例值這三個關(guān)鍵要素比如一個狀態(tài)字段我可以聲明“僅允許取值為 pending / running / done / failed 之一”模型基本不會再犯低級錯誤。給個實(shí)際的 schema 片段這是execute_sql技能的描述我稍微簡化一下{ name: execute_sql, description: 在指定的業(yè)務(wù)數(shù)據(jù)庫上執(zhí)行只讀 SQL 查詢。僅適用于數(shù)據(jù)查詢場景禁止執(zhí)行寫入操作。, parameters: { type: object, properties: { sql: { type: string, description: 完整的 SQL 查詢語句必須為 SELECT 開頭, pattern: ^SELECT\\s.* }, db_name: { type: string, enum: [orders, users, inventory], description: 要查詢的目標(biāo)數(shù)據(jù)庫名稱 }, limit: { type: integer, default: 100, minimum: 1, maximum: 5000, description: 返回結(jié)果的最大行數(shù) } }, required: [sql, db_name] } }注意幾個細(xì)節(jié)。pattern字段用正則約束 SQL 必須以 SELECT 開頭這比任何提示詞都管用——模型見過這種約束后基本不會嘗試 INSERT 或 DROP。enum限制了數(shù)據(jù)庫范圍避免模型憑空捏造一個不存在的庫名。default值給模型提供了“不傳也行”的容錯空間。方案落地后需要人工修正參數(shù)的比例下降了七成以上。2.2 描述語言的寫作技巧告訴模型“什么時候別用”技能描述里最容易忽略的是“負(fù)面條件”——也就是告訴模型不該在什么情況下調(diào)用。大多數(shù)人的描述只寫“能做”不寫“不能做”結(jié)果模型在邊界場景反復(fù)試探。舉個例子get_weather技能大多數(shù)人會寫“獲取某個城市的天氣信息”。但我建議這樣寫獲取某個城市當(dāng)天的天氣信息。僅當(dāng)用戶明確指定城市名稱且意圖為查詢天氣時調(diào)用。若用戶詢問“適合穿什么衣服”“要不要帶傘”等衍生問題仍需先調(diào)用本技能獲取基礎(chǔ)天氣數(shù)據(jù)。若用戶未指定城市請勿調(diào)用應(yīng)主動向用戶詢問城市名稱。這段描述包含了三重信息觸發(fā)條件明確指定城市 天氣意圖、衍生場景處理間接問題也要先調(diào)技能、拒絕條件缺參不調(diào)用改為追問。這種精細(xì)化的描述直接把模型誤調(diào)用的概率按住了。經(jīng)驗(yàn)是描述信息里“不做什么”和“做什么”同樣重要。描述長度也有講究。太短模型理解不到位太長會擠占上下文空間。我踩過的平衡點(diǎn)是 200~400 字之間重點(diǎn)信息密集、覆蓋邊界情況即可不需要把每個使用案例都列用。技能過多時超過 50 個描述精簡化更是剛需否則上下文根本塞不下。2.3 參數(shù)校驗(yàn)與執(zhí)行沙箱最后的防線在代碼里模型再聰明也可能給出不合規(guī)的調(diào)用參數(shù)。所以參數(shù)校驗(yàn)一定不能省。我之前在execute_sql上吃過虧——一次內(nèi)測中模型把 DELETE 語句包裝成 SELECT 用子查詢發(fā)了過來雖然沒造成損失但也嚇出一身冷汗。從那以后技能執(zhí)行器的第一道工序一定是運(yùn)行時校驗(yàn)流程分三步校驗(yàn)參數(shù)格式類型、枚舉約束直接參考 JSON Schema 校驗(yàn)庫手寫。校驗(yàn)安全邊界比如 SQL 技能判斷語句前綴文件技能判斷路徑是否在允許目錄內(nèi)。校驗(yàn)調(diào)用頻率同一技能短時間內(nèi)的調(diào)用次數(shù)是否超過閾值防止模型陷入死循環(huán)。這些校驗(yàn)必須在技能執(zhí)行器層面完成不能依賴模型自律。更進(jìn)一步危險類技能我建議直接跑在沙箱里。數(shù)據(jù)庫操作走只讀賬號、文件操作限制目錄可寫范圍外部命令執(zhí)行盡量用容器隔離這是底線不是可選項(xiàng)我踩過的坑太多不想讓讀者再踩一遍。執(zhí)行器捕獲異常后返回結(jié)構(gòu)化錯誤信息模型讀到錯誤信息能自行修正參數(shù)重試這在 Agent 里是閉環(huán)的一環(huán)。3. 實(shí)操過程與核心環(huán)節(jié)實(shí)現(xiàn)3.1 技能注冊流程從新建技能到 Agent 可調(diào)用我按項(xiàng)目里成熟的做法走一套標(biāo)準(zhǔn)流程定義 schema 文件、實(shí)現(xiàn)執(zhí)行函數(shù)、注冊技能管理器、編寫測試用例、注入 Agent 配置。第一步在skills目錄下新建以技能名命名的子目錄里面放schema.json和main.pyschema 按 2.1 的規(guī)范寫。第二步實(shí)現(xiàn)執(zhí)行函數(shù)。這里給出一個文件讀取技能的完整示例# skills/read_file/main.py import os import json from pathlib import Path ALLOWED_ROOT Path(/data/workspace) def execute(params): # 參數(shù)校驗(yàn) file_path params.get(file_path) if not file_path: raise ValueError(缺少 file_path 參數(shù)) # 路徑安全檢查 target (ALLOWED_ROOT / file_path).resolve() if not target.is_relative_to(ALLOWED_ROOT): raise PermissionError(f路徑 {file_path} 超出允許訪問范圍) if not target.exists(): raise FileNotFoundError(f文件不存在: {file_path}) if target.stat().st_size 5 * 1024 * 1024: # 5MB raise ValueError(文件超過 5MB請配合 read_large_file 技能分段讀取) # 執(zhí)行讀取 content target.read_text(encodingutf-8, errorsreplace) # 輸出截斷保護(hù)防止上下文溢出 max_chars params.get(max_chars, 3000) truncated len(content) max_chars return { content: content[:max_chars], truncated: truncated, file_name: target.name, file_size: target.stat().st_size }注意里面的細(xì)節(jié)ALLOWED_ROOT限制訪問目錄5MB 大小保護(hù)max_chars截斷防上下文溢出errorsreplace防亂碼導(dǎo)致編碼報錯。這些都是真實(shí)調(diào)用場景里坑過我的點(diǎn)。is_relative_to是 Python 3.9 才有的方法如果你還在用老版本用os.path.commonpath替代效果完全一樣。第三步把技能注冊進(jìn)技能管理器。我用的注冊方式是裝飾器簡單直接# skill_manager.py skill_registry {} def register(name): def decorator(func): skill_registry[name] { handler: func, schema: json.loads(Path(fskills/{name}/schema.json).read_text()) } return func return decorator def list_skills(): return {name: info[schema] for name, info in skill_registry.items()} def execute_skill(name, params): if name not in skill_registry: raise KeyError(f未注冊的技能: {name}) skill skill_registry[name] # 入?yún)⑿r?yàn)直接用 jsonschema 庫驗(yàn)證 import jsonschema jsonschema.validate(params, skill[schema]) return skill[handler](params)這段代碼把“注冊”“列出”“校驗(yàn)執(zhí)行”三個核心能力串起來了。第四步測試用例至少覆蓋正常輸入、非法參數(shù)、找不到技能三個場景。第五步在 Agent 的 system prompt 尾部追加一段自動生成的技能清單格式類似“可用技能read_file讀取指定文件內(nèi)容支持文本文件— execute_sql對業(yè)務(wù)數(shù)據(jù)庫執(zhí)行只讀查詢…”模型就能感知到技能庫的存在并在決策時主動選用。3.2 模型調(diào)用與技能編排讓 Agent 學(xué)會“用”技能技能都注冊好了接下來是 Agent 調(diào)度層也就是 Agent 拿到用戶需求時怎么選技能、怎么傳參、怎么編排多步調(diào)用。控制流程我用標(biāo)準(zhǔn)的 ReAct 思路思考、決策、行動、觀測循環(huán)往復(fù)。具體實(shí)現(xiàn)上我設(shè)計了一個解析函數(shù)把回復(fù)拆解成一個調(diào)用列表agent-skills 的實(shí)現(xiàn)如下import json import re from typing import List, Dict def parse_skill_calls(text: str) - List[Dict]: # 匹配形如 SKILL_CALL: {skill: read_file, params: {...}} 的調(diào)用塊 pattern rSKILL_CALL:\s*(\{.*?\}) matches re.findall(pattern, text, re.DOTALL) calls [] for match in matches: try: data json.loads(match) calls.append({ skill: data.get(skill), params: data.get(params, {}) }) except json.JSONDecodeError: # 解析失敗就跳過不影響主流程 continue return calls為什么用SKILL_CALL:關(guān)鍵詞而不是直接用 function calling 系統(tǒng)核心原因有兩點(diǎn)。其一是模型無關(guān)我項(xiàng)目里要兼容 OpenAI 格式、Claude 格式還有開源模型每家 function calling 的實(shí)現(xiàn)方式不同統(tǒng)一走文本協(xié)議后上層 Agent 邏輯不用分叉。其二也是因?yàn)樗`活A(yù)gent 可以一次輸出多個并列的技能調(diào)用——比如同時讀兩個文件做對比這在純 function calling 里需要多輪交互才能實(shí)現(xiàn)。配套的 Agent 編排循環(huán)大致是讀取用戶輸入 → 構(gòu)造上下文system 技能描述 歷史 需求→ 模型輸出 → 解析技能調(diào)用 → 逐個執(zhí)行并寫回結(jié)果 → 再給模型繼續(xù)決策循環(huán)最多 5 輪以免死循環(huán)。我試過一到三輪根本不夠用一些需要先結(jié)果再決策的任務(wù)壓根沒法結(jié)束五輪是比較平衡的經(jīng)驗(yàn)值。3.3 技能組合實(shí)戰(zhàn)一個多步驟任務(wù)的完整鏈路光說理論容易飄直接展示一個“自動生成數(shù)據(jù)分析周報”的技能組合。Agent 收到指令“生成本周訂單周報”實(shí)際調(diào)用鏈路如下第一輪Agent 調(diào)用get_current_week獲取本周一至周日的日期范圍返回{start: 2025-06-09, end: 2025-06-15}。第二輪Agent 調(diào)用execute_sql查詢訂單數(shù)據(jù)SQL 形如SELECT date, SUM(amount) FROM orders WHERE date BETWEEN 2025-06-09 AND 2025-06-15 GROUP BY date。第三輪Agent 看到返回的數(shù)據(jù)觀察到部分日期缺失比如周四沒有訂單此刻它在上下文里自主判斷應(yīng)該調(diào)用append_report_note技能寫入“下劃線提示六月十二日暫無訂單記錄”這個判斷如果你沒給技能描述里寫上“觀察數(shù)據(jù)完整性”這條約束模型一般都發(fā)現(xiàn)不了但寫好描述后它就能補(bǔ)上這個思考環(huán)節(jié)。第四輪調(diào)用generate_html_report把數(shù)據(jù)渲染成周報 HTML 文件并返回文件路徑給用戶。整個流程里沒有強(qiáng)規(guī)則硬編碼鏈路Agent 每次都會根據(jù)實(shí)際數(shù)據(jù)“隨機(jī)應(yīng)變”。這就是技能編排的魅力——你給它足夠的組件和清晰的描述它自己就能規(guī)劃路徑。你真正要操心的只是每個技能的邊界是否清晰、描述是否準(zhǔn)確。4. 常見問題與排查技巧實(shí)錄4.1 技能調(diào)用了但結(jié)果不對先查描述再查代碼這是遇到最多的狀況。Agent 確實(shí)調(diào)了技能但拿到的結(jié)果不是想要的或者干脆跑偏。我的排查順序是固定的按頻率排序描述歧義、參數(shù)傳遞錯誤、模型輸出格式異常、執(zhí)行器 bug 這四類。描述歧義是最隱蔽的。比如你有一個search_products技能描述里只寫了“根據(jù)關(guān)鍵詞搜索商品”模型可能把“查找用戶名下訂單”也調(diào)用了它。排查方法比較簡單——把技能的 schema 描述和實(shí)際調(diào)用日志打印出來對照看模型到底理解成什么樣。修復(fù)方式是在描述里增加明確的“目的對比”比如“搜索商品僅用于用戶尋找可購買商品場景不用于查詢已購記錄查詢歷史訂單請使用 search_orders 技能”。這類問題修完效果立竿見影。參數(shù)傳遞錯誤也常見模型理解了該調(diào)哪個技能但傳參不對。比如read_file需要傳file_path模型總傳成path。這就是 schema 的description不夠直白或者屬性命名不直觀。我習(xí)慣把屬性名設(shè)計得和前幾個自然語言關(guān)鍵詞強(qiáng)相關(guān)——比如把file_path的 description 寫成“文件的路徑字符串例如 /data/workspace/report.docx”附上示例模型幾乎不會再用錯。輸出格式異常就是模型生成的內(nèi)容不符合約定的result標(biāo)簽或 JSON 結(jié)構(gòu)。這種情況多半是 prompt 里對輸出格式的約束不夠強(qiáng)或者是溫度參數(shù)設(shè)太高。技能調(diào)用的生成溫度我固定在 0.2 以下邏輯決策類任務(wù)溫度太高基本必出錯。4.2 技能多了就“瞎選”上下文優(yōu)化的三種思路技能數(shù)量超過 30 個后新的問題又來了Agent 選擇技能的正確率開始下降經(jīng)常把不相關(guān)的技能也列進(jìn)調(diào)用計劃。這是上下文過載和選擇困難癥的疊加效應(yīng)。一個直接的辦法是分組路由。把技能按領(lǐng)域分組比如“數(shù)據(jù)查詢組”“文件處理組”“消息通知組”Agent 先根據(jù)需求選一個組再在組內(nèi)選具體技能。這等于把“50 選 1”變成了“5 選 1 10 選 1”準(zhǔn)確率提升明顯。我用一個簡單的group處理先讓模型用關(guān)鍵詞匹配找到最相關(guān)的組描述再從該組返回技能做二次匹配組內(nèi)匹配失敗則回退到全局匹配保證可用性兜底。另一個是給技能增加“熱度權(quán)重”。高頻使用的技能放在描述列表的前面低頻技能放后面或折疊。模型對上下文靠前的信息權(quán)重更高這個排序調(diào)整帶來的收益很低成本值得每個人都試試。第三個思路就是動態(tài)裁剪——對于那些明確包含“不要調(diào)用技能 X 完成該任務(wù)”的指令在上下文里直接剔除 X 的描述。也可以在任務(wù)開始時先做一次意圖識別只把相關(guān)組的技能描述注入上下文其他組的描述留到需要時再加載。這種方式適合技能庫非常大的場景上下文瘦身后選擇準(zhǔn)確率和推理速度都上去了。4.3 技能調(diào)用的性能與穩(wěn)定性日志、超時和降級Agent 技能調(diào)用有三件容易被忽略的事。第一件事技能執(zhí)行一定要有超時控制。我之前有過一個 Python 技能在處理大文件時卡了 20 分鐘用戶端看起來就是 Agent 徹底失聯(lián)?,F(xiàn)在所有技能執(zhí)行統(tǒng)一套超時外部 API 類技能默認(rèn) 10 秒本地計算類默認(rèn) 30 秒超時直接返回錯誤信息給 Agent讓它換策略。第二件事全鏈路日志必須有。對技能調(diào)用記錄至少包含調(diào)用時間、技能名、入?yún)?、出參摘要、耗時、錯誤信息、模型決策前文。其中包括模型觀察片段對排查那些“模型突然調(diào)用奇怪技能”的問題幫助太大了。有了這些日志你可以回溯每一步?jīng)Q策的因果關(guān)系——我遇到過模型在一個查詢技能失敗后連續(xù)重試五次同樣的調(diào)用看了日志才發(fā)現(xiàn) prompt 里沒有約束重試邏輯導(dǎo)致死循環(huán)。后續(xù)在描述里加了“技能執(zhí)行失敗時更換參數(shù)或更換技能連續(xù)兩次失敗請停止并告知用戶”。第三件事要有降級方案。核心技能掛掉時 Agent 至少要說人話。我的做法是給每個關(guān)鍵技能配一個“替代技能鏈”比如數(shù)據(jù)庫查詢失敗降級為讀取已生成的離線數(shù)據(jù)快照文件文件解析失敗降級為轉(zhuǎn)交用戶手動處理。降級邏輯必須在描述里寫明Agent 才不會在異常情況下一問三不知。4.4 常見問題速查表現(xiàn)象直接原因排查方法解決方案Agent 不調(diào)用任何技能技能描述未注入上下文 / 描述與任務(wù)意圖不匹配查看發(fā)給模型的 system prompt 是否包含技能清單確認(rèn)技能描述注入邏輯在 prompt 末尾明示“可直接調(diào)用的技能有…”反復(fù)調(diào)用同一技能直到報錯缺少失敗重試約束查看調(diào)用日志中錯誤代碼在技能描述中加入失敗處理指引設(shè)定重試次數(shù)上限技能被“張冠李戴”多個技能之間場景差異不夠清晰對比相似技能的 description 文本增加“使用場景”和“禁用場景”專門段落必要時合并技能參數(shù)格式頻繁報錯缺少入?yún)⑿r?yàn) / 未使用 JSON Schema 約束檢查執(zhí)行器的校驗(yàn)邏輯用 jsonschema 庫做嚴(yán)格校驗(yàn)schema 中補(bǔ)充枚舉、約束、示例上下文長度不夠放技能列表技能數(shù)量過多統(tǒng)計技能列表的 token 開銷分組路由 動態(tài)裁剪只注入當(dāng)前任務(wù)相關(guān)技能模型自己“編造”技能結(jié)果模型幻覺 / 執(zhí)行器未返回結(jié)構(gòu)化錯誤檢查生成時的溫度參數(shù)和技能輸出格式約束把溫度降到 0.3 以下在描述中強(qiáng)制要求“必須先調(diào)用后回答”5. 更多實(shí)操心得與后續(xù)想法5.1 從零搭建技能包三種適合起步的通用技能如果你打算在自己的項(xiàng)目里把 Skills 這套跑起來除了項(xiàng)目自身的業(yè)務(wù)技能我建議優(yōu)先搭三個通用的“地基級”技能。第一個是web_search讓 Agent 能檢索外部信息而非全靠模型記憶連搜索引擎的 API 地址、參數(shù)、超時和錯誤處理都封裝好。第二是web_fetch抓取指定 URL 的正文內(nèi)容并轉(zhuǎn)成 Markdown很多 RAG 場景都依賴它。第三個是current_datetime返回當(dāng)前時間和日期——別覺得這個技能low模型訓(xùn)練數(shù)據(jù)根本沒有實(shí)時時間概念沒有這個技能Agent 連“今天星期幾”都可能答錯更別提和日期相關(guān)的業(yè)務(wù)邏輯了。這三個技能每次新項(xiàng)目開箱即用占據(jù)了技能調(diào)用量的很大比例?!皼]多少‘高技術(shù)含量’但卻是 Agent 日常運(yùn)轉(zhuǎn)的基礎(chǔ)設(shè)施?!?.2 技能數(shù)量會隨著業(yè)務(wù)增長而膨脹規(guī)?;瘯r要做的事當(dāng)技能庫膨脹到幾百個除了分組路由和動態(tài)裁剪還有兩件事要提前想清楚。其一是技能間互相調(diào)用的權(quán)限管理我的做法是把技能分成基礎(chǔ)層和業(yè)務(wù)層業(yè)務(wù)技能可以調(diào)用基礎(chǔ)技能但反過來禁止避免依賴混亂。其二是技能版本管理一旦多個 Agent 共享技能庫升級技能必須帶版本號和變更日志否則一個技能更新可能導(dǎo)致所有下游 Agent 行為突變——這個問題我在實(shí)踐里踩過現(xiàn)在每個技能目錄下都會維護(hù)一份簡單的CHANGELOG.md。做一次技能庫全量審查也是必要的至少每個迭代做一輪找出兩個月以上未被調(diào)用的技能要么刪除要么合并。技能不是越多越好——無用的技能描述不僅增加 token 開銷還會干擾模型的決策判斷。精簡二十來個描述后調(diào)用準(zhǔn)確率整體提升了好幾個百分點(diǎn)這是實(shí)打?qū)嵉氖找妗?.3 最后的一個小技巧讓技能會說話我最后想分享一個項(xiàng)目里真正落地有效的設(shè)計——給技能加上“返回意圖”字段。很多技能執(zhí)行完只是干巴巴地把數(shù)據(jù)返回給 Agent比如查庫存返回 JSONAgent 還得自己組織語言。但如果技能在設(shè)計時就帶上一個user_message字段比如execute_sql返回{user_message: 本周累計銷售額為 128,500 元較上周下降 3.2%, data: [...]}Agent 可以直接把這個話術(shù)微調(diào)后發(fā)給用戶。用戶在體驗(yàn)上會感覺這對話很自然“像是真人助手在匯報”而不是冷冰冰的 JSON 輸出。需要注意的是user_message的內(nèi)容也必須讓模型評估可信度技能執(zhí)行器只提供事實(shí)性陳述模型負(fù)責(zé)潤色和補(bǔ)充別讓技能直接替模型“定調(diào)子”這個邊界要守住。這個技巧雖然簡單但對提升智能體產(chǎn)品體驗(yàn)的幫助非常明顯。這套 agent-skills 的設(shè)計實(shí)踐從拆解技能到底層實(shí)現(xiàn)踩過的坑一次比一次深刻。說實(shí)話Agent 開發(fā)沒有銀彈Skills 也不過是讓模型能力具象化的手段之一。但把技能描述寫得精細(xì)點(diǎn)、邊界劃得清晰點(diǎn)、日志留得充分點(diǎn)你會明顯感覺到 Agent 的表現(xiàn)從一個“碰運(yùn)氣的聊天氣泡”逐漸變成一個“按規(guī)矩辦事的同事”。我個人的體會是你不必一開始就追求完美的架構(gòu)哪怕只從兩三個原子技能起步把循環(huán)跑通再逐步擴(kuò)充這比設(shè)計一堆花哨的能力卻跑不通要強(qiáng)得多。希望這篇記錄能給你一些啟發(fā)。