實戰(zhàn):用Python為智能體打造工具技能)
最近群里聊 OpenClaw 的人越來越多了大家都管它叫“龍蝦”而這套東西最讓人上頭的點就是 Skill 機制。簡單說OpenClaw 是一個偏執(zhí)行側(cè)的智能體框架模型負責“想”Skill 負責“做”。框架本身會帶一些默認技能可真到自己的業(yè)務(wù)場景里你會發(fā)現(xiàn)大部分時候都得自己動手寫 Skill。用 Python 給 OpenClaw 寫自定義 Skill是當前門檻最低、生態(tài)最順手的一條路——Python 社區(qū)里輪子多你只要把一個業(yè)務(wù)功能封裝成 Skill 的標準格式Agent 就能在對話里自動調(diào)用它。這篇文章不是給你念官方文檔而是按照我自己從零摸一遍的經(jīng)驗走先拆 Skill 的運行邏輯再完整寫一個能落地的系統(tǒng)信息采集技能最后聊注冊、調(diào)試和踩坑。如果你正準備給 OpenClaw 加技能或者只是好奇這種“模型 工具插件”的模式怎么玩這篇文章應(yīng)該能幫你少走不少彎路。1. Skill機制拆解龍蝦的“技能插件”到底是怎么跑的1.1 Skill的本質(zhì)一個目錄、一份元信息和一段執(zhí)行邏輯很多人第一次看到 OpenClaw 的 Skill 目錄會懵一個文件夾里放一個.py文件、一個.yaml文件這算什么“技能”其實它的設(shè)計思路跟 IDE 插件、瀏覽器擴展很像。每個 Skill 就是一個自包含的模塊向 Agent 暴露兩樣東西元信息告訴 Agent“我會什么、需要什么參數(shù)”執(zhí)行函數(shù)告訴 Agent“你調(diào)我之后我能具體做什么”。我在實際使用中習慣把 Skill 看作“給大模型配的一把專用工具”。模型本身的強項是語義理解和生成弱項是執(zhí)行確定性的計算、讀本機狀態(tài)、調(diào)領(lǐng)域接口。Skill 正好補上這塊。比如你問模型“看看這臺機器內(nèi)存剩多少”模型如果只靠訓練知識它根本不知道你這個環(huán)境里真實的內(nèi)存占用但如果你掛了一個system_info技能模型就會決定調(diào)用它把工具返回的真實數(shù)據(jù)整理成回答。所以理解 Skill 不能只看代碼要把它放進 Agent 的工作鏈路里看用戶請求進來模型判斷需要外部能力通過元信息匹配到合適的 Skill再按參數(shù)約定觸發(fā)執(zhí)行函數(shù)拿到返回值后繼續(xù)組織語言。這里面的核心設(shè)計點就是元信息能不能讓模型“一眼看懂”。很多新手寫 Skill 只顧著寫實現(xiàn)結(jié)果模型根本不知道什么時候該調(diào)問題就出在元信息寫得太抽象。1.2 開發(fā)前的環(huán)境準備動手寫 Skill 之前先把環(huán)境捋清楚。我的建議是不要直接往系統(tǒng) Python 里裝東西而是為 OpenClaw 單獨準備一個虛擬環(huán)境。原因很簡單OpenClaw 本身依賴不少庫你寫 Skill 時還會加一些自己的依賴如果全堆在系統(tǒng)環(huán)境里沒過多久就會出現(xiàn)“裝了這個包那個包被降級”的連鎖反應(yīng)?;A(chǔ)環(huán)境有這么幾樣Python 3.8 或更高版本。OpenClaw 生態(tài)目前對 3.10/3.11 的支持最穩(wěn)如果你還沒裝 Python直接裝 3.11 就行。OpenClaw 本體。安裝方式去官方倉庫看就好不同版本的安裝命令會變我不建議死記硬背命令重點是把它的skills目錄找到。一個你順手的編輯器。VSCode 或者任意文本編輯器都行Skill 本質(zhì)上就是一個目錄里的幾個文件不需要重型 IDE。代碼版本管理工具。哪怕只有你自己開發(fā)也建議建一個 git 倉庫Skill 的迭代速度比你想象中快。裝完以后先別急著寫代碼在命令行里跑一下 OpenClaw 自帶的示例 Skill確認“框架主流程”是通的。這一步特別重要因為它把“框架問題”和“你的代碼問題”隔離開如果示例 Skill 都跑不通那就是環(huán)境問題如果示例能跑通而你的 Skill 不行那問題多半出在自己寫的代碼里。1.3 命名與設(shè)計原則在我看過的二三十個 Skill 插件里最影響使用體驗的不是代碼寫得好不好而是命令命名。OpenClaw 里模型的調(diào)用決策依賴元信息里的name和description如果你的技能叫data_processor描述寫“一個數(shù)據(jù)處理模塊”模型面對用戶問題“幫我把這段文字里的電話號碼都提取出來”很難確定是不是該調(diào)你這個技能。我自己定了幾條命名規(guī)范供你參考技能名用動詞開頭比如fetch_weather、send_email、check_system讓模型一眼看出動作。描述里寫清楚“什么場景用、輸入什么、輸出什么”不要寫空話。比如描述寫“當用戶想查看本機CPU/內(nèi)存/磁盤使用情況時使用此技能獲取系統(tǒng)信息并返回結(jié)構(gòu)化數(shù)據(jù)”模型命中率會高很多。一個 Skill 只做一件事。有些朋友喜歡寫一個大而全的 Skill里面有十幾個函數(shù)看似方便但模型在調(diào)用時會很猶豫參數(shù)也容易傳錯。寧可多做幾個小 Skill也別憋一個大怪物。2. 手把手寫第一個Python Skill2.1 場景選擇先做一個系統(tǒng)信息采集技能我在給新手建議時從來不讓他們上來就寫“調(diào)用外部 API”或者“操作數(shù)據(jù)庫”這種帶網(wǎng)絡(luò)和服務(wù)依賴的技能而推薦先寫一個能“自我感知”的技能。這里我就用系統(tǒng)信息采集做例子它有幾個好處只用標準庫就能跑通最小版本方便確認 Skill 機制本身沒問題返回結(jié)果是真實數(shù)據(jù)調(diào)試時有明確預(yù)期后續(xù)想加 psutil 增強版也順理成章。我們這個技能要實現(xiàn)的能力是當用戶問“這臺電腦什么系統(tǒng)”“內(nèi)存夠不夠”“磁盤還剩多少”時Agent 能調(diào)用一個 Python Skill返回 JSON 格式的系統(tǒng)信息。為了讓過程可感我會先實現(xiàn)一個“標準庫版”再升級成“psutil 增強版”這樣你能看出來一個技能是怎么從零到一演進的。2.2 目錄與元信息文件怎么寫先看看 Skill 的標準目錄結(jié)構(gòu)。在 OpenClaw 的skills目錄下建一個文件夾名字就是技能名里面放代碼和元信息skills/ └── system_info/ ├── skill.py └── skill.yamlskill.yaml是模型感知 Skill 的第一入口相當于“技能說明書”。我一般會這樣寫name: system_info version: 1.0.0 description: 獲取本機操作系統(tǒng)、CPU、內(nèi)存、磁盤等基礎(chǔ)信息。當用戶詢問系統(tǒng)版本、內(nèi)存占用、磁盤剩余空間時使用。 python: 3.8 parameters: - name: detail type: boolean required: false default: false description: 是否輸出詳細的CPU占用率和內(nèi)存/磁盤使用情況這里有幾個細節(jié)容易踩坑。第一個是description別寫太短寫清楚觸發(fā)條件模型才能正確決策第二個是parameters里的type一定要和代碼里的邏輯對應(yīng)否則模型會傳錯類型第三個是version字段我要求自己每改一次就升一個小版本這樣出問題時能排查是不是緩存了舊版本。2.3 Skill代碼標準庫版本與psutil增強版skill.py部分我一般會定義一個Skill類然后實現(xiàn)run方法。OpenClaw 各個小版本的調(diào)用約定可能略有差異但“定義類、實現(xiàn)run()”這個模式在多數(shù) Agent 框架里是通用的你只需要把函數(shù)名對牢自己用的版本就行。先看最簡單的版本import platform import json import socket class Skill: name system_info version 1.0.0 description 獲取本機操作系統(tǒng)、CPU、內(nèi)存、磁盤等基礎(chǔ)信息 def run(self, **kwargs): detail kwargs.get(detail, False) info { os: platform.system(), os_version: platform.version(), machine: platform.machine(), hostname: socket.gethostname(), python_version: platform.python_version(), } if detail: try: import psutil info[cpu_usage_percent] psutil.cpu_percent(interval1) memory psutil.virtual_memory() info[memory] { total_gb: round(memory.total / 1024 ** 3, 2), available_gb: round(memory.available / 1024 ** 3, 2), usage_percent: memory.percent, } disk psutil.disk_usage(/) info[disk] { total_gb: round(disk.total / 1024 ** 3, 2), free_gb: round(disk.free / 1024 ** 3, 2), usage_percent: disk.percent, } except ImportError: info[warning] psutil not installed, only basic information returned return json.dumps(info, ensure_asciiFalse)我解釋幾個設(shè)計決策。第一detail參數(shù)默認是False但psutil是懶加載的也就是說只有用戶明確要求詳細信息時這個庫才會被導入。這么做是為了讓基礎(chǔ)調(diào)用不依賴第三方庫降低失敗率。第二返回值我用json.dumps(..., ensure_asciiFalse)確保中文不會被轉(zhuǎn)成\u開頭的一串轉(zhuǎn)義符否則在 Agent 端看起來非常不直覺。第三interval1是cpu_percent的參數(shù)表示采樣一秒能拿到一個相對真實的瞬時 CPU 使用率而不是 0.0 這種無意義值。2.4 參數(shù)傳遞與返回值約定寫 Skill 的時候最怕框架約定的輸入輸出接口和你自己寫的函數(shù)對不上。我強烈的建議是統(tǒng)一用**kwargs接收參數(shù)統(tǒng)一返回 JSON 字符串。拿**kwargs的原因在于 Skill 可能被框架用多種方式調(diào)用有的版本會傳入模型填好的命名參數(shù)有的會傳一組字典你用kwargs.get(detail)取值無論哪種都能應(yīng)對。返回值那里也容易迷糊。有些框架希望run返回字符串因為字符串可以直接塞回 Agent 上下文有些框架希望返回 dict框架自己幫你序列化。你沒法保證所有版本一致時就按項目文檔來。我的默認選擇是返回字符串因為它的兼容面最廣而且可以在返回前手動控制序列化過程。還要注意一個設(shè)計細節(jié)返回內(nèi)容不要讓 Agent“看不懂”。比如返回一個套娃式的嵌套 JSON模型解析起來費勁回答自然容易出錯。我習慣把數(shù)據(jù)打平到兩級以內(nèi)像上面memory、disk這種對象里包含基礎(chǔ)字段就是夠用的深度。3. 把Skill裝進OpenClaw并完成本地聯(lián)調(diào)3.1 安裝路徑與自動發(fā)現(xiàn)寫完了代碼和元信息接下來就是把 Skill 放進 OpenClaw 能掃到的地方。不同版本的 OpenClaw 對 Skill 目錄的掃描路徑不完全相同但大體邏輯沒變有一個根目錄叫skills下一級每個子目錄代表一個 Skill框架啟動時遍歷這些目錄讀取skill.yaml并把skill.py里的類實例化注冊到技能庫里。我用過一個笨但有效的辦法來確認路徑對不對先在skills目錄下建一個空目錄再在里面放一個只返回ping的最小 Skill啟動 OpenClaw 看日志里有沒有出現(xiàn)這個技能名。如果日志里都找不到先查目錄層級很常見的問題是有人把system_info.py直接扔在skills下面而不是放到skills/system_info/skill.py。另外如果你用了配置文件來管理技能白名單記得在配置里把system_info加進啟用列表。這一步在不同框架版本里差異挺大有的默認全量加載有的默認只加載顯式聲明的技能??慈罩咀钪苯蛹虞d失敗時通常會有 “failed to load skill” 之類的報錯跟著提示改就好。3.2 用Ollama跑本地模型做端到端驗證OpenClaw 比較大的亮點之一就是可以接本地模型玩常見方案是 Ollama 部署一個開源模型比如 qwen 或者 llama 系列然后讓 OpenClaw 通過本地模型做推理。這樣整個鏈路不依賴外部服務(wù)適合在離線環(huán)境或本地開發(fā)機里反復(fù)驗證 Skill。我的端到端測試流程是這樣的先確保 Ollama 服務(wù)啟動然后在 OpenClaw 的模型配置里把地址指向本地 Ollama重啟框架。接著在對話里輸入一句自然語言比如“查一下這臺電腦的磁盤使用情況”。如果配置正常模型會自己判斷需要調(diào)用system_info技能然后你應(yīng)該能在日志里看到技能命中、參數(shù)填充、執(zhí)行結(jié)果返回這幾個階段的記錄。這里有個常見現(xiàn)象本地小模型在參數(shù)填充上不如大模型準確它可能會漏掉detail參數(shù)或者把布爾值傳成字符串“True”。所以在設(shè)計 Skill 時參數(shù)盡量給默認值并且代碼里要有容錯。比如把kwargs.get(detail, False)改成能兼容字符串真值的寫法可以寫成detail str(kwargs.get(detail, False)).lower() in (true, 1, yes)這樣模型再怎么傳錯你的代碼也不會崩。3.3 調(diào)試三板斧單測入口、日志隔離、JSON輸出檢查在把 Skill 交給 OpenClaw 之前我建議你把它當作普通 Python 模塊先單獨測試一遍。我以前直接往 Agent 里調(diào)試一報錯就掙扎半天分不清是我代碼的問題還是框架調(diào)用的問題。后來養(yǎng)成了習慣每個skill.py底部都留一個if __name__ __main__:入口里邊調(diào)用Skill().run()先當作腳本跑。調(diào)試時的三條經(jīng)驗單測入口的模擬要貼近框架傳參。比如框架傳參數(shù)可能是字典你就在入口里構(gòu)造一個字典傳進去不要直接用函數(shù)默認參數(shù)測試。日志要能區(qū)分“技能執(zhí)行日志”和“模型思考日志”。我的做法是在 Skill 代碼里只往 stdout 輸出最終結(jié)果任何中間日志一律寫到文件或 stderr避免污染返回內(nèi)容。JSON 輸出要做一次合法性檢查。寫完json.dumps之后再用json.loads讀回來確認沒有因為變量類型問題導致序列化失敗。這一步可以作為“金標準”在命令行能正確輸出 JSON再接 OpenClaw 聯(lián)調(diào)。我見過太多人跳過了這個步驟直接在 Agent 里報錯最后排查一圈發(fā)現(xiàn)就是 Python 序列化時遇到set對象這種鍋不該讓 Agent 背。4. 常見問題與排查實錄4.1 高頻報錯速查表把我在實際使用中遇到過的、以及在開發(fā)群里看別人踩過的坑整理一下做成一個速查表可以收藏備用。現(xiàn)象可能原因處理辦法框架日志里沒有 Skill 加載記錄目錄層級不對、技能未啟用、元信息格式錯誤檢查skills/技能名/skill.py結(jié)構(gòu)確認配置里已啟用Agent 一直不選擇調(diào)用技能元信息描述不具體模型無法判斷觸發(fā)場景重寫description增加觸發(fā)條件和示例場景調(diào)用后返回內(nèi)容為空執(zhí)行發(fā)生異常但異常信息被吞掉在run里給執(zhí)行邏輯包try/except打印堆棧到日志ModuleNotFoundErrorSkill 代碼引用了未安裝的依賴在 OpenClaw 的 Python 環(huán)境里安裝依賴或用懶加載邏輯中文輸出亂碼Windows 下控制臺編碼與 Python 默認編碼不一致設(shè)置PYTHONIOENCODINGutf-8返回 JSON 時指定ensure_asciiFalse技能返回了但 Agent 說沒結(jié)果返回內(nèi)容里混了日志輸出結(jié)構(gòu)不符合模型預(yù)期檢查 stdout 是否只有 JSON格式確保能被json.loads解析這個表看起來簡單卻是我屢次“懷璧其罪”的總結(jié)。尤其是第一行新手十有八九會折在目錄結(jié)構(gòu)上不是你代碼寫錯是文件放錯位置。4.2 三個容易被忽略的坑第一個坑是Skill 執(zhí)行環(huán)境的 Python 和 OpenClaw 的 Python 不是同一個。如果你用系統(tǒng)自帶的 Python 跑了pip install psutil但 OpenClaw 跑在虛擬環(huán)境里那 Skill 照樣找不到模塊。排查方法是啟動前在配置里打印sys.executable確認到底走的哪個解釋器。第二個坑是緩存。有一些版本會緩存 Skill 信息你改了skill.py但它加載的還是舊代碼。我改代碼后必做的動作是清楚緩存目錄并重啟框架別省錢。第三個坑是權(quán)限問題。不是所有環(huán)境都允許你讀取完整系統(tǒng)信息尤其在 Linux 容器里磁盤統(tǒng)計可能只有掛載點的一部分。這種問題在 Python 層沒有解只能在 Skill 輸出里加一個warning字段保證 Agent 不會拿到 false 數(shù)據(jù)。4.3 Windows和Linux部署差異開發(fā)環(huán)境如果是 Windows到了 Linux 服務(wù)器上跑有兩點要提前處理。第一是路徑分隔符skills目錄掃描在 Windows 下用反斜杠Linux 下用正斜杠代碼里不要硬編碼任何路徑盡量用pathlib去拼。第二是權(quán)限模型Linux 下讀取磁盤信息和進程信息有時需要 sudo所以如果你的 Skill 里有這些操作要提前想好運行 OpenClaw 的服務(wù)賬號權(quán)限。Windows 上還有一種常見坑就是 PowerShell 的默認編碼和 Python 輸出的 UTF-8 不兼容。我自己碰到過腳本單獨運行沒問題接入 OpenClaw 后中文信息全變成亂碼。后來在啟動腳本里加了環(huán)境變量才解決。這個問題不算復(fù)雜但一旦碰上會浪費一下午在這里提個醒。5. 我的建議與擴展方向5.1 新手的入場順序如果讓我給新手排一個開發(fā)路線我的建議是這樣的先寫一個不帶參數(shù)、只返回“hello world”的最小 Skill跑通注冊和調(diào)用鏈路然后給這個最小 Skill 加一個參數(shù)體驗一下參數(shù)傳遞如何影響輸出再把它替換成有實際價值的系統(tǒng)信息采集技能體驗完整業(yè)務(wù)閉環(huán)最后再去嘗試 API 類、數(shù)據(jù)庫類、文件操作類的復(fù)雜技能。這條路徑看起來慢其實最快。因為你每一步都只增加一個變量出了問題能立刻定位。反觀一上來就想寫個大而全的電商助手、GIS 分析助手結(jié)局往往是卡在環(huán)境問題上連“我的代碼到底有沒有被執(zhí)行”都不知道。5.2 值得擴展的方向Skill 這個機制本身是中性的任何領(lǐng)域能力都可以封裝成技能。我目前看到社區(qū)里有人在寫 GIS 空間分析技能把常用空間操作包裝成 Skill 給 Agent 調(diào)用也有做電商場景的把商品查詢、價格計算做成技能ROS2 方向也有人在做嘗試想通過 Skill 讓 Agent 間接控制機器人仿真環(huán)境。這些嘗試的共同點就是把原本需要手動寫腳本的領(lǐng)域邏輯變成模型可以按需調(diào)用的“工具”。往深了做還有兩個方向值得關(guān)注。一個是多技能協(xié)作比如先調(diào)用系統(tǒng)信息技能拿到本機狀態(tài)再根據(jù)狀態(tài)決定要不要調(diào)用另一個清理技能這種“技能編排”會極大豐富 Agent 的行為能力。另一個是技能參數(shù)復(fù)雜化讓 Skill 支持結(jié)構(gòu)化參數(shù)對象而不是一層的鍵值對這能讓技能適應(yīng)更復(fù)雜的業(yè)務(wù)輸入。最后分享一個小技巧調(diào)試 Skill 時一定要讓 Agent 肉眼可見地拿到結(jié)果。我的做法是先在單獨測試入口里把返回的 JSON 字符串寫到一個臨時文件確認文件內(nèi)容完全正確再接 OpenClaw 聯(lián)調(diào)。等這一步穩(wěn)了再去做參數(shù)變形和異常分支。按這個順序走我基本沒再遇到過“卡在兩套環(huán)境之間”的難題希望你也能一次跑通。