核心要點
- OpenClaw Skills 是以 skill.md 為規範核心的模組化能力系統,目前官方商店已收錄逾 100 個技能,涵蓋瀏覽器自動化、檔案系統、行事曆整合、Shell 執行等主要類別。
- skill.md 採用 YAML 前置資料加 Markdown 說明的混合格式,透過
capabilities、permissions與inputs三個關鍵欄位,讓 OpenClaw 代理在執行前即能完整理解技能邊界。 - BlogWatcher 與 Supermemory 是社群使用率最高的兩大 Skill:前者負責網路內容定期監控,後者則提供跨工作階段的長期記憶向量存儲。
- 《The Register》2026 年 2 月的報導揭露,部分社群提交的 Skills 在日誌輸出中洩露 API 金鑰,安全團隊應對輸入驗證與密鑰管理訂定強制規範。[4]
- 企業部署建議建立私有 Skill 登錄中心(Private Skill Registry),並搭配審批工作流程與版本鎖定機制,確保生產環境的穩定性與合規性。
OpenClaw 自 2025 年末以「開源超級代理」之姿進入公眾視野,迅速從小眾開發者工具演變為企業級 AI 自動化平台的有力競爭者。[5] 其成功的關鍵之一,在於設計了一套兼具靈活性與安全性的技能(Skill)擴展系統,讓任何開發者都能以標準化方式為代理新增能力,而無需修改核心程式碼。[6]
本文將以開發者視角,深入剖析 OpenClaw Skills 的完整生命週期:從理解 skill.md 規範語法、逐步建立第一個自定義技能、研究熱門官方 Skills 的實作模式,到安全地發佈至 Skills 商店,以及在企業環境中管理私有 Skill 生態系統。無論您是希望為團隊內部打造專屬自動化工具的工程師,還是有意將 Skill 商業化的獨立開發者,本指南都將提供可直接應用的技術知識。[1]
一、Skills 生態系統概覽
1.1 什麼是 OpenClaw Skill
在 OpenClaw 架構中,Skill 是一個自包含的能力模組,它透過標準化介面告訴代理「我能做什麼」以及「我需要什麼資源才能做」。不同於傳統外掛系統需要宿主程式掛載特定 API,OpenClaw Skills 採用宣告式設計:開發者在 skill.md 檔案中以人類可讀的格式描述技能能力,OpenClaw 核心引擎在規劃任務時會動態讀取這些描述,判斷何時應調用哪個技能。[2]
這種設計帶來了兩個重要優勢:其一,代理能在執行前就理解每個技能的能力邊界,避免試誤式調用;其二,人類可以直接閱讀 skill.md 審查技能行為,無需反向工程程式碼。這對企業合規審查尤為重要。
每個 Skill 在文件系統中以一個獨立目錄呈現,結構如下:
my-skill/
├── skill.md # 技能規範(必要)
├── index.js # 主邏輯入口(必要)
├── package.json # npm 依賴宣告(選用)
├── tests/ # 單元測試目錄(建議)
│ └── skill.test.js
└── README.md # 額外文件(選用)
1.2 官方 Skills 商店現況
截至 2026 年 2 月,OpenClaw 官方 Skills 商店(Skills Marketplace)已收錄超過 100 個技能,由官方核心團隊、驗證合作夥伴與社群開發者共同貢獻。[3] 按功能類別分佈如下:
- 瀏覽器自動化(Browser Automation):約 22 個,包含網頁擷取、表單填寫、截圖、多分頁管理等
- 檔案系統操作(File System):約 15 個,涵蓋讀寫、搜尋、壓縮、格式轉換
- 通訊與訊息(Messaging):約 18 個,整合 Slack、Discord、Email、Telegram 等平台;send message 類技能在此類別中最為豐富
- 行事曆與排程(Calendar & Scheduling):約 10 個,支援 Google Calendar、Outlook、iCal 格式
- Shell 與系統執行(Shell Execution):約 8 個,提供受控的命令列執行環境
- 資料與 API(Data & API):約 14 個,含 REST 客戶端、GraphQL、資料庫連接器
- AI 增強(AI Enhancement):約 13 個,包括 Supermemory 長期記憶、向量搜尋、RAG 管線等
- 監控與通知(Monitoring & Alerting):約 8 個,以 BlogWatcher 為代表的內容監控技能
此外,每月新增 Skill 數量呈上升趨勢,社群生態的活躍度已成為 OpenClaw 在競品中脫穎而出的重要因素。
1.3 Skill 的調用機制
當使用者向 OpenClaw 提出請求時,代理會執行以下流程:首先掃描所有已安裝 Skills 的 skill.md,建立能力索引;接著根據任務語意比對最合適的 Skill;確認所需權限已獲使用者授權後,才實際執行 Skill 的 index.js 邏輯。整個過程對使用者近乎透明,但開發者理解這個流程對設計良好的技能描述至關重要。[9]
二、skill.md 規範詳解
2.1 整體結構
skill.md 採用 YAML 前置資料(frontmatter)加上 Markdown 正文的混合格式,其中 YAML 區塊是機器可讀的結構化元資料,Markdown 正文則是給 AI 模型和人類開發者閱讀的詳細說明。兩者缺一不可:YAML 決定技能的可發現性與執行邊界,Markdown 決定代理能否正確理解技能的使用時機與方式。[2]
以下是一個完整的 skill.md 結構範例:
---
name: my-custom-skill
version: 1.2.0
description: "一句話描述此技能的核心功能"
author: your-username
license: MIT
capabilities:
- id: fetch-webpage
description: "擷取指定 URL 的網頁內容"
- id: extract-text
description: "從 HTML 中提取純文字段落"
permissions:
network: true
filesystem: false
shell: false
env:
- MY_API_KEY
inputs:
- name: url
type: string
required: true
description: "目標網頁的完整 URL"
- name: selector
type: string
required: false
default: "body"
description: "CSS 選擇器,用於指定擷取範圍"
outputs:
- name: text
type: string
description: "擷取到的純文字內容"
- name: wordCount
type: number
description: "文字字數統計"
tags:
- browser
- scraping
- text-extraction
minOpenClawVersion: "2.1.0"
---
## 技能說明
此技能用於從指定網頁擷取文字內容,適用於需要快速取得網頁資訊、
進行內容分析或建立資料管線的場景。
## 使用範例
請幫我擷取 https://example.com 的主要內文,並統計字數。
## 注意事項
- 不支援需要登入驗證的頁面
- JavaScript 動態渲染的內容可能無法完整擷取
- 請遵守目標網站的 robots.txt 規範
2.2 必要欄位詳解
name:技能的唯一識別名稱,採用 kebab-case 格式(如 blog-watcher)。在同一個 OpenClaw 實例中,名稱不得重複。發佈至商店時,名稱將加上作者前綴,如 @username/blog-watcher。
version:遵循語意化版本規範(Semantic Versioning),格式為 MAJOR.MINOR.PATCH。主版本號變更表示破壞性 API 變更,次版本號表示新增功能,修訂號表示錯誤修正。
capabilities:這是 skill.md 中最關鍵的欄位,直接決定 OpenClaw 代理能否在適當時機調用此技能。每個 capability 需要一個 id(機器可讀的識別符)和一個 description(自然語言描述,供 AI 推理使用)。描述應盡量具體,避免使用模糊詞彙如「處理」或「管理」,改用動詞加受詞的結構,如「發送 Slack 訊息到指定頻道」。
permissions:宣告技能執行所需的系統資源存取權限。OpenClaw 採用最小權限原則,只有在此處宣告的權限才能被執行。主要權限類型包括:
network: true/false:是否可發起外部網路請求filesystem: true/false:是否可讀寫本機檔案系統shell: true/false:是否可執行系統命令(高風險,需特別審查)env: [...]:允許讀取的環境變數名稱清單clipboard: true/false:是否可存取剪貼簿
2.3 輸入輸出規範
inputs 欄位定義技能接受的參數,每個參數包含:name(參數名)、type(資料型別:string、number、boolean、array、object)、required(是否必填)、default(預設值)和 description(說明)。清晰的 inputs 定義能讓代理自動從對話中萃取參數,減少使用者需要明確指定參數的次數。
outputs 欄位描述技能回傳的資料結構,幫助代理在規劃多步驟任務時,能夠將此技能的輸出正確傳遞給下一個步驟。
2.4 版本相容性管理
minOpenClawVersion 欄位指定技能所需的最低 OpenClaw 版本,這對使用了較新 API 功能的技能尤其重要。若使用者的 OpenClaw 版本不符合要求,系統將在安裝前提示警告,而非讓技能在執行時靜默失敗。
三、從零建立第一個自定義 Skill
3.1 環境準備
開始開發前,確認您的環境已安裝 OpenClaw 2.1.0 以上版本以及 Node.js 20.0.0 以上版本。OpenClaw 提供官方 CLI 工具輔助技能開發:[9]
# 安裝 OpenClaw CLI(若尚未安裝)
npm install -g @openclaw/cli
# 確認版本
openclaw --version
# 啟用技能開發模式
openclaw skills dev-mode enable
3.2 建立技能目錄
本教學將建立一個「每日天氣摘要」技能(daily-weather),它能根據使用者指定的城市,擷取當日天氣資訊並產生簡短摘要。
# 使用官方腳手架建立技能骨架
openclaw skills create daily-weather
# 此命令會建立以下結構:
# daily-weather/
# ├── skill.md
# ├── index.js
# ├── package.json
# └── tests/
# └── skill.test.js
cd daily-weather
3.3 撰寫 skill.md
開啟自動生成的 skill.md,填入完整的技能規範:
---
name: daily-weather
version: 1.0.0
description: "擷取指定城市的當日天氣資訊,並生成人類可讀的天氣摘要報告"
author: your-username
license: MIT
capabilities:
- id: get-weather-summary
description: "取得指定城市今天的天氣狀況,包含溫度、濕度、降雨機率與風速"
- id: format-weather-report
description: "將天氣數據格式化為自然語言摘要,適合在對話中直接回報"
permissions:
network: true
filesystem: false
shell: false
env:
- OPENWEATHER_API_KEY
inputs:
- name: city
type: string
required: true
description: "城市名稱,支援中文或英文,如『台北』或『Taipei』"
- name: language
type: string
required: false
default: "zh-TW"
description: "摘要語言代碼,預設為繁體中文"
- name: units
type: string
required: false
default: "metric"
description: "溫度單位,metric(攝氏)或 imperial(華氏)"
outputs:
- name: summary
type: string
description: "天氣摘要文字,適合直接在對話中呈現"
- name: rawData
type: object
description: "原始天氣 API 回應資料"
- name: city
type: string
description: "API 確認的城市標準名稱"
tags:
- weather
- data-fetching
- productivity
minOpenClawVersion: "2.1.0"
---
## 技能說明
daily-weather 技能能夠即時擷取全球主要城市的天氣資訊,並將技術性
的氣象數據轉換為自然語言摘要,方便直接在 OpenClaw 對話介面中閱讀。
## 使用範例
- 「今天台北天氣如何?」
- 「幫我查一下東京今天的氣象,用英文回答」
- 「新加坡現在幾度?濕度多少?」
## 前置需求
需要在環境變數中設定 `OPENWEATHER_API_KEY`。
可至 https://openweathermap.org/api 免費申請 API 金鑰。
## 注意事項
- 天氣資料每 10 分鐘更新一次
- 免費方案每分鐘限制 60 次 API 呼叫
- 部分偏遠地區城市可能無法取得精確資料
3.4 實作技能邏輯
接著在 index.js 中實作核心邏輯。OpenClaw Skills 的主程式必須匯出一個標準的非同步執行函式:
// index.js
import { SkillContext } from '@openclaw/skill-sdk';
/**
* @param {Object} inputs - 來自 skill.md 定義的輸入參數
* @param {SkillContext} context - OpenClaw 提供的執行上下文
* @returns {Promise<Object>} 符合 skill.md outputs 定義的回傳物件
*/
export async function execute(inputs, context) {
const { city, language = 'zh-TW', units = 'metric' } = inputs;
// 從安全的環境變數讀取 API 金鑰
// 注意:永遠不要在日誌中輸出 apiKey 的值
const apiKey = context.env.get('OPENWEATHER_API_KEY');
if (!apiKey) {
throw new Error(
'缺少必要環境變數 OPENWEATHER_API_KEY。' +
'請在 OpenClaw 設定中加入此環境變數。'
);
}
context.log.info(`正在查詢城市天氣:${city}`);
const url = new URL('https://api.openweathermap.org/data/2.5/weather');
url.searchParams.set('q', city);
url.searchParams.set('appid', apiKey);
url.searchParams.set('units', units);
url.searchParams.set('lang', language === 'zh-TW' ? 'zh_tw' : 'en');
const response = await context.fetch(url.toString());
if (!response.ok) {
if (response.status === 404) {
throw new Error(`找不到城市「${city}」的天氣資料,請確認城市名稱是否正確。`);
}
throw new Error(`天氣 API 請求失敗,狀態碼:${response.status}`);
}
const data = await response.json();
const tempUnit = units === 'metric' ? '°C' : '°F';
const summary = formatWeatherSummary(data, tempUnit, language);
return {
summary,
rawData: data,
city: data.name,
};
}
function formatWeatherSummary(data, tempUnit, language) {
const temp = Math.round(data.main.temp);
const feelsLike = Math.round(data.main.feels_like);
const humidity = data.main.humidity;
const description = data.weather[0].description;
const windSpeed = data.wind.speed;
if (language === 'zh-TW') {
return (
`${data.name} 今日天氣:${description}。` +
`氣溫 ${temp}${tempUnit},體感溫度 ${feelsLike}${tempUnit},` +
`濕度 ${humidity}%,風速 ${windSpeed} m/s。`
);
}
return (
`${data.name} today: ${description}. ` +
`Temperature ${temp}${tempUnit}, feels like ${feelsLike}${tempUnit}, ` +
`humidity ${humidity}%, wind speed ${windSpeed} m/s.`
);
}
3.5 本機測試
OpenClaw 提供技能沙盒測試環境,讓開發者在不影響實際代理工作流程的情況下驗證技能行為:
# 在技能目錄中執行本機測試
openclaw skills test ./daily-weather \
--input '{"city": "台北", "language": "zh-TW"}' \
--env OPENWEATHER_API_KEY=your_test_api_key
# 執行單元測試
cd daily-weather && npm test
# 啟動互動式除錯模式
openclaw skills debug ./daily-weather
確認測試通過後,將技能安裝至本機 OpenClaw 實例進行整合測試:
# 從本機路徑安裝技能(開發模式)
openclaw skills install ./daily-weather --dev
# 在 OpenClaw 對話中測試
# 輸入:「今天台北天氣怎麼樣?」
四、熱門官方 Skills 深度剖析
4.1 瀏覽器自動化 Skill
瀏覽器自動化類技能是 OpenClaw Skills 商店中下載量最高的類別,核心技能 @openclaw/browser 基於 Playwright 建構,提供完整的無頭瀏覽器控制能力。[1]
其 skill.md 宣告的主要 capabilities 包括:
navigate-to-url:開啟指定網址click-element:點擊頁面元素(支援 CSS 選擇器與自然語言描述)fill-form:填寫表單欄位take-screenshot:擷取頁面截圖extract-content:從頁面提取結構化內容wait-for-element:等待特定元素出現或消失execute-script:在頁面上下文中執行 JavaScript(需額外確認)
值得注意的是,瀏覽器 Skill 預設在每次任務結束後清除 cookies 與 session 資料,若需要保持登入狀態執行多步驟任務,需在呼叫時傳入 persistSession: true 參數,並明確了解相關安全含義。
4.2 BlogWatcher Skill
BlogWatcher(@openclaw/blog-watcher)是 OpenClaw 社群中最受歡迎的監控類技能之一,設計用途是定期追蹤指定網站或 RSS 來源的新內容,並在有新文章時觸發通知或後續動作。
BlogWatcher 的技術亮點在於它結合了 Scheduled Skill(定時執行)與 Event Emission(事件發射)兩種進階模式。其 skill.md 關鍵片段如下:
capabilities:
- id: watch-blog
description: "監控指定部落格或 RSS 來源,當有新文章發佈時觸發通知"
- id: list-watched-sources
description: "列出目前正在監控的所有來源及其最後檢查時間"
- id: unwatch-source
description: "停止監控指定的來源"
- id: get-latest-posts
description: "立即擷取指定來源的最新文章清單"
schedule:
type: interval
intervalMinutes: 30
capability: check-for-updates
events:
- id: new-post-detected
description: "當監控的來源出現新文章時發射此事件"
payload:
- name: sourceUrl
type: string
- name: postTitle
type: string
- name: postUrl
type: string
- name: publishedAt
type: string
BlogWatcher 的典型使用場景包括:競品動態追蹤、研究領域論文預印本監控、技術部落格更新通知,以及將新內容自動摘要後推送至 Slack 頻道。
4.3 Supermemory Skill
Supermemory(@openclaw/supermemory)解決了 AI 代理最根本的限制之一:上下文視窗的有限性。它透過向量資料庫為 OpenClaw 提供跨工作階段的長期記憶能力,讓代理能夠「記住」使用者過去告知的偏好、完成過的任務與學到的資訊。[1]
Supermemory 的 capabilities 設計非常精細:
remember-fact:主動儲存一條事實或使用者偏好recall-relevant:根據當前任務語意,召回最相關的記憶片段forget-memory:刪除指定的記憶條目(GDPR 合規必要功能)list-memories:列出所有儲存的記憶條目,供使用者審查search-memories:以關鍵字搜尋特定記憶
在技術實作上,Supermemory 使用本機嵌入模型(預設為 nomic-embed-text)對記憶內容進行向量化,儲存於 OpenClaw 資料目錄下的 SQLite + 向量索引中,確保所有記憶資料保持在本機,不上傳至任何外部服務。
4.4 Send Message Skills
OpenClaw Skills 商店中有一個重要的技能子類別:發送訊息(send message)類技能,這類技能讓代理能夠代表使用者跨平台發送通知與訊息。主要技能包括:
@openclaw/send-slack:支援發送訊息至 Slack 頻道或私訊,可附加 Block Kit 格式的結構化內容。其 capability 宣告為「發送 Slack 訊息到指定工作空間的頻道或用戶」。
@openclaw/send-email:整合 SMTP 或 SendGrid API,能夠發送純文字或 HTML 格式電子郵件。
@openclaw/send-telegram:透過 Telegram Bot API 發送訊息至個人或群組。
@openclaw/send-discord:支援 Discord Webhook 與 Bot API 兩種發送模式。
所有 send message 類技能都在 skill.md 中明確宣告 requiresUserConfirmation: true,代表代理在實際發送前必須向使用者確認,防止意外觸發大量訊息發送。
4.5 Shell Execution Skill
@openclaw/shell 是功能強大但需謹慎使用的系統執行技能,它允許代理在受控環境中執行 Shell 命令。由於此技能的 permissions.shell: true 宣告賦予了較高的系統存取權,OpenClaw 在安裝此技能時會顯示額外的安全警告,並要求使用者明確確認。[7]
Shell Skill 透過白名單機制限制可執行的命令類別,預設允許的命令包括:ls、cat、grep、find、git、npm、python3 等開發常用工具,而 rm -rf、sudo、網路配置命令等高風險指令則在預設配置中被封鎖。
五、進階 Skill 開發模式
5.1 有狀態技能(Stateful Skills)
大多數技能是無狀態的:每次執行相互獨立,不保留前次呼叫的資料。但某些場景需要技能在多次呼叫間維持狀態,例如 BlogWatcher 需要記錄「上次檢查到的最新文章」才能判斷是否有更新。
OpenClaw Skill SDK 提供 context.state API 實現安全的狀態持久化:
export async function execute(inputs, context) {
// 讀取前次儲存的狀態
const prevState = await context.state.get('lastCheck') || {
lastPostId: null,
lastCheckedAt: null,
};
// 執行邏輯...
const latestPosts = await fetchLatestPosts(inputs.sourceUrl);
const newPosts = latestPosts.filter(
post => post.id !== prevState.lastPostId
);
// 更新狀態(自動持久化到加密存儲)
await context.state.set('lastCheck', {
lastPostId: latestPosts[0]?.id,
lastCheckedAt: new Date().toISOString(),
});
return { newPosts };
}
狀態資料儲存在 OpenClaw 的加密資料目錄中,與技能名稱綁定,跨工作階段持久保存。
5.2 技能間通訊(Skill-to-Skill Communication)
進階應用場景中,一個技能可能需要呼叫另一個技能的能力。OpenClaw 提供 context.skills.invoke API 實現技能間協作:
export async function execute(inputs, context) {
// 使用瀏覽器技能擷取頁面
const pageContent = await context.skills.invoke(
'@openclaw/browser',
'extract-content',
{ url: inputs.targetUrl, selector: 'article' }
);
// 使用 Supermemory 技能儲存提取的資訊
await context.skills.invoke(
'@openclaw/supermemory',
'remember-fact',
{
content: pageContent.text,
tags: ['web-research', inputs.topic],
}
);
return { status: 'saved', contentLength: pageContent.text.length };
}
在 skill.md 中需要宣告依賴關係,確保使用者在安裝主技能時能得到提示:
dependencies:
skills:
- name: "@openclaw/browser"
minVersion: "3.0.0"
- name: "@openclaw/supermemory"
minVersion: "2.0.0"
5.3 定時技能(Scheduled Skills)
BlogWatcher 所代表的定時執行模式,讓技能能夠在背景週期性運行,而不需要使用者主動觸發。在 skill.md 中聲明排程設定:
schedule:
type: cron
expression: "0 9 * * 1-5" # 每個工作日上午 9 點
capability: generate-daily-report
timezone: "Asia/Taipei"
排程技能在 OpenClaw 後台守護進程中運行,即使使用者沒有開啟對話視窗,技能仍會在指定時間自動執行。執行結果會儲存至通知佇列,待使用者下次開啟 OpenClaw 時呈現。
5.4 非同步長任務技能
當技能需要執行耗時超過 30 秒的任務時,應採用非同步長任務模式,避免阻塞對話介面:
export async function execute(inputs, context) {
// 標記為長任務模式
context.setLongRunning(true);
// 回傳初始確認,讓使用者知道任務已啟動
context.sendProgressUpdate('已開始處理,預計需要 2-5 分鐘...');
// 執行長時間任務
const result = await processLargeDataset(inputs.dataPath, {
onProgress: (percent) => {
context.sendProgressUpdate(`處理進度:${percent}%`);
}
});
return { result, completedAt: new Date().toISOString() };
}
六、Skills 商店發佈流程
6.1 發佈前準備
在提交技能至 Skills 商店前,開發者需完成以下檢查清單:[3]
- 確認
skill.md所有必要欄位完整填寫 - 所有宣告的 capabilities 都有對應的程式碼實作
- 單元測試覆蓋率達到 80% 以上
- 無硬編碼(hardcoded)的 API 金鑰或密碼
- 所有外部依賴明確列於
package.json - README.md 包含安裝說明、使用範例與授權聲明
- 已通過 OpenClaw 官方安全掃描工具檢查
6.2 打包與提交
使用 OpenClaw CLI 打包技能並提交審查:
# 在技能目錄中執行打包
openclaw skills pack
# 這將生成 my-skill-v1.0.0.tgz 打包檔
# 執行官方安全掃描
openclaw skills audit
# 提交至商店(需要已驗證的開發者帳號)
openclaw skills publish \
--token $OPENCLAW_PUBLISH_TOKEN \
--visibility public
openclaw skills audit 會執行靜態代碼分析,重點檢查:環境變數是否可能被洩露至日誌、是否有未宣告的網路請求、是否存在已知的惡意代碼模式,以及 package.json 依賴是否有已知漏洞。
6.3 審查流程
提交後,技能將進入 OpenClaw 官方審查流程,通常需要 3-7 個工作日:
- 自動掃描(即時):安全漏洞、惡意代碼、依賴合規性
- 人工審查(1-3 天):技能功能描述的準確性、使用者體驗評估
- 沙盒測試(1-2 天):在隔離環境中實際執行技能,驗證行為與宣告一致
- 上架或退回:若審查通過,技能將發佈至商店;若有問題,退回開發者並附上詳細說明
6.4 版本更新機制
已發佈技能的版本更新遵循以下規則:修訂版本(PATCH)更新無需重新審查,直接發佈;次版本(MINOR)更新需要自動掃描通過後方可發佈;主版本(MAJOR)更新涉及 API 破壞性變更,需完整重新審查,且舊版本將在商店中標記為「已棄用」但保留 90 天,讓使用者有時間遷移。
# 發佈修訂版本更新
npm version patch
openclaw skills publish
# 發佈主版本更新(需在 skill.md 同步更新版本號)
npm version major
openclaw skills publish --breaking-change \
--migration-guide https://github.com/your/repo/MIGRATION.md
七、安全審查與最佳實踐
7.1 API 金鑰洩露風險
2026 年 2 月,《The Register》報導了一個嚴重的安全問題:部分在 Skills 商店上架的社群技能,在執行日誌中以明文輸出 API 金鑰,任何能存取 OpenClaw 日誌檔案的人都能輕易取得這些密鑰。[4] 這個問題暴露出技能開發者普遍缺乏安全意識的現況。
CrowdStrike 的安全研究報告進一步指出,部分惡意技能甚至透過在日誌或錯誤訊息中嵌入環境變數值,將 API 金鑰外洩至攻擊者控制的伺服器。[7] Cisco 的研究也強調,個人 AI 代理系統對 API 金鑰的依賴使其成為高價值攻擊目標。[10]
以下是造成洩露的典型反模式,開發者應避免:
// 危險:永遠不要這樣做
export async function execute(inputs, context) {
const apiKey = context.env.get('MY_API_KEY');
// 錯誤 1:在日誌中輸出金鑰
context.log.info(`使用 API 金鑰:${apiKey}`);
// 錯誤 2:在錯誤訊息中包含金鑰
throw new Error(`API 呼叫失敗,金鑰 ${apiKey} 無效`);
// 錯誤 3:將金鑰包含在回傳值中
return { status: 'ok', usedKey: apiKey };
}
正確的密鑰處理方式:
// 安全:正確的 API 金鑰處理方式
export async function execute(inputs, context) {
const apiKey = context.env.get('MY_API_KEY');
if (!apiKey) {
// 錯誤訊息中只說明「缺少」,不暴露任何金鑰資訊
throw new Error('缺少必要的環境變數 MY_API_KEY');
}
// 日誌只記錄金鑰的前 4 個字元(用於除錯識別)
const keyPrefix = apiKey.substring(0, 4) + '****';
context.log.info(`使用 API 金鑰:${keyPrefix}`);
// 實際使用時直接傳入,不儲存於任何可見位置
const result = await callExternalAPI(inputs.data, apiKey);
// 回傳值中絕不包含金鑰
return { status: 'ok', result };
}
7.2 輸入驗證
雖然 skill.md 定義了輸入的型別與必填性,但開發者不應依賴框架自動驗證,而應在 index.js 中實施主動的輸入驗證,防止注入攻擊與意外行為:
export async function execute(inputs, context) {
// 明確驗證 URL 格式
let targetUrl;
try {
targetUrl = new URL(inputs.url);
} catch {
throw new Error('無效的 URL 格式,請提供完整的 https:// 開頭 URL');
}
// 限制只允許 HTTPS
if (targetUrl.protocol !== 'https:') {
throw new Error('基於安全考量,僅支援 HTTPS 協議的 URL');
}
// 驗證字串長度,防止超大輸入
if (inputs.query && inputs.query.length > 1000) {
throw new Error('查詢字串過長,最多支援 1000 個字元');
}
// 繼續執行...
}
7.3 沙盒限制理解
OpenClaw 的技能沙盒並非完全隔離的容器環境,而是基於 Node.js 模組系統的軟隔離。這意味著惡意技能理論上仍能存取部分系統資源。開發者在評估第三方技能時,應優先選擇:通過官方審查的技能、擁有完整開源代碼的技能、在 GitHub 上有活躍維護記錄的技能,以及安裝量超過 1000 且有正面評價的技能。[7]
7.4 權限最小化原則
技能開發者應嚴格遵守最小權限原則,只宣告技能真正需要的權限:
- 若技能只讀取檔案,宣告
filesystem: read而非filesystem: true(等同 read-write) - 若技能只需要一個特定環境變數,只在
env清單中列出該變數 - 若技能不需要網路存取,明確宣告
network: false - 避免宣告
shell: true,除非絕對必要且有充分說明
八、企業內部 Skill 管理
8.1 私有 Skill 登錄中心
企業環境中,直接讓員工從公開商店安裝任意技能存在安全與合規風險。建議建立私有 Skill 登錄中心(Private Skill Registry),架構上可以使用 npm 私有登錄(如 Verdaccio 或 GitHub Packages)作為後端:[8]
# 在企業 OpenClaw 設定中指定私有登錄中心
# openclaw.config.json
{
"skillRegistry": {
"primary": "https://skills.internal.company.com",
"fallback": null,
"allowPublicMarketplace": false
},
"skillApproval": {
"required": true,
"approvers": ["[email protected]"]
}
}
將 allowPublicMarketplace 設為 false 後,所有 Skills 安裝請求都必須指向私有登錄中心,有效防止員工安裝未經審查的公開技能。
8.2 企業 Skill 審批工作流程
建立標準化的技能審批流程,確保每個進入企業環境的技能都經過安全評估:
- 申請階段:員工提交技能安裝申請,說明業務需求與技能來源
- 安全掃描:自動執行
openclaw skills audit並生成掃描報告 - 代碼審查:資安團隊審查技能的
skill.md與index.js,重點檢查權限宣告與外部呼叫 - 沙盒測試:在隔離的測試環境中實際執行技能,確認行為符合描述
- 發佈至私有登錄:審查通過後,將技能版本鎖定後發佈至企業私有登錄
- 定期複審:每季度對已批准技能進行例行安全複審
8.3 版本控制與依賴管理
企業生產環境中應鎖定技能版本,防止上游更新引入意外行為:
# 企業技能鎖定配置
# skills.lock.json(應納入版本控制)
{
"@openclaw/browser": {
"version": "3.2.1",
"integrity": "sha512-abc123...",
"approvedAt": "2026-01-15",
"approvedBy": "security-team"
},
"@company/internal-crm-skill": {
"version": "2.0.0",
"integrity": "sha512-def456...",
"approvedAt": "2026-02-01",
"approvedBy": "it-team"
}
}
8.4 內部技能開發規範
對於企業內部開發的自定義技能,建議制定以下開發規範:
- 所有內部技能必須存放於企業 Git 存儲庫,並要求 PR 審查才能合併
- 技能代碼禁止直接儲存 API 金鑰,一律透過企業 Secret Manager(如 HashiCorp Vault)取得
skill.md的description欄位必須包含對應的業務功能說明及負責部門- 所有技能必須撰寫單元測試,且在 CI/CD 管線中自動執行
- 技能的外部 API 呼叫必須記錄於統一的 API 呼叫日誌系統,便於審計追蹤
九、常見開發問題與排障
9.1 技能未被代理正確調用
最常見的問題之一是開發者完成技能實作後,發現代理在相關任務中未能自動調用該技能。根本原因通常在於 skill.md 的 capabilities.description 描述不夠具體或與使用者實際說法差距太大。
排障方法:
# 查看技能匹配評分(顯示代理如何理解各技能)
openclaw skills debug-match "今天台北天氣怎樣" --verbose
# 輸出範例:
# 評估 [email protected]:
# - get-weather-summary: 匹配分數 0.87 ✓
# - format-weather-report: 匹配分數 0.72 ✓
# 評估 @openclaw/browser:
# - navigate-to-url: 匹配分數 0.23
# 最終選擇:daily-weather (最高匹配)
若匹配分數偏低(低於 0.6),嘗試修改 capability description:使用更接近使用者自然語言的表述,加入具體的動詞(「查詢」、「取得」、「擷取」)和受詞(「天氣資訊」、「氣溫數據」)。
9.2 環境變數讀取失敗
技能在 skill.md 的 permissions.env 中宣告了環境變數,但執行時 context.env.get() 回傳 undefined。常見原因:
- 環境變數名稱在
skill.md與 OpenClaw 設定中拼寫不一致(注意大小寫) - 環境變數在全域設定中存在,但未加入技能的
permissions.env白名單 - 技能以開發模式安裝(
--dev),而環境變數只在正式安裝模式下生效
# 診斷技能的環境變數存取
openclaw skills env-check daily-weather
# 輸出:
# OPENWEATHER_API_KEY: ✓ 已設定(前 4 字元:abcd****)
# 技能宣告:✓ 已在 permissions.env 中列出
# 存取狀態:✓ 技能可正常讀取
9.3 網路請求被封鎖
技能宣告了 network: true,但發出的網路請求被 OpenClaw 攔截。可能原因:
- 技能試圖請求不在允許清單中的域名(若 OpenClaw 配置了域名白名單)
- 請求使用了不安全的 HTTP(而非 HTTPS)
- 企業環境中的網路代理設定干擾了請求
# 在技能中正確使用 context.fetch(而非全域 fetch)
// 錯誤:直接使用全域 fetch,可能繞過安全檢查
const response = await fetch(url);
// 正確:使用 context.fetch,確保安全攔截生效
const response = await context.fetch(url);
9.4 技能執行超時
OpenClaw 預設技能執行超時為 30 秒,若技能需要更長時間,應在 skill.md 中明確宣告:
execution:
timeout: 120 # 秒,最大允許 600
longRunning: true # 標記為長任務,啟用進度回報機制
9.5 技能狀態損壞
有狀態技能在異常終止後,可能留下不一致的狀態資料,導致後續執行失敗。建議實施防禦性的狀態讀取:
export async function execute(inputs, context) {
let state;
try {
state = await context.state.get('myState');
// 驗證狀態資料結構完整性
if (!state || typeof state.lastId !== 'string') {
context.log.warn('狀態資料格式不正確,重置為初始值');
state = getInitialState();
}
} catch (error) {
context.log.error('無法讀取狀態,重置為初始值', error.message);
state = getInitialState();
}
// 繼續執行...
}
function getInitialState() {
return { lastId: null, lastCheckedAt: null };
}
9.6 效能分析
若技能執行速度過慢,使用 OpenClaw 的效能分析工具找出瓶頸:
# 啟用效能分析模式執行技能
openclaw skills profile daily-weather \
--input '{"city": "台北"}' \
--output profile-report.json
# 查看分析結果
openclaw skills profile-view profile-report.json
常見效能問題包括:在迴圈中發出串行 API 請求(應改為 Promise.all 並行執行)、未快取重複的外部請求、狀態讀寫過於頻繁(應批次更新狀態)。
結語:建立可信賴的 Skills 生態系統
OpenClaw Skills 系統代表了 AI 代理擴展能力的一種新範式:以宣告式的 skill.md 規範取代傳統外掛 API,讓技能的能力邊界對人與機器都清晰可讀。這種設計不僅降低了開發門檻,也為安全審查提供了明確的切入點。[1]
然而,《The Register》揭露的 API 金鑰洩露事件提醒我們,技術設計的良善意圖需要輔以嚴格的開發者安全教育與商店審查機制。[4] 無論是個人開發者還是企業工程團隊,都應將安全性視為 Skill 開發的第一要務,而非事後補救的項目。
隨著 OpenClaw Skills 商店持續擴展、BlogWatcher 與 Supermemory 等核心技能日趨成熟,以及企業私有技能生態系統的逐步建立,我們正在目睹一個以技能為原子單元的代理能力市場的形成。理解並掌握這套技能開發範式,將是 2026 年 AI 代理工程師的核心競爭力之一。