據(jù)設(shè)計(jì)到沙箱權(quán)限的完整指南)
1. Skills 在 OpenClaw 里的定位先搞懂它到底解決了什么問題接觸 OpenClaw 的朋友應(yīng)該都有這種感覺默認(rèn)裝好之后它確實(shí)能聊天、能總結(jié)、能寫代碼但真要它去操作點(diǎn)實(shí)際的東西——比如幫你批量重命名一整個(gè)目錄的文件、定時(shí)抓某個(gè)網(wǎng)頁的更新、把散落的筆記按規(guī)則歸檔——它就有點(diǎn)“有勁使不出”。這不是模型不行而是 LLM 本質(zhì)上只是一個(gè)文本生成引擎它不會(huì)真的去執(zhí)行命令、讀寫文件、調(diào)用外部服務(wù)。Skills 這套機(jī)制存在的全部意義就是把這層“只會(huì)說不會(huì)做”的窗戶紙捅破。我在第一次看到 OpenClaw 的 Skills 設(shè)計(jì)時(shí)印象最深的一點(diǎn)是它沒有把技能做成一堆散落的函數(shù)庫也沒有做成一個(gè)巨大的插件框架而是把每個(gè)技能都定義成一個(gè)“目錄 元數(shù)據(jù) 可執(zhí)行方法”的自治單元。這種設(shè)計(jì)的好處是極其直觀的——你給 AI 添加一項(xiàng)能力和你在命令行里安裝一個(gè)工具的行為模式完全一致把東西放到該放的位置聲明清楚它是什么、什么時(shí)候用、怎么用剩下的交給系統(tǒng)去加載。如果你只是想給 OpenClaw 加一個(gè)“幫我查天氣”的小功能你完全可以不用理解底層實(shí)現(xiàn)照著目錄結(jié)構(gòu)往里填內(nèi)容就行。但如果你想把它用扎實(shí)——比如給自己的項(xiàng)目定制一套自動(dòng)化工作流、讓多個(gè)技能之間互相調(diào)用、控制技能的觸發(fā)條件和權(quán)限邊界——那就必須把它的技術(shù)架構(gòu)吃透。這篇內(nèi)容我把 Skills 從目錄規(guī)范、元數(shù)據(jù)設(shè)計(jì)到執(zhí)行流程、常見坑全部過一遍適合已經(jīng)被 OpenClaw 的基本使用勾起了興趣、準(zhǔn)備深度定制自己技能包的開發(fā)者參考。2. Skills 的整體設(shè)計(jì)思路拆解為什么是“目錄即技能”2.1 LLM 的能力邊界與技能系統(tǒng)的補(bǔ)位邏輯要理解 Skills 的架構(gòu)得先回到 AI 助手的運(yùn)行本質(zhì)上。OpenClaw 的核心引擎是一個(gè)對(duì)話循環(huán)用戶輸入 → 模型判斷意圖 → 生成回復(fù) → 可能需要調(diào)用工具 → 拿到結(jié)果繼續(xù)生成。在這個(gè)循環(huán)里模型自身的能力只覆蓋了前兩步后面真正產(chǎn)生實(shí)際效果的步驟必須要靠外部代碼來補(bǔ)位。Skills 系統(tǒng)就是這塊“外部代碼”的統(tǒng)一封裝層。它把原本散落在各種地方的工具調(diào)用邏輯——比如寫死的 Python 腳本、shell 命令、HTTP 請(qǐng)求——全部收納進(jìn)一個(gè)標(biāo)準(zhǔn)化的技能目錄里并告訴模型你面前有這些能力可用每一項(xiàng)能力何時(shí)適用、需要什么參數(shù)、會(huì)返回什么結(jié)果。這樣模型就不需要“憑空想象”怎么操作一個(gè)文件系統(tǒng)它只需要學(xué)會(huì)“選擇技能 填對(duì)參數(shù)”具體的臟活累活交給技能的底層實(shí)現(xiàn)去完成。從整體架構(gòu)上看OpenClaw 把技能系統(tǒng)分成了四個(gè)層面技能定義層描述技能是什么、何時(shí)用、方法實(shí)現(xiàn)層實(shí)際執(zhí)行的代碼邏輯、調(diào)度層模型決定調(diào)用哪個(gè)技能、傳什么參數(shù)、執(zhí)行層在隔離環(huán)境里跑方法并返回結(jié)果。四個(gè)層面各司其職互不侵入。這個(gè)分層好處在后期維護(hù)時(shí)特別明顯你要改一個(gè)技能的底層實(shí)現(xiàn)邏輯完全不用動(dòng)元數(shù)據(jù)描述你想調(diào)整某個(gè)技能的觸發(fā)條件也不需要碰它的代碼。2.2 技能注冊(cè)表與“按需加載”的取舍很多人剛開始會(huì)有一個(gè)直覺既然要給模型暴露技能那把全部技能的所有信息一股腦塞進(jìn)上下文不就行了這個(gè)想法在技能數(shù)量少時(shí)沒問題但一旦你攢了十幾個(gè)甚至幾十個(gè)技能對(duì)上下文的浪費(fèi)是致命的。OpenClaw 的做法是維護(hù)一張技能注冊(cè)表。系統(tǒng)在啟動(dòng)時(shí)掃描技能目錄把每個(gè)技能的名字、功能描述、適用場景這些“摘要信息”匯總成一張清單注入到模型上下文中。模型看到的是類似“當(dāng)前可用 12 項(xiàng)技能文件批量重命名操作文件系統(tǒng)、網(wǎng)頁內(nèi)容抓取獲取 URL 內(nèi)容、定時(shí)任務(wù)管理創(chuàng)建/查詢定時(shí)任務(wù)……”這樣的索引。只有當(dāng)模型判斷某個(gè)技能確實(shí)適用時(shí)完整的方法參數(shù)定義和調(diào)用約定才會(huì)被加載。這就像餐廳只會(huì)把菜單遞給你而不是把后廚所有食材的采購記錄、切配流程全擺在你面前。你根據(jù)菜單點(diǎn)菜后廚才會(huì)開始處理具體食材。我實(shí)際用下來這種“按需加載”對(duì) token 占用的控制效果非常顯著。剛開始我只有五六個(gè)技能時(shí)感覺不明顯后來把技能擴(kuò)展到二十多個(gè)上下文壓縮的優(yōu)勢才真正體現(xiàn)出來——模型在復(fù)雜對(duì)話場景下很少因?yàn)榧寄苊枋鲞^長而“忘記”用戶的原始指令。2.3 為什么強(qiáng)調(diào)“技能自治”O(jiān)penClaw 對(duì)技能的最基本原則是一個(gè)技能目錄就是一個(gè)完整的、自包含的功能模塊。你在目錄里放進(jìn)元數(shù)據(jù)、方法代碼、依賴清單、示例說明這個(gè)技能就能在任意一臺(tái)部署了 OpenClaw 的機(jī)器上運(yùn)行——不需要額外手動(dòng)注冊(cè)也不用往別處再填一份配置。這個(gè)設(shè)計(jì)直接帶來的好處有兩個(gè)。第一是可移植性我在本機(jī)調(diào)試好的技能包直接拷貝到另一臺(tái)服務(wù)器上重啟后就能被識(shí)別和調(diào)用這大大降低了技能的分發(fā)成本讓人可以像分享開源工具一樣分享自己寫好的技能包。第二是可測試性因?yàn)槊總€(gè)技能都是獨(dú)立的你可以在不啟動(dòng)整個(gè) AI 對(duì)話引擎的情況下單獨(dú)跑一個(gè)技能的方法入口來驗(yàn)證邏輯正確性調(diào)試效率提升非常明顯尤其是技能里帶了復(fù)雜的文件操作或網(wǎng)絡(luò)請(qǐng)求邏輯時(shí)。3. Skills 的目錄結(jié)構(gòu)與元數(shù)據(jù)規(guī)范3.1 一個(gè)標(biāo)準(zhǔn)技能目錄長什么樣OpenClaw 里每個(gè)技能都以一個(gè)獨(dú)立的子目錄形式存放在skills/根目錄下。我自己在建技能時(shí)使用的標(biāo)準(zhǔn)結(jié)構(gòu)是這樣的skills/ └── my_skill/ ├── SKILL.md ├── methods/ │ ├── __init__.py │ ├── main.py │ └── helper.py ├── assets/ │ └── templates/ ├── requirements.txt └── README.md其中SKILL.md是整個(gè)技能的門面承載了所有模型需要知道的元數(shù)據(jù)methods/目錄放實(shí)際的 Python 方法實(shí)現(xiàn)assets/目錄放技能運(yùn)行所需的靜態(tài)資源requirements.txt聲明依賴。這四個(gè)部分各司其職缺一不可。有一點(diǎn)要提醒目錄名就是技能名。命名時(shí)盡量用全小寫加下劃線不要用空格和奇怪的符號(hào)。我在調(diào)試時(shí)踩過坑給一個(gè)技能起名叫My Web Fetcher結(jié)果系統(tǒng)在掃描時(shí)因?yàn)槟夸浢锏目崭駥?dǎo)致解析異常技能一直無法被識(shí)別。最后老老實(shí)實(shí)改成my_web_fetcher才正常。OpenClaw 對(duì)技能目錄名的解析規(guī)則不算嚴(yán)格但空格和中文這類字符建議避免否則后續(xù)你在技能索引里看到的名稱也可能顯示異常。3.2 SKILL.md 元數(shù)據(jù)逐項(xiàng)拆解SKILL.md是技能系統(tǒng)里最核心的配置文件格式上采用 YAML frontmatter 加 Markdown 正文的組合。我整理了一份最常用的元數(shù)據(jù)字段表按必需程度排列如下字段含義必需性name技能唯一標(biāo)識(shí)一般與目錄名保持一致必需description一句話描述技能功能模型靠它判斷是否適用必需trigger_keywords觸發(fā)關(guān)鍵詞列表輔助模型在對(duì)話中識(shí)別調(diào)用時(shí)機(jī)推薦overrides覆蓋默認(rèn)模型行為的指令定義“何時(shí)絕對(duì)不要調(diào)用”可選require_env需要的環(huán)境變量或憑據(jù)名稱列表按需permission技能運(yùn)行所需的權(quán)限級(jí)別如sandboxed/admin推薦description這個(gè)字段我建議認(rèn)真打磨因?yàn)樗苯記Q定了模型“看不看得懂”你的技能。寫的時(shí)候不要用太籠統(tǒng)的話比如“處理文件”這種就沒法讓模型判斷具體怎么用。比較好的格式是動(dòng)作 對(duì)象 適用場景。例如description: 批量重命名指定目錄下的所有文件支持前后綴添加、擴(kuò)展名替換、正則表達(dá)式匹配適用于需要整理下載目錄或日志文件的場景。trigger_keywords也不是擺設(shè)。雖然如今模型的意圖識(shí)別能力已經(jīng)很強(qiáng)但在技能數(shù)量龐大的情況下給幾個(gè)高頻關(guān)鍵詞能顯著提升觸發(fā)準(zhǔn)確率。比如你的技能是抓取網(wǎng)頁內(nèi)容加抓取、網(wǎng)頁內(nèi)容、爬取、讀取頁面這些詞模型在用戶說“幫我看看這個(gè)網(wǎng)頁里寫了什么”時(shí)就能更快聯(lián)想到調(diào)用這個(gè)技能。3.3 方法注冊(cè)與參數(shù)聲明讓你的函數(shù)能被模型“看見”元數(shù)據(jù)描述解決了“什么時(shí)候用”的問題方法注冊(cè)則解決“怎么用”的問題。一個(gè)技能可以在methods/目錄里定義多個(gè)方法每個(gè)方法都對(duì)應(yīng)一段獨(dú)立的可調(diào)用邏輯。以 Python 技能為例每個(gè)方法本質(zhì)上就是一個(gè)普通的 Python 函數(shù)但你需要通過一個(gè)統(tǒng)一入口暴露給系統(tǒng)。OpenClaw 的約定是在methods/__init__.py里維護(hù)一個(gè)METHODS字典把方法名映射到對(duì)應(yīng)的處理函數(shù)上。系統(tǒng)只會(huì)加載這個(gè)字典里顯式聲明的方法沒注冊(cè)的私有函數(shù)不會(huì)被暴露這給了開發(fā)者很大的自由度——你可以在輔助模塊里寫一堆內(nèi)部函數(shù)但只要不注冊(cè)模型就不會(huì)碰它們也不用擔(dān)心暴露多余的調(diào)用面。參數(shù)聲明方面我建議使用類型注解并給出默認(rèn)值。這不僅是為了代碼規(guī)范而是實(shí)打?qū)嵉赜泻锰幠P驮谏烧{(diào)用參數(shù)時(shí)會(huì)參考你的函數(shù)簽名類型注解可以幫助它生成更精確的 JSON 參數(shù)。如果你的函數(shù)需要一個(gè)directory參數(shù)寫成directory: str和寫成不帶注解的directory模型在兩種情況下傳錯(cuò)類型的概率有明顯差異。安全的經(jīng)驗(yàn)是每個(gè)方法都寫成接收一個(gè)參數(shù)字典的函數(shù)而不是寫成一個(gè)參數(shù)跟一個(gè)位置實(shí)參的普通函數(shù)。這樣當(dāng)模型生成調(diào)用時(shí)你可以在函數(shù)內(nèi)部對(duì)參數(shù)做統(tǒng)一校驗(yàn)和兜底處理某個(gè)參數(shù)缺失也不會(huì)讓整個(gè)技能直接崩潰。我在自己的技能包里給每個(gè)方法都加了一段這樣的開頭邏輯大幅提升了調(diào)用的健壯性。4. Skills 的執(zhí)行流程與觸發(fā)機(jī)制深入4.1 從系統(tǒng)啟動(dòng)到技能調(diào)用全鏈路時(shí)序搞清楚一個(gè)技能從被掃描到被執(zhí)行的完整過程是理解整個(gè)架構(gòu)的關(guān)鍵。我按實(shí)際運(yùn)行時(shí)的先后順序梳理了這條鏈路上的核心環(huán)節(jié)掃描階段OpenClaw 啟動(dòng)時(shí)遍歷skills/根目錄識(shí)別每個(gè)包含SKILL.md的子目錄。解析階段讀取SKILL.md的 frontmatter校驗(yàn)必填字段是否齊全將技能名、描述、關(guān)鍵詞等摘要信息寫入注冊(cè)表。注入階段注冊(cè)表里的技能摘要被拼接進(jìn)系統(tǒng)提示詞作為模型可見的“可用技能列表”。觸發(fā)決策階段用戶在對(duì)話中提出請(qǐng)求模型根據(jù)當(dāng)前上下文和技能摘要判斷是否調(diào)用某個(gè)技能、調(diào)用哪個(gè)方法、傳入什么參數(shù)。調(diào)用執(zhí)行階段模型發(fā)出函數(shù)調(diào)用請(qǐng)求OpenClaw 解析請(qǐng)求、校驗(yàn)參數(shù)、找到對(duì)應(yīng)方法在目標(biāo)權(quán)限環(huán)境下執(zhí)行。結(jié)果回填階段方法執(zhí)行結(jié)果被轉(zhuǎn)換為文本重新注入對(duì)話上下文模型基于結(jié)果生成最終回復(fù)。這四個(gè)步驟里最容易出問題的是第 4 步——模型的決策環(huán)節(jié)。其他步驟都是確定性代碼流程出問題基本在參數(shù)上唯獨(dú)觸發(fā)決策是概率性的同一個(gè)請(qǐng)求可能每次的調(diào)用結(jié)果都不一樣。所以我后面會(huì)說為了讓技能穩(wěn)定觸發(fā)你需要在元數(shù)據(jù)描述和權(quán)限配置上多做功課。4.2 顯式調(diào)用 vs. 隱式觸發(fā)OpenClaw 的 Skills 觸發(fā)策略大致分為兩種模式顯式調(diào)用和隱式觸發(fā)。理解它們的區(qū)別能幫助你針對(duì)不同技能選擇正確的配置方式。顯式調(diào)用是最高可控的模式。在技能元數(shù)據(jù)中如果你明確聲明了這個(gè)技能只應(yīng)在用戶提到特定語法時(shí)使用比如設(shè)置了override指令“只有用戶輸入/search開頭的內(nèi)容時(shí)才調(diào)用本技能”那么模型幾乎不會(huì)誤觸發(fā)它。這種模式適合那些“寧可不調(diào)不能亂調(diào)”的技能——比如刪除文件、修改配置、發(fā)送消息這類有副作用的操作我強(qiáng)烈建議用顯式調(diào)用約束住。隱式觸發(fā)則依賴模型在自然對(duì)話中對(duì)用戶意圖的理解。比如你有個(gè)“總結(jié)網(wǎng)頁內(nèi)容”的技能用戶說“這篇新聞太長了幫我精簡一下核心觀點(diǎn)”模型可能不會(huì)自覺去調(diào)用技能——因?yàn)橛脩魶]提“網(wǎng)頁”也沒提“抓取”。這種情況下你需要在trigger_keywords里覆蓋語義寬度足夠的詞比如新聞、文章、網(wǎng)頁、內(nèi)容太多、精簡。我個(gè)人的建議是把兩者結(jié)合核心操作技能設(shè)置為顯式調(diào)用保證安全邊界輔助提效技能設(shè)置為隱式觸發(fā)讓使用體驗(yàn)更自然。而且 OpenClaw 支持在對(duì)話中間動(dòng)態(tài)提醒用戶有哪些可用技能相當(dāng)于隱式觸發(fā)的一個(gè)兜底——即使模型沒識(shí)別出來你可以用一句話把它點(diǎn)醒。4.3 上下文注入與 Token 預(yù)算分配策略技能摘要注入到上下文里這聽起來簡單但實(shí)際操作時(shí)需要精心控制信息量。每個(gè)技能的描述、關(guān)鍵詞、權(quán)限聲明都要占 token 預(yù)算技能一多這塊累積起來非??捎^。為了避免上下文爆炸OpenClaw 會(huì)為每個(gè)技能生成一段精簡的索引項(xiàng)而不是把全文都塞進(jìn)去。我在使用中發(fā)現(xiàn)技能描述寫得越精煉模型在決策時(shí)越果斷。寫得很啰嗦的描述反而會(huì)導(dǎo)致模型“看不過來”在技能多的時(shí)候經(jīng)常出現(xiàn)選擇困難甚至把兩個(gè)相似技能搞混。我自己控制 token 預(yù)算的經(jīng)驗(yàn)是每個(gè)技能的描述控制在 60~100 字左右關(guān)鍵詞控制在 5 個(gè)左右。超過這個(gè)量收益就開始遞減。另外一個(gè)實(shí)用技巧是在技能描述里主動(dòng)說明“本技能不適用于什么場景”這種負(fù)例描述雖然會(huì)多花一點(diǎn)點(diǎn) token但能顯著減少誤調(diào)用。比如一個(gè)只處理本地文件的技能加上“僅處理本機(jī)文件不適用于網(wǎng)絡(luò)資源”模型在用戶提出 URL 請(qǐng)求時(shí)就不會(huì)錯(cuò)誤地選它。4.4 權(quán)限隔離與沙箱機(jī)制為什么技能不能想干嘛就干嘛OpenClaw 的 Skills 技術(shù)架構(gòu)里最值得借鑒的設(shè)計(jì)是它的權(quán)限分級(jí)與沙箱隔離。每個(gè)技能在元數(shù)據(jù)里可以聲明自己需要的權(quán)限級(jí)別級(jí)別不同執(zhí)行環(huán)境的邊界也不同。我目前用過的權(quán)限模式有三種純沙箱模式方法運(yùn)行在受限環(huán)境里不能訪問文件系統(tǒng)只能通過專門的 API 完成少數(shù)操作。適合純計(jì)算、文本處理類技能。有限文件訪問模式可以讀寫特定目錄如~/openclaw/workspace/下的子目錄但被限制在固定的工作目錄里不能滿盤亂跑。適合大多數(shù)文件操作類技能。宿主模式全權(quán)限運(yùn)行可以執(zhí)行任意命令、訪問任意目錄。適合系統(tǒng)管理類技能但風(fēng)險(xiǎn)也最高。我強(qiáng)烈建議哪怕是本地單機(jī)使用的 OpenClaw也要認(rèn)真思考每個(gè)技能的權(quán)限邊界。很多人一開始圖省事把所有技能都配成宿主模式覺得反正都是自己機(jī)器。但技能多了以后你可能會(huì)從社區(qū)下載別人分享的技能包那些代碼你根本沒逐行讀過。有一次我下載了一個(gè)“生成項(xiàng)目周報(bào)”的技能運(yùn)行后發(fā)現(xiàn)它在assets/目錄里藏了一段收集系統(tǒng)信息并上傳的代碼。雖然可能是無意的數(shù)據(jù)統(tǒng)計(jì)但這種行為如果發(fā)生在全權(quán)限模式下風(fēng)險(xiǎn)非常大。配好權(quán)限級(jí)別才是對(duì)“技能自治”這個(gè)理念最好的補(bǔ)充。5. 實(shí)操從零編寫一個(gè)自定義 Skill 的完整過程5.1 先定場景再寫代碼寫技能最忌諱一上來就開始擼代碼。我先分享自己慣用的流程先明確用戶會(huì)在什么場景下說出一句什么樣的話、期望得到一個(gè)什么樣的結(jié)果再倒推技能該拆成幾個(gè)方法、需要哪些參數(shù)。舉個(gè)例子。我想給 OpenClaw 加一個(gè)“整理下載目錄”的技能。用戶的訴求可能是“把下載文件夾里最近一周的安裝包整理到按軟件名分好的子目錄里”。這個(gè)場景就非常清晰技能名organize_downloads功能描述掃描指定下載目錄按擴(kuò)展名/來源/日期分類文件移動(dòng)到對(duì)應(yīng)子目錄需要的方法scan_directory掃描并統(tǒng)計(jì)文件類型、move_files按規(guī)則移動(dòng)文件、dry_run預(yù)演模式下只輸出整理計(jì)劃不真正移動(dòng)這個(gè)拆分的好處是dry_run方法相當(dāng)于給整個(gè)技能加了一層安全網(wǎng)用戶在真實(shí)執(zhí)行前可以先預(yù)覽整理計(jì)劃確認(rèn)無誤后再真正動(dòng)手。我在技能里加了這個(gè)方法之后誤刪文件的焦慮基本消失了推薦你也這樣做。5.2 目錄初始化與元數(shù)據(jù)編寫實(shí)例場景定好之后我開始搭目錄。以organize_downloads為例完整步驟如下cd ~/openclaw/skills mkdir organize_downloads cd organize_downloads mkdir methods assets touch SKILL.md requirements.txt然后我寫SKILL.md內(nèi)容如下--- name: organize_downloads description: 掃描指定目錄并按文件擴(kuò)展名、修改日期或文件名關(guān)鍵詞將文件分類移動(dòng)到子目錄適用于整理下載文件夾、日志目錄、臨時(shí)文件目錄等場景。僅處理本機(jī)文件不適用于網(wǎng)絡(luò)資源。 trigger_keywords: - 整理下載 - 分類文件 - 歸檔 - 移動(dòng)文件 permission: sandboxed --- # organize_downloads 按規(guī)則整理指定目錄中的文件支持三種分類模式 1. 按擴(kuò)展名分類 2. 按修改日期分類如 2024-01/ 3. 按文件名關(guān)鍵詞分類 所有移動(dòng)操作前必須先調(diào)用 dry_run 預(yù)覽計(jì)劃。注意這里我聲明了permission: sandboxed并把移動(dòng)操作限定在指定工作目錄內(nèi)防止它滿盤亂跑。trigger_keywords里覆蓋了用戶最可能說出的幾種說法模型看到“整理下載文件夾”這種請(qǐng)求時(shí)大概率會(huì)聯(lián)想到這個(gè)技能。5.3 方法實(shí)現(xiàn)與返回值設(shè)計(jì)接下來是核心代碼。methods/main.py里我實(shí)現(xiàn)一個(gè)公開入口函數(shù)它接收參數(shù)字典并完成調(diào)度organize_downloads 技能方法實(shí)現(xiàn) import os import shutil from datetime import datetime from typing import Dict, Any def _normalize_path(path: str) - str: 統(tǒng)一路徑格式避免傳入?yún)?shù)帶尾斜杠導(dǎo)致比較出錯(cuò) return os.path.abspath(os.path.expanduser(path)) def scan_directory(params: Dict[str, Any]) - Dict[str, Any]: 掃描目錄返回文件類型統(tǒng)計(jì)與文件清單 target_dir _normalize_path(params.get(directory, .)) if not os.path.isdir(target_dir): return {success: False, error: f目錄不存在: {target_dir}} file_list [] type_stats {} for root, dirs, files in os.walk(target_dir): # 跳過以 . 開頭的隱藏目錄 dirs[:] [d for d in dirs if not d.startswith(.)] for fname in files: fpath os.path.join(root, fname) ext os.path.splitext(fname)[1].lower() or [無擴(kuò)展名] type_stats[ext] type_stats.get(ext, 0) 1 file_list.append({ path: fpath, name: fname, ext: ext, size: os.path.getsize(fpath), }) return {success: True, total_files: len(file_list), type_stats: type_stats, files: file_list[:100]}返回值的設(shè)計(jì)很有講究。我在技能里做一件事時(shí)會(huì)堅(jiān)持讓返回結(jié)果包含三層信息是否成功、簡潔的統(tǒng)計(jì)摘要、必要時(shí)附上的詳情數(shù)據(jù)。這樣模型拿到返回結(jié)果后不需要在長列表里反復(fù)找重點(diǎn)可以直接基于摘要生成回復(fù)。如果直接把上百條文件清單全拋給模型不僅費(fèi) token還可能讓模型的回復(fù)變得冗長且抓不住重點(diǎn)。move_files方法的實(shí)現(xiàn)也類似區(qū)別在于它在移動(dòng)前會(huì)做一次目錄創(chuàng)建和路徑?jīng)_突檢查def move_files(params: Dict[str, Any]) - Dict[str, Any]: 按規(guī)則移動(dòng)文件支持 dry_run 預(yù)演 target_dir _normalize_path(params.get(directory, .)) mode params.get(mode, extension) # extension | date | keyword dry_run params.get(dry_run, False) keyword params.get(keyword, ) if not os.path.isdir(target_dir): return {success: False, error: f目錄不存在: {target_dir}} scan_result scan_directory({directory: target_dir}) if not scan_result[success]: return scan_result moved_count 0 plan [] for item in scan_result[files]: if mode extension: dest_dir os.path.join(target_dir, item[ext].lstrip(.) or no_ext) elif mode date: mtime os.path.getmtime(item[path]) date_str datetime.fromtimestamp(mtime).strftime(%Y-%m) dest_dir os.path.join(target_dir, date_str) elif mode keyword: if keyword not in item[name]: continue dest_dir os.path.join(target_dir, keyword) else: return {success: False, error: f不支持的分類模式: {mode}} dest_file os.path.join(dest_dir, item[name]) plan.append({from: item[path], to: dest_file}) if not dry_run and item[path] ! dest_file: os.makedirs(dest_dir, exist_okTrue) shutil.move(item[path], dest_file) moved_count 1 return {success: True, dry_run: dry_run, planned: len(plan), moved: moved_count, plan: plan[:50]}在methods/__init__.py里注冊(cè)from .main import scan_directory, move_files METHODS { scan_directory: scan_directory, move_files: move_files, }這樣模型就能看到兩個(gè)可調(diào)用的方法。我不把_normalize_path這種輔助函數(shù)注冊(cè)進(jìn)去因?yàn)槟P蜎]有必要直接調(diào)用它暴露最小可用面是維護(hù)可調(diào)用接口的好習(xí)慣。5.4 本地驗(yàn)證與效果調(diào)優(yōu)寫完之后不要急著啟動(dòng)完整的對(duì)話引擎去測試我推薦先用一個(gè)簡單的本地驗(yàn)證腳本直接調(diào)用技能方法cd ~/openclaw/skills/organize_downloads python -c from methods import METHODS; print(METHODS[scan_directory]({directory: /tmp/test_downloads}))先用dry_run模式跑一遍計(jì)劃預(yù)覽確認(rèn)分類邏輯符合預(yù)期再真正執(zhí)行移動(dòng)操作。我在第一次寫這個(gè)技能時(shí)就發(fā)現(xiàn)按擴(kuò)展名分類時(shí)把臨時(shí)文件.tmp也單獨(dú)歸了一類這些其實(shí)應(yīng)該直接忽略。所以我在move_files里加了個(gè)忽略列表IGNORE_EXTS {.tmp, .crdownload, .part}這一小步對(duì)實(shí)際體驗(yàn)的提升很大——用戶不會(huì)希望整理完文件夾后看到一堆沒用的臨時(shí)文件垃圾。技能的調(diào)優(yōu)是一個(gè)持續(xù)的過程建議你每用一段時(shí)間就看看它的執(zhí)行結(jié)果從失敗案例里反推元數(shù)據(jù)描述或代碼邏輯哪里需要修正。6. 常見問題與排查技巧實(shí)錄6.1 技能注冊(cè)了但模型就是不調(diào)用它這是最頻繁遇到的問題。技能被掃描成功了可對(duì)話時(shí)模型總是“無視”它。我的排查順序是在注冊(cè)表里確認(rèn)技能摘要確實(shí)被注入可以通過 OpenClaw 的診斷命令查看當(dāng)前加載了哪些技能如果這項(xiàng)技能根本不在列表里那就是掃描/解析階段出了問題。檢查description是否寫得足夠具體描述模糊是模型不調(diào)用的頭號(hào)原因?!疤幚砦募焙汀皰呙璨⒄砟夸洶盐募磾U(kuò)展名/日期移動(dòng)到子目錄”在模型眼中的可用性是兩個(gè)量級(jí)。檢查有沒有其他技能在描述上“搶占”了這個(gè)場景兩個(gè)技能描述重疊時(shí)模型可能會(huì)優(yōu)先選擇描述更詳細(xì)的那個(gè)。在對(duì)話里明確提到技能名看模型是否還是不用。如果明確提到都不用那基本可以斷定是權(quán)限或注入環(huán)節(jié)出問題了。我遇到過最隱蔽的一次是某個(gè)技能的元數(shù)據(jù)被我誤寫成了permission: admin而當(dāng)前運(yùn)行的 OpenClaw 進(jìn)程是沙箱模式系統(tǒng)直接把該技能標(biāo)記為“不可用”。這種權(quán)限不對(duì)稱的坑不做全量排查很難發(fā)現(xiàn)。6.2 模型的調(diào)用參數(shù)總是傳錯(cuò)當(dāng)模型把字符串傳給了整型參數(shù)或者漏掉了必填參數(shù)時(shí)先別急著罵模型笨。大概率是你的函數(shù)簽名寫得不夠“有引導(dǎo)性”。我舉一個(gè)真實(shí)改進(jìn)過的例子。原來我寫的方法是def move_files(params): mode params.get(mode, extension)參數(shù)說明完全缺失模型只能猜。改成這樣之后調(diào)用準(zhǔn)確率大幅上升def move_files(params: Dict[str, Any]) - Dict[str, Any]: mode 可選值: extension|date|keyword directory: 要整理的目錄絕對(duì)路徑 dry_run: 是否只預(yù)覽不執(zhí)行布爾值 keyword: 當(dāng) modekeyword 時(shí)必填 原因很簡單模型在生成調(diào)用時(shí)會(huì)參考函數(shù)簽名和 docstring 中的信息。你給的信息越結(jié)構(gòu)化它傳參就越準(zhǔn)確。我還建議在函數(shù)開頭加一段參數(shù)校驗(yàn)邏輯而不是直接假設(shè)參數(shù)都合法mode params.get(mode, extension) if mode not in (extension, date, keyword): return {success: False, error: fmode 必須是 extension/date/keyword收到: {mode}}一旦傳參錯(cuò)誤至少能得到一個(gè)明確的失敗原因而不是在運(yùn)行到一半時(shí)拋異常或者更糟——靜默地執(zhí)行了錯(cuò)誤的操作。6.3 技能執(zhí)行超時(shí)或卡死技能執(zhí)行超時(shí)通常有兩種原因一是方法本身做了耗時(shí)很長的操作比如遞歸掃描一個(gè)龐大的目錄樹、訪問網(wǎng)絡(luò)接口遲遲不返回二是死循環(huán)或等待用戶輸入。處理超時(shí)問題我的經(jīng)驗(yàn)是給技能方法設(shè)計(jì)明確的“最大掃描量”或超時(shí)閾值。比如我的scan_directory只返回前 100 個(gè)文件詳情并且用total_files作為總量估計(jì)而不是把幾萬個(gè)文件的所有信息都計(jì)算一遍。對(duì)網(wǎng)絡(luò)請(qǐng)求類技能我會(huì)顯式傳入超時(shí)參數(shù)requests.get(url, timeout10)別小看這行代碼。有一次我寫抓網(wǎng)頁的技能時(shí)忘了加超時(shí)某個(gè)站點(diǎn)響應(yīng)極慢整個(gè)技能卡了兩分多鐘期間對(duì)話完全被阻塞。加上超時(shí)后即使某個(gè)網(wǎng)站無法訪問技能也會(huì)在 10 秒內(nèi)返回錯(cuò)誤信息對(duì)話體驗(yàn)不會(huì)中斷。另一個(gè)思路是把重操作拆成多個(gè)輕量方法。用戶先調(diào)用scan_directory拿到文件清單再調(diào)用move_files執(zhí)行移動(dòng)而不是把一個(gè)“全自動(dòng)整理”塞進(jìn)一個(gè)方法里。這樣每個(gè)方法的執(zhí)行時(shí)間都在可接受范圍模型也更容易中途介入確認(rèn)而不是被動(dòng)等待一個(gè)黑盒任務(wù)跑完。6.4 上下文污染技能返回結(jié)果把對(duì)話帶偏技能執(zhí)行完了返回結(jié)果也進(jìn)上下文了但模型的后續(xù)回復(fù)質(zhì)量急劇下降——比如開始用文件的絕對(duì)路徑來回復(fù)用戶或者在無關(guān)話題里反復(fù)提到剛才處理的文件名。這就是上下文污染。控制上下文污染有幾個(gè)實(shí)用技巧第一精簡返回結(jié)果的大小。scan_directory返回前 100 個(gè)文件的詳情是我故意限制的——因?yàn)槌^這個(gè)量模型大概率不會(huì)用反而會(huì)被無意義的長列表干擾。你可以在摘要里放關(guān)鍵統(tǒng)計(jì)數(shù)字文件總數(shù)、類型分布、目錄大小把大數(shù)據(jù)量結(jié)果以文件形式落盤并在返回結(jié)果里附上文件路徑讓模型知道“完整清單在那邊”。第二返回結(jié)果里加上“此結(jié)果僅供操作參考不要在回復(fù)中逐條羅列”這類指令。OpenClaw 支持在技能返回文本中附帶面向模型的指令相當(dāng)于給結(jié)果“貼個(gè)標(biāo)簽”明確告訴模型應(yīng)該怎么使用這些信息。這個(gè)技巧對(duì)抑制“倒豆子式回復(fù)”非常有效。第三技能完成后主動(dòng)收尾。在任務(wù)執(zhí)行鏈的最后一步調(diào)用一個(gè)類似于“任務(wù)完成狀態(tài)確認(rèn)”的輕量方法返回一個(gè)干凈的結(jié)論文本覆蓋掉此前可能堆積的大量過程信息。就像 6.2 里的參數(shù)校驗(yàn)失敗返回也會(huì)輸出干凈的 error 信息而不是讓模型面對(duì)一個(gè)異常堆棧自己想辦法。寫到這里我想起一個(gè)常在 OpenClaw 社區(qū)里被討論的現(xiàn)象很多開發(fā)者過早地去學(xué)習(xí)大規(guī)模技能編排、多技能協(xié)同、動(dòng)態(tài)裝載這些進(jìn)階概念結(jié)果反而在基礎(chǔ)技能觸發(fā)、參數(shù)傳遞這類環(huán)節(jié)反復(fù)受挫。Skills 系統(tǒng)真正的學(xué)習(xí)路徑是先把一個(gè)技能做扎實(shí)——從場景定義、元數(shù)據(jù)描述、方法實(shí)現(xiàn)到執(zhí)行驗(yàn)證跑通一遍有了這個(gè)完整的感知后面的擴(kuò)展都會(huì)水到渠成。我自己也是被“技能不觸發(fā)”折磨了幾天之后才開始意識(shí)到元數(shù)據(jù)描述和權(quán)限聲明這些看似簡單的字段才是整個(gè)架構(gòu)里決定成敗的細(xì)節(jié)。