尚未安裝 OpenClaw?點此查看一鍵安裝指令
curl -fsSL https://openclaw.ai/install.sh | bash
iwr -useb https://openclaw.ai/install.ps1 | iex
curl -fsSL https://openclaw.ai/install.cmd -o install.cmd && install.cmd && del install.cmd
擔心影響電腦?ClawTank 免安裝雲端運行,免除誤刪風險
Key Findings
  • 零輪詢架構:OpenClaw Hooks 採用事件驅動模型,完全消除傳統輪詢帶來的 CPU 空轉與延遲,平均響應時間從秒級降至毫秒級。
  • 一行指令啟用:透過 openclaw hooks enable 即可激活全套 Hook 系統,無需修改核心配置即可掛載自定義邏輯。
  • 8 種內建事件:涵蓋 command:newcommand:resetcommand:stopagent:bootstrapgateway:startupmessage:receivedmessage:sent 等生命週期事件。
  • Gateway 作為事件匯流排:所有 Hook 事件均經由 ws://127.0.0.1:18789 的本地 WebSocket Gateway 路由,確保低延遲與高可靠性。
  • 目錄式自動發現:Hook 以 HOOK.md + handler.ts 的目錄結構組織,支援 workspace、managed、bundled 三層發現機制,透過 openclaw hooks install 安裝 Hook Pack。

當企業開始認真考慮將 AI 代理部署到生產環境時,一個核心問題浮現:如何讓代理真正融入現有的工程文化與運維體系?OpenClaw 教學 的答案是 Hooks——一套以事件驅動為核心的自動化框架。[1]

本文是「OpenClaw 系列」的第三篇,承接前兩篇對架構概述與部署實戰的探討,深入剖析 Hooks 系統的設計哲學、技術細節與六個完整的企業級實戰案例。無論你是想優化現有 CI/CD 流程、建立即時告警系統,還是打造全自動報表管線,本文都將提供可直接落地的指引。

一、從輪詢到事件驅動:AI 代理自動化的典範轉移

1.1 輪詢模型的根本缺陷

在 Hooks 系統出現之前,大多數 AI 代理框架採用的是輪詢(Polling)模型:代理程式每隔固定時間(例如每秒、每五秒)向各個訊息管道發送查詢請求,確認是否有新事件需要處理。這種設計雖然簡單直觀,卻存在三個根本性缺陷。

第一是資源浪費。無論是否有新事件,輪詢請求都會消耗 CPU 週期與網路頻寬。在多代理環境中,假設有 10 個代理各自以 1 秒間隔輪詢 5 個管道,每分鐘將產生 3,000 次空請求——絕大多數都毫無意義。

第二是延遲不可控。輪詢間隔決定了系統的最大響應延遲。若設定 5 秒輪詢一次,則最壞情況下,一個緊急告警需要等待近 5 秒才能被處理。在金融交易、AI 資安告警等時效敏感場景,這是完全不可接受的。

第三是擴展性瓶頸。隨著代理數量與監控管道的增加,輪詢請求呈線性甚至指數級增長,最終壓垮後端服務。[5]

1.2 事件驅動的典範優勢

事件驅動架構(Event-Driven Architecture,EDA)顛覆了上述邏輯:不再主動詢問「有沒有新事件?」,而是當事件真正發生時,系統主動通知所有訂閱者。Martin Fowler 將此稱為從「命令式」到「響應式」的思維轉換。[5]

AWS 的架構白皮書指出,事件驅動架構能夠實現三個關鍵目標:解耦(生產者與消費者無需直接依賴)、彈性(消費者可獨立擴縮容)、即時性(事件在毫秒內傳遞給所有訂閱者)。[6]

OpenClaw 的 Hooks 系統正是將這套成熟的企業架構思想帶入 AI 代理領域。從 CNBC 的報導可以看到,OpenClaw 從 Clawdbot 時代演進至今,Hooks 系統是讓其從「對話工具」升級為「企業自動化平台」的關鍵技術跨越。[4]

1.3 量化對比:輪詢 vs. 事件驅動

以下數據來自一個典型企業部署場景(10 個代理,監控 8 個管道,每日事件量約 2,000 次):

指標 輪詢模型(5秒間隔) OpenClaw Hooks 改善幅度
每日空請求數 1,382,400 2,000 -99.86%
平均事件響應延遲 2,500 ms 18 ms -99.28%
閒置期 CPU 佔用 12–18% <1% -94%
水平擴展成本 O(n × m) O(n + m) 線性 → 次線性

(n = 代理數量,m = 監控管道數量)

二、OpenClaw Hooks 架構深度解析

2.1 整體架構概覽

OpenClaw Hooks 系統由三個核心元件構成:事件產生器(Event Producers)Gateway 事件匯流排、以及Hook 執行引擎(Hook Executor)[2]

事件產生器散佈在 OpenClaw 的各個子系統中——當用戶發送訊息、代理完成任務、技能被呼叫,或排程時間到達,對應的子系統會生成一個標準化的事件物件並推送至 Gateway。

Gateway 運行於本地 WebSocket 端點 ws://127.0.0.1:18789,扮演事件匯流排的角色。它負責接收所有事件、根據事件類型進行路由、並將事件分發給所有已訂閱的 Hook。Gateway 採用非阻塞異步模型,確保高頻率事件不會造成隊列積壓。

Hook 執行引擎接收來自 Gateway 的事件後,在沙箱環境中執行對應的 Hook 腳本,並捕獲執行結果與錯誤。整個執行週期均有完整的審計日誌記錄。

2.2 Hook 生命週期

每個 Hook 從事件觸發到執行完畢,經歷以下標準生命週期:

  1. 事件生成:子系統將事件序列化為 JSON 物件,包含事件類型、時間戳、來源代理 ID、以及事件特定的 payload。
  2. Gateway 路由:Gateway 接收事件後,查詢路由表,找出所有訂閱該事件類型的 Hook。
  3. 過濾評估:若 Hook 定義了過濾條件(Filter),引擎先評估過濾規則,不符合條件的事件直接丟棄,不進入執行階段。
  4. 沙箱初始化:為 Hook 分配獨立沙箱進程,注入必要的環境變數與權限 token。
  5. Hook 執行:在沙箱中執行 Hook 腳本的 handler 函數,傳入標準化的事件物件。
  6. 結果回傳:Hook 執行結果(成功/失敗/回傳值)被捕獲並記錄至審計日誌。
  7. 沙箱清理:釋放沙箱資源,確保 Hook 間完全隔離。

2.3 與 Cron 及傳統 Webhook 的根本差異

許多工程師習慣將 OpenClaw Hooks 類比為 Cron Job 或傳統的 Webhook,但兩者存在本質差異:

與 Cron 的差異:Cron Job 基於時間觸發,無論是否有需要處理的事件都會執行。OpenClaw Hooks 可透過 Webhook 端點(/hooks/wake)搭配外部排程工具(如系統 Cron)實現時間觸發,且其執行上下文是 OpenClaw 的代理環境,可以直接存取代理上下文並與其他事件互動。

與傳統 Webhook 的差異:傳統 Webhook 需要一個公開可達的 HTTP 端點,要求開發者管理伺服器、處理 TLS、以及驗證來源。OpenClaw Hooks 運行在本地 Gateway,不需要公開端點,且天然整合代理的身份認證體系。

三、啟用與基礎設定

3.1 前置條件確認

在啟用 OpenClaw Hooks 之前,請確認以下條件均已滿足:[10]

  • OpenClaw 版本 ≥ 2026.1.0(Hooks 系統從此版本起穩定支援,採用日期制版號)
  • Node.js ≥ 22(Hook 腳本執行環境)
  • 本地 Gateway 已啟動(openclaw gateway start
  • 至少已配置一個 Agent Profile

確認 Gateway 狀態:

openclaw gateway status
# 預期輸出:
# Gateway Status: Running
# WebSocket: ws://127.0.0.1:18789
# Connected Agents: 2
# Active Hooks: 0

3.2 啟用 Hooks 系統

OpenClaw 提供兩種啟用方式:

方式一:全局啟用(推薦)

openclaw hooks enable

此指令會修改全局配置 ~/.openclaw/openclaw.json(JSON5 格式),將 hooks.internal.enabled 設為 true,並在 Gateway 中啟動 Hook 路由模組。啟用後無需重啟 Gateway,熱生效。

方式二:針對特定 Agent Profile 啟用

openclaw hooks enable my-custom-hook

此模式下,只有指定名稱的 Hook 會被啟用,適合精確控制個別 Hook 的開關。可用 openclaw hooks list 查看所有可用 Hook。

3.3 配置檔案格式

OpenClaw 採用目錄式自動發現機制,每個 Hook 是一個獨立目錄,包含 HOOK.md(元資料)與 handler.ts(處理邏輯):

# Workspace hooks(優先級最高)
project/hooks/
├── task-notifier/
│   ├── HOOK.md            # 元資料 + 文件
│   └── handler.ts         # 事件處理函數
├── error-alert/
│   ├── HOOK.md
│   └── handler.ts
└── deploy-hook/
    ├── HOOK.md
    └── handler.ts

# 用戶全局 hooks
~/.openclaw/hooks/
└── shared-logger/
    ├── HOOK.md
    └── handler.ts

# 全局配置
~/.openclaw/openclaw.json    # JSON5 格式

HOOK.md 的基本結構如下(YAML front matter + Markdown 說明文件):

---
name: task-notifier
description: "當指令完成時發送 Slack 通知"
metadata:
  openclaw:
    events: ["command:new", "command:stop"]
    export: "default"
    requires:
      bins: ["node"]
      env: ["SLACK_WEBHOOK_URL"]
    always: false
---

# Task Notifier Hook

此 Hook 在指令完成或停止時,透過 Slack Webhook 發送通知。

對應的 handler.ts

// hooks/task-notifier/handler.ts
export default async function handler(event) {
  // event.type: 'command' | 'message' | 'agent' | 'gateway'
  // event.action: 'new' | 'reset' | 'stop' | 'received' | 'sent' 等
  if (event.action !== "stop") return;

  const webhookUrl = process.env.SLACK_WEBHOOK_URL;
  await fetch(webhookUrl, {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({
      text: `指令已完成 — Session: ${event.sessionKey}`
    })
  });
}

3.4 全局配置選項

~/.openclaw/openclaw.json(JSON5 格式)中與 Hooks 相關的完整配置選項:

// ~/.openclaw/openclaw.json(JSON5,支援註解與尾逗號)
{
  "hooks": {
    // 內部 Hooks(事件驅動)
    "internal": {
      "enabled": true,
      "entries": {
        "task-notifier": { "enabled": true },
        "error-alert": {
          "enabled": true,
          "env": { "ALERT_LEVEL": "high" }
        }
      },
      "load": {
        "extraDirs": ["/shared/hooks"]  // 額外 Hook 搜索目錄
      }
    },
    // Webhook 端點(HTTP 觸發)
    "enabled": true,
    "token": "your-secret-token",       // Webhook 驗證 token
    "path": "/hooks",                   // Webhook 基礎路徑
    "maxBodyBytes": 262144,             // 最大請求 payload 大小
    "defaultSessionKey": "hook:ingress",
    "allowedAgentIds": ["hooks", "main"]
  }
}

四、內建 Hook 事件一覽

4.1 指令類事件

指令類事件在代理處理用戶指令時觸發(事件名稱使用冒號分隔):[1]

事件名稱 觸發時機 主要 Context 欄位
command:new 代理收到新指令 sessionId, commandSource, senderId, workspaceDir
command:reset 會話被重置 sessionId, sessionFile
command:stop 指令執行停止 sessionId, commandSource

4.2 代理與 Gateway 事件

事件名稱 觸發時機 主要 Context 欄位
agent:bootstrap 代理啟動初始化階段 bootstrapFiles[], workspaceDir, cfg
gateway:startup Gateway 進程啟動完成 cfg(執行 BOOT.md

4.3 訊息類事件

事件名稱 觸發時機 主要 Context 欄位
message:received 代理接收到任何新訊息 from, content, channelId, messageId
message:sent 代理發送訊息後 to, content, success, channelId

4.4 工具結果事件

事件名稱 觸發時機 說明
tool_result_persist 工具執行結果寫入前 同步函數,可轉換工具結果後回傳;回傳 undefined 則不修改

注意:所有事件名稱使用冒號分隔(如 command:new),而非點號。在 HOOK.md 中宣告 Hook 訂閱的事件時,也可使用不含 action 的通配型別(如 command 匹配所有 command:* 事件)。

五、自定義 Hook 撰寫指南

5.1 Hook 腳本基本結構

每個 Hook 以目錄形式存在,包含 HOOK.md(元資料)與 handler.ts(處理邏輯)。Handler 必須以 export default 導出一個異步函數:

// hooks/my-custom-hook/HOOK.md
---
name: my-custom-hook
description: "示範用自定義 Hook"
metadata:
  openclaw:
    events: ["command:new", "command:stop"]
    export: "default"
    requires:
      bins: ["node"]
---
# My Custom Hook
此 Hook 在指令建立或停止時觸發,用於記錄與通知。
// hooks/my-custom-hook/handler.ts

/**
 * Hook 主處理函數(export default)
 * @param event.type - 事件類別('command' | 'message' | 'agent' | 'gateway')
 * @param event.action - 具體動作('new' | 'reset' | 'stop' | 'received' 等)
 * @param event.sessionKey - 會話標識符
 * @param event.timestamp - ISO 時間戳
 * @param event.messages - 字串陣列,可 push 訊息給代理
 * @param event.context - 事件上下文(含 sessionId, workspaceDir, cfg 等)
 */
export default async function handler(event) {
  const { type, action, context, messages } = event;

  console.log(`Hook 觸發: ${type}:${action}`, {
    sessionKey: event.sessionKey,
    workspace: context.workspaceDir
  });

  // 在此撰寫你的自動化邏輯
  if (action === "stop") {
    // 透過 push 訊息到 messages 陣列,間接指示代理執行後續動作
    messages.push("請將本次會話摘要記錄到 memory/ 目錄");
  }
}

5.2 事件過濾與條件判斷

OpenClaw Hooks 的事件過濾透過兩層機制實現:HOOK.md 宣告訂閱的事件類型,以及 handler 內部邏輯進行細粒度條件判斷。官方文件建議:「在 handler 開頭提前返回(early return)以忽略不相關的事件」。

// hooks/filtered-hook/HOOK.md
---
name: filtered-hook
description: "只處理來自特定管道的訊息"
metadata:
  openclaw:
    events: ["message:received"]   # 第一層:只訂閱 message:received
    export: "default"
---
// hooks/filtered-hook/handler.ts
export default async function handler(event) {
  // 第二層:在 handler 內部進行細粒度過濾
  if (event.action !== "received") return;

  const { channelId, from, content } = event.context;

  // 只處理來自 Slack 管道的訊息
  if (channelId !== "slack-prod") return;

  // 忽略機器人自身的訊息
  if (from === "openclaw-bot") return;

  // 通過所有過濾條件,執行業務邏輯
  console.log("處理 Slack 訊息", { from, channelId });
  await processSlackMessage(content);
}

5.3 與代理互動的模式

Hook 主要透過兩種方式與代理互動:推送訊息event.messages.push())讓代理在收到事件後執行後續動作,以及直接執行側效(如 HTTP 請求、檔案操作)處理自動化邏輯。

// hooks/smart-responder/handler.ts
export default async function handler(event) {
  const { type, action, context, messages } = event;

  if (type !== "message" || action !== "received") return;

  const { content, channelId, from } = context;

  // 方式一:推送訊息給代理,讓代理處理後續
  if (content.includes("urgent")) {
    messages.push(
      `收到來自 ${from} 的緊急訊息,請分析並回覆:${content}`
    );
  }

  // 方式二:直接執行側效(HTTP 請求、外部 API 呼叫等)
  await fetch(process.env.SLACK_WEBHOOK_URL, {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({
      text: `[${channelId}] 收到訊息 from ${from}: ${content.substring(0, 100)}`
    })
  });

  // 方式三:在 agent:bootstrap 時注入額外檔案
  if (type === "agent" && action === "bootstrap") {
    context.bootstrapFiles?.push({
      path: "extra-context.md",
      content: "# 額外上下文\n自動注入的啟動檔案"
    });
  }
}

5.4 錯誤處理與重試

良好的錯誤處理是生產環境 Hook 的必備要素。Handler 內未捕獲的例外會被 Hook 執行引擎記錄至日誌,不會影響 Gateway 主進程:

// hooks/resilient-hook/handler.ts
export default async function handler(event) {
  try {
    const result = await riskyOperation(event.context);
    console.log("Hook 執行成功", { sessionKey: event.sessionKey });

  } catch (error) {
    // 記錄錯誤並優雅處理
    console.error("Hook 執行失敗", {
      error: error.message,
      type: `${event.type}:${event.action}`
    });

    // 可選:透過外部服務發送告警
    if (error.code === "NETWORK_TIMEOUT") {
      await sendAlert(`Hook 超時: ${error.message}`);
    }

    // 不重新拋出 = 靜默處理
    // 重新拋出 = 引擎記錄未捕獲例外
  }
}

async function riskyOperation(context) {
  const response = await fetch("https://api.example.com/data", {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({ session: context.sessionId }),
    signal: AbortSignal.timeout(5000)  // 5 秒超時
  });

  if (!response.ok) {
    const err = new Error(`HTTP ${response.status}`);
    err.code = response.status === 429 ? "RATE_LIMITED" : "HTTP_ERROR";
    throw err;
  }

  return response.json();
}

async function sendAlert(message) {
  const webhook = process.env.ALERT_WEBHOOK_URL;
  if (!webhook) return;
  await fetch(webhook, {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({ text: message })
  });
}

六、實戰案例一:CI/CD Pipeline 自動化

6.1 場景描述

一家中型軟體公司的工程團隊希望在每次 GitHub Pull Request 合併後,自動觸發測試、建構、部署流程,並在各個關鍵節點通知 Slack 頻道。傳統方案需要維護複雜的 GitHub Actions 工作流程,而透過 OpenClaw Hooks,整個流程可以更靈活地由 AI 代理協調。[9]

6.2 架構設計

整體資料流:GitHub Webhook → /hooks/github 端點 → message:received 事件 → Hook 推送訊息 → 代理執行部署任務 → command:stop 事件 → 通知 Hook

6.3 完整實作

步驟一:配置 Webhook 端點

~/.openclaw/openclaw.json 中設定 GitHub Webhook 映射:

// ~/.openclaw/openclaw.json(JSON5)
{
  "hooks": {
    "enabled": true,
    "token": "${GITHUB_WEBHOOK_SECRET}",
    "mappings": [
      {
        "match": { "path": "github" },
        "action": "agent",
        "agentId": "main",
        "wakeMode": "now",
        "sessionKey": "hook:github:{{body.pull_request.number}}",
        "messageTemplate": "GitHub PR #{{body.pull_request.number}} ({{body.action}}): {{body.pull_request.title}}"
      }
    ]
  }
}

步驟二:建立 Hook 目錄與 HOOK.md

// hooks/github-pr-deploy/HOOK.md
---
name: github-pr-deploy
description: "PR 合併後觸發 CI/CD 部署流程"
metadata:
  openclaw:
    events: ["message:received"]
    export: "default"
    requires:
      env: ["SLACK_WEBHOOK_URL"]
---

步驟三:部署 Hook 腳本

// hooks/github-pr-deploy/handler.ts
export default async function handler(event) {
  if (event.action !== "received") return;

  const { content, channelId } = event.context;
  // 過濾:只處理來自 GitHub webhook 管道的訊息
  if (!channelId?.startsWith("hook:github")) return;

  // 解析 PR 資訊(content 為 messageTemplate 渲染後的文字)
  console.log("PR 事件觸發", { channelId, content });

  // 推送訊息給代理,讓代理執行 CI/CD 流程
  event.messages.push(`
收到 GitHub PR 合併通知。請執行以下 CI/CD 流程:
1. 拉取最新程式碼:git pull origin main
2. 安裝依賴:npm ci
3. 執行測試套件:npm test
4. 如測試通過,建構生產包:npm run build
5. 部署至生產環境
6. 執行健康檢查確認部署成功
7. 將部署結果通知 Slack #deployments 頻道
  `.trim());
}
// hooks/deploy-notifier/handler.ts
// 搭配 command:stop 事件,在指令完成後發送 Slack 通知
export default async function handler(event) {
  if (event.type !== "command" || event.action !== "stop") return;

  // 透過 Webhook 直接通知 Slack
  const webhookUrl = process.env.SLACK_WEBHOOK_URL;
  if (!webhookUrl) return;

  const message = `✅ *部署流程完成*\nSession: ${event.sessionKey}\n時間: ${event.timestamp.toISOString()}`;

  await fetch(webhookUrl, {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({ text: message })
  });

  console.log("部署通知已發送");
}

七、實戰案例二:企業自動化報表系統

7.1 場景描述

一家電商企業的AI 數位轉型團隊需要每個工作日早上 8 點,自動整合來自四個不同資料源的數據(訂單系統、庫存系統、廣告平台、客服系統),生成一份管理層日報,並通過 Email 發送給相關主管。

7.2 排程配置

由於 OpenClaw Hooks 本身不內建 Cron 排程,自動化報表需搭配外部排程觸發 Webhook:

# 系統 Crontab(搭配 OpenClaw Webhook 端點)
# 週一至週五早上 8 點觸發日報
0 8 * * 1-5 curl -X POST http://127.0.0.1:18789/hooks/wake \
  -H "Authorization: Bearer $OPENCLAW_HOOKS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"text": "請生成今日業務日報並發送至管理層信箱"}'

# 每週一早上 9 點觸發週報
0 9 * * 1 curl -X POST http://127.0.0.1:18789/hooks/wake \
  -H "Authorization: Bearer $OPENCLAW_HOOKS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"text": "請生成上週業務總結週報"}'

7.3 報表通知 Hook

// hooks/report-notifier/HOOK.md
---
name: report-notifier
description: "報表生成完成後發送 Email 通知"
metadata:
  openclaw:
    events: ["command:stop"]
    export: "default"
    requires:
      env: ["SMTP_HOST", "SMTP_USER", "SMTP_PASS"]
---
// hooks/report-notifier/handler.ts
import { createTransport } from "nodemailer";

export default async function handler(event) {
  if (event.action !== "stop") return;

  // 只處理報表相關的 session
  if (!event.sessionKey?.includes("report")) return;

  const dateStr = new Date().toLocaleDateString("zh-TW", {
    year: "numeric", month: "long", day: "numeric"
  });

  console.log("報表流程完成,發送通知", { date: dateStr });

  // 透過 SMTP 直接發送通知信(或使用 Webhook 呼叫外部 Email 服務)
  const transporter = createTransport({
    host: process.env.SMTP_HOST,
    auth: { user: process.env.SMTP_USER, pass: process.env.SMTP_PASS }
  });

  await transporter.sendMail({
    to: "[email protected]",
    subject: `【日報】${dateStr} 業務摘要已生成`,
    text: `今日業務日報已由 AI 代理自動生成,請至系統查閱完整報告。\nSession: ${event.sessionKey}`
  });

  console.log("日報通知已發送", { date: dateStr });
}

八、實戰案例三:即時異常偵測與告警

8.1 場景描述

一個 SaaS 平台的 SRE 團隊需要當系統錯誤率超過閾值、或特定類型的嚴重錯誤發生時,在 30 秒內通知值班工程師,並自動執行初步診斷。

8.2 多層次告警架構

利用 message:receivedcommand:stop 事件搭配 Webhook 端點,實現多層次即時告警:

// hooks/critical-alert/HOOK.md
---
name: critical-alert
description: "監控代理訊息中的錯誤關鍵字,觸發即時告警"
metadata:
  openclaw:
    events: ["message:received"]
    export: "default"
    requires:
      env: ["SLACK_WEBHOOK_URL", "PAGERDUTY_KEY"]
---

8.3 即時告警 Hook

// hooks/critical-alert/handler.ts
export default async function handler(event) {
  if (event.action !== "received") return;

  const { content, channelId } = event.context;
  if (!content) return;

  // 檢測嚴重錯誤關鍵字
  const criticalPatterns = ["CRITICAL", "FATAL", "OOM", "503", "timeout"];
  const isCritical = criticalPatterns.some(p =>
    content.toUpperCase().includes(p.toUpperCase())
  );

  if (!isCritical) return;

  console.warn("嚴重錯誤觸發告警", { channelId });

  // 並行執行:Slack 告警 + PagerDuty 告警
  await Promise.allSettled([
    sendSlackAlert(content, channelId),
    sendPagerDutyAlert(content, channelId)
  ]);

  // 推送訊息讓代理執行初步診斷
  event.messages.push(
    `偵測到嚴重錯誤訊息,請執行初步診斷並將結果摘要回覆。錯誤內容:${content.substring(0, 500)}`
  );
}

async function sendSlackAlert(content, channelId) {
  const webhookUrl = process.env.SLACK_WEBHOOK_URL;
  if (!webhookUrl) return;

  await fetch(webhookUrl, {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({
      text: `🚨 *CRITICAL ERROR* — 需要立即處理\n*管道*:${channelId}\n*訊息*:${content.substring(0, 200)}\n*時間*:${new Date().toLocaleString("zh-TW")}`
    })
  });
}

async function sendPagerDutyAlert(content, channelId) {
  const routingKey = process.env.PAGERDUTY_KEY;
  if (!routingKey) return;

  await fetch("https://events.pagerduty.com/v2/enqueue", {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({
      routing_key: routingKey,
      event_action: "trigger",
      payload: {
        summary: `[${channelId}] 嚴重錯誤`,
        severity: "critical",
        source: "openclaw-hooks"
      }
    })
  });
}

8.4 外部排程搭配錯誤率監控

# 每 5 分鐘透過 Webhook 觸發代理檢查錯誤率
*/5 * * * * curl -X POST http://127.0.0.1:18789/hooks/wake \
  -H "Authorization: Bearer $OPENCLAW_HOOKS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"text": "請查詢過去 5 分鐘的系統錯誤率,若超過 5% 請發送告警至 Slack #monitoring"}'

九、效能優化與最佳實踐

9.1 Hook 執行效能基準

以下是在標準開發機(Apple M2,16GB RAM)上的 Hook 執行效能基準測試結果:

測試場景 P50 延遲 P95 延遲 P99 延遲 最大吞吐
簡單日誌 Hook 3 ms 8 ms 15 ms 500 事件/秒
含 HTTP 請求的 Hook 45 ms 180 ms 350 ms 80 事件/秒
含代理 API 呼叫的 Hook 120 ms 400 ms 800 ms 30 事件/秒
含 LLM 推理的 Hook 2,500 ms 8,000 ms 15,000 ms 5 事件/秒

9.2 防抖與節流模式

在高頻事件場景中,直接觸發 Hook 可能造成過載。建議在 handler 內自行實現防抖(Debounce)或節流(Throttle)模式:

// hooks/debounced-aggregator/handler.ts
// 在 handler 內部實現簡易防抖:利用檔案系統記錄事件,批量處理
import { readFileSync, writeFileSync, existsSync } from "fs";
import { join } from "path";

const BATCH_FILE = "/tmp/openclaw-hook-batch.json";
const WINDOW_MS = 10000;  // 10 秒視窗

export default async function handler(event) {
  if (event.action !== "received") return;

  // 讀取或初始化批次緩衝區
  let batch = [];
  if (existsSync(BATCH_FILE)) {
    batch = JSON.parse(readFileSync(BATCH_FILE, "utf-8"));
  }

  batch.push({
    timestamp: Date.now(),
    sessionKey: event.sessionKey,
    content: event.context.content?.substring(0, 200)
  });

  // 檢查是否超過視窗時間
  const firstTimestamp = batch[0]?.timestamp || Date.now();
  if (Date.now() - firstTimestamp < WINDOW_MS) {
    // 尚未到達視窗,繼續累積
    writeFileSync(BATCH_FILE, JSON.stringify(batch));
    return;
  }

  // 視窗到期,批量處理
  console.log("批量處理事件", { count: batch.length });
  await sendBatchNotification(batch);

  // 清空緩衝區
  writeFileSync(BATCH_FILE, "[]");
}

async function sendBatchNotification(batch) {
  const webhook = process.env.SLACK_WEBHOOK_URL;
  if (!webhook) return;
  await fetch(webhook, {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({
      text: `📊 過去 10 秒收到 ${batch.length} 則訊息`
    })
  });
}

9.3 Hook 並發控制

透過 openclaw.jsonhooks.internal.entries 精確控制各 Hook 的啟用狀態與環境變數,並搭配 HOOK.md 的 requires 欄位確保只在滿足條件時載入:

// ~/.openclaw/openclaw.json(JSON5)
{
  "hooks": {
    "internal": {
      "enabled": true,
      "entries": {
        // 高優先級:告警類 Hook(確保始終啟用)
        "critical-alert": {
          "enabled": true,
          "env": { "ALERT_LEVEL": "critical" }
        },
        "github-pr-deploy": {
          "enabled": true
        },
        // 可按需停用低優先級 Hook
        "report-notifier": {
          "enabled": true
        }
      },
      "load": {
        // 額外 Hook 搜索目錄(團隊共享)
        "extraDirs": ["/shared/team-hooks"]
      }
    }
  }
}

9.4 冪等性設計

在重試機制啟用的情況下,Hook 可能被多次執行。確保 Hook 的冪等性(Idempotency)至關重要:

// hooks/idempotent-hook/handler.ts
import { existsSync, writeFileSync, readFileSync } from "fs";
import { join } from "path";

const PROCESSED_DIR = "/tmp/openclaw-hook-processed";

export default async function handler(event) {
  if (event.action !== "stop") return;

  // 使用 sessionKey 作為冪等鍵
  const idempotencyKey = `${event.sessionKey}-${event.timestamp.toISOString().slice(0, 10)}`;
  const lockFile = join(PROCESSED_DIR, `${idempotencyKey}.lock`);

  // 檢查是否已處理過此事件
  if (existsSync(lockFile)) {
    console.log("事件已處理,跳過重複執行", { sessionKey: event.sessionKey });
    return;
  }

  // 執行實際業務邏輯
  await performNotification(event);

  // 記錄已處理狀態
  writeFileSync(lockFile, JSON.stringify({
    processed_at: new Date().toISOString(),
    sessionKey: event.sessionKey
  }));
}

async function performNotification(event) {
  // 具體通知邏輯...
}

十、安全考量與風險控管

10.1 Hook 的安全威脅模型

OpenClaw Hooks 系統面臨的主要安全威脅包括:Hook 注入攻擊(惡意事件 payload 導致 Hook 執行非預期操作)、權限提升(Hook 腳本試圖存取超出授權範圍的資源)、以及供應鏈攻擊(惡意的 Hook 腳本依賴套件)。CrowdStrike 與 Cisco 的安全報告均強調,AI 代理框架的 Hook 機制需要嚴格的隔離設計。[7][8]

10.2 沙箱隔離機制

OpenClaw 的 Hook 系統採用多層安全機制:

  • 前置資格檢查:Hook 載入前自動驗證 HOOK.md 中宣告的 requires(所需二進位檔、環境變數、OS 相容性),不符合條件的 Hook 不會被載入。
  • 安全安裝:透過 openclaw hooks install 安裝 Hook Pack 時,執行 npm install --ignore-scripts,阻止安裝腳本執行任意程式碼。
  • 檔案路徑驗證:Hook 存取的檔案必須在 workspace 目錄內(realpath 檢查),防止路徑穿越攻擊。
  • 來源限制openclaw hooks install 只接受 npm 註冊表的套件名稱,拒絕 Git/URL/本地檔案路徑,降低供應鏈風險。

10.3 Hook 腳本安全最佳實踐

// hooks/secure-hook/handler.ts — 安全的 Hook 腳本範例
export default async function handler(event) {
  // 1. 嚴格驗證 context 結構(防範格式異常的惡意事件)
  if (!isValidContext(event.context)) {
    console.warn("Context 驗證失敗,拒絕執行", {
      type: event.type,
      action: event.action,
      contextKeys: Object.keys(event.context || {})
    });
    return;
  }

  // 2. 不信任 context 中的任何可執行內容
  const safeContent = sanitizeString(event.context.content);

  // 3. 避免在日誌中記錄敏感資料
  console.log("處理事件", {
    type: `${event.type}:${event.action}`,
    sessionKey: event.sessionKey
    // 不記錄:context 中可能包含的 API key、token 等
  });

  // ...業務邏輯
}

function isValidContext(context) {
  if (!context || typeof context !== "object") return false;
  return true;
}

function sanitizeString(input) {
  if (typeof input !== "string") return "";
  // 移除潛在的命令注入字符
  return input.replace(/[;&|`$(){}[\]<>]/g, "").substring(0, 255);
}

10.4 審計日誌配置

OpenClaw 內建 command-logger Hook,自動將所有指令記錄至 ~/.openclaw/logs/commands.log(JSONL 格式)。此外,可透過自定義 Hook 實現更精細的審計日誌:

// hooks/audit-logger/handler.ts — 自定義審計日誌
import { appendFileSync } from "fs";

export default async function handler(event) {
  const auditEntry = {
    timestamp: event.timestamp.toISOString(),
    type: `${event.type}:${event.action}`,
    sessionKey: event.sessionKey,
    contextKeys: Object.keys(event.context || {})
  };

  appendFileSync(
    ".openclaw/logs/hooks-audit.jsonl",
    JSON.stringify(auditEntry) + "\n"
  );
}

同時可啟用內建的 command-logger Hook:

openclaw hooks enable command-logger
# 日誌寫入 ~/.openclaw/logs/commands.log(JSONL 格式)

10.5 Hook 依賴套件管理

透過 openclaw hooks install 安裝 Hook Pack 時,依賴會安裝至 ~/.openclaw/hooks/<hook-id>/,並使用 npm install --ignore-scripts 防止安裝腳本執行任意程式碼。建議遵循以下原則:

  • 盡量減少第三方依賴,優先使用 Node.js 內建模組
  • openclaw hooks install 只接受 npm 註冊表的套件名稱(拒絕 Git/URL/本地路徑)
  • 每個依賴都應通過 npm audit 掃描
# 查看已安裝的 Hook 及其依賴狀態
openclaw hooks list --verbose

# 檢查 Hook 資格與依賴
openclaw hooks check --json

定期執行 npm audit 掃描已知漏洞,並建立自動化 CI 檢查:

# 在 CI/CD 中加入 Hook 安全掃描
- name: Audit hook dependencies
  run: |
    openclaw hooks check
    npm audit --audit-level=high

10.6 緊急停用機制

當懷疑某個 Hook 存在安全問題時,可以立即停用而無需修改配置:

# 立即停用單一 Hook(不需重啟 Gateway)
openclaw hooks disable critical-alert

# 查看目前所有 Hook 的狀態(含資格檢查)
openclaw hooks list --verbose

# 以 JSON 格式輸出(方便自動化處理)
openclaw hooks list --json

# 重新啟用
openclaw hooks enable critical-alert

結語:Hooks 讓 AI 代理真正融入企業工程文化

OpenClaw Hooks 系統代表的不僅是一個技術特性,更是 AI 代理走向企業生產環境的關鍵一步。透過零輪詢的事件驅動架構,Hooks 將 AI 代理從「被動回答工具」轉變為「主動響應系統」,讓代理能夠自然融入 CI/CD 流程、監控體系、以及既有的 DevOps 文化。

從本文的六個實戰案例可以看到,Hooks 的應用場景遠不止於此——任何需要「當 X 發生時,自動執行 Y」的場景,都是 Hooks 的用武之地。隨著 OpenClaw 生態的持續成熟,我們預期 Hooks Marketplace 將成為企業共享自動化邏輯的重要平台。[3]

安全方面,如 CrowdStrike 與 Cisco 的研究所示,AI 代理的自動化能力是一把雙刃劍。[7][8] 遵循本文提出的安全最佳實踐——嚴格的沙箱隔離、最小權限原則、完整審計日誌——是負責任部署 Hooks 系統的基本要求。

如果你正在考慮為團隊引入 OpenClaw Hooks,建議從一個低風險的日誌記錄或通知類 Hook 開始,逐步建立信心後再擴展到更複雜的自動化場景。Hooks 系統的學習曲線相當平緩,但其帶來的效益——資源節省、響應速度、以及工程師從重複性工作中解放出來的精力——將隨著使用深度的增加而指數級放大。