目录
-
- 摘要
- [1. 引言 - Webhook 与 Hooks 的区别与联系](#1. 引言 - Webhook 与 Hooks 的区别与联系)
-
- [1.1 概念辨析](#1.1 概念辨析)
- [1.2 架构定位对比](#1.2 架构定位对比)
- [1.3 协同工作模式](#1.3 协同工作模式)
- [2. Webhook 机制详解 - 外部事件接入](#2. Webhook 机制详解 - 外部事件接入)
-
- [2.1 核心架构](#2.1 核心架构)
- [2.2 启用与配置](#2.2 启用与配置)
- [2.3 认证机制](#2.3 认证机制)
- [2.4 端点详解](#2.4 端点详解)
-
- [2.4.1 `/hooks/wake` - 唤醒主会话](#2.4.1
/hooks/wake- 唤醒主会话) - [2.4.2 `/hooks/agent` - 运行隔离智能体](#2.4.2
/hooks/agent- 运行隔离智能体) - [2.4.3 `/hooks/<name>` - 自定义映射](#2.4.3
/hooks/<name>- 自定义映射)
- [2.4.1 `/hooks/wake` - 唤醒主会话](#2.4.1
- [2.5 响应状态码](#2.5 响应状态码)
- [3. Hooks 机制详解 - 内部事件钩子](#3. Hooks 机制详解 - 内部事件钩子)
-
- [3.1 设计理念](#3.1 设计理念)
- [3.2 Hook 发现机制](#3.2 Hook 发现机制)
- [3.3 Hook 结构](#3.3 Hook 结构)
- [3.4 事件类型](#3.4 事件类型)
-
- [3.4.1 命令事件](#3.4.1 命令事件)
- [3.4.2 智能体事件](#3.4.2 智能体事件)
- [3.4.3 Gateway 事件](#3.4.3 Gateway 事件)
- [3.5 捆绑的 Hooks](#3.5 捆绑的 Hooks)
-
- [3.5.1 session-memory](#3.5.1 session-memory)
- [3.5.2 command-logger](#3.5.2 command-logger)
- [3.5.3 boot-md](#3.5.3 boot-md)
- [4. Webhook 配置实战 - 飞书、GitHub、企业微信](#4. Webhook 配置实战 - 飞书、GitHub、企业微信)
-
- [4.1 飞书 Webhook 集成](#4.1 飞书 Webhook 集成)
-
- [4.1.1 配置步骤](#4.1.1 配置步骤)
- [4.1.2 飞书事件处理流程](#4.1.2 飞书事件处理流程)
- [4.2 GitHub Webhook 集成](#4.2 GitHub Webhook 集成)
-
- [4.2.1 配置步骤](#4.2.1 配置步骤)
- [4.3 企业微信 Webhook 集成](#4.3 企业微信 Webhook 集成)
-
- [4.3.1 配置步骤](#4.3.1 配置步骤)
- [5. Hooks 开发指南 - 自定义钩子函数](#5. Hooks 开发指南 - 自定义钩子函数)
-
- [5.1 创建自定义 Hook](#5.1 创建自定义 Hook)
- [5.2 测试与调试](#5.2 测试与调试)
-
- [5.2.1 验证 Hook 发现](#5.2.1 验证 Hook 发现)
- [5.2.2 启用并测试](#5.2.2 启用并测试)
- [5.2.3 查看日志](#5.2.3 查看日志)
- [5.3 最佳实践](#5.3 最佳实践)
-
- [5.3.1 保持处理程序快速](#5.3.1 保持处理程序快速)
- [5.3.2 优雅处理错误](#5.3.2 优雅处理错误)
- [5.3.3 尽早过滤事件](#5.3.3 尽早过滤事件)
- [6. 安全与最佳实践](#6. 安全与最佳实践)
-
- [6.1 Webhook 安全](#6.1 Webhook 安全)
-
- [6.1.1 令牌管理](#6.1.1 令牌管理)
- [6.1.2 网络安全](#6.1.2 网络安全)
- [6.1.3 请求体验证](#6.1.3 请求体验证)
- [6.2 Hooks 安全](#6.2 Hooks 安全)
-
- [6.2.1 权限隔离](#6.2.1 权限隔离)
- [6.2.2 资源控制](#6.2.2 资源控制)
- [6.3 监控与日志](#6.3 监控与日志)
-
- [6.3.1 日志配置](#6.3.1 日志配置)
- [6.3.2 监控指标](#6.3.2 监控指标)
- [7. 总结](#7. 总结)
- [8. 参考资料](#8. 参考资料)
摘要
在现代智能体系统中,事件驱动架构是实现灵活扩展的关键。OpenClaw 提供了两套互补的事件处理机制:Webhook 用于接收外部系统的事件推送,Hooks 用于响应内部命令和生命周期事件。本文深入剖析这两套机制的设计理念、配置方法和最佳实践,帮助开发者构建高度自动化的智能体工作流。通过飞书、GitHub、企业微信等实际案例,展示如何将 OpenClaw 无缝集成到现有业务系统中,实现从被动响应到主动触发的范式转变。🔒
1. 引言 - Webhook 与 Hooks 的区别与联系
1.1 概念辨析
在 OpenClaw 的自动化体系中,Webhook 和 Hooks 是两个容易混淆但本质不同的概念。理解它们的区别,是掌握 OpenClaw 自动化能力的第一步。🎯
Webhook(外部事件接入) 是一种 HTTP 端点机制,允许外部系统通过 HTTP 请求主动"唤醒"OpenClaw 智能体。它是一种"推"模式------外部事件发生时,由外部系统主动通知 OpenClaw,而不是 OpenClaw 轮询检查。这种模式大大降低了延迟,减少了不必要的资源消耗。
Hooks(内部事件钩子) 是一种事件驱动系统,在 OpenClaw 内部命令和生命周期事件触发时自动执行预定义的操作。它是一种"响应"模式------当用户发送 /new、/reset、/stop 等命令,或 Gateway 启动时,Hooks 会自动执行相应的处理逻辑。
1.2 架构定位对比
为了更清晰地理解两者的定位,我们可以从以下几个维度进行对比:
| 维度 | Webhook | Hooks |
|---|---|---|
| 事件来源 | 外部系统(飞书、GitHub、企业微信等) | 内部事件(命令、生命周期) |
| 触发方式 | HTTP POST 请求 | 事件监听器自动触发 |
| 执行环境 | 隔离的智能体会话 | Gateway 网关内部 |
| 主要用途 | 接入外部通知、触发自动化任务 | 会话管理、审计日志、状态保存 |
| 配置方式 | Gateway 配置文件 + HTTP 端点 | HOOK.md + handler.ts |
| 安全边界 | 需要令牌认证,请求体视为不可信 | 内部执行,继承 Gateway 权限 |
1.3 协同工作模式
Webhook 和 Hooks 并非孤立存在,它们可以协同工作,构建完整的自动化链路。例如:
HTTP POST
触发
执行任务
完成
消息渠道
触发
自动执行
自动执行
外部系统
飞书/GitHub
Webhook 端点
智能体会话
业务处理
发送响应
用户接收
用户命令
/new /reset
Hooks
会话快照
审计日志
在这个架构中,Webhook 负责打通外部世界,而 Hooks 负责维护内部秩序。两者相辅相成,共同构成了 OpenClaw 强大的自动化基础设施。
2. Webhook 机制详解 - 外部事件接入
2.1 核心架构
OpenClaw 的 Webhook 机制由 Gateway 网关提供,暴露一个轻量级的 HTTP 端点用于接收外部事件。其核心架构如下:
智能体 事件队列 Gateway 网关 外部系统 智能体 事件队列 Gateway 网关 外部系统 mode=now 时立即触发 POST /hooks/wake Authorization: Bearer token 验证令牌 加入系统事件 200 OK 触发心跳 读取事件 处理事件
2.2 启用与配置
要启用 Webhook 功能,需要在 Gateway 配置中添加以下设置:
json5
{
hooks: {
enabled: true,
token: "shared-secret", // 必填:用于认证的共享密钥
path: "/hooks", // 可选:端点路径,默认为 /hooks
},
}
配置要点:
- 🔑
hooks.enabled=true时,hooks.token为必填项 - 🛣️
hooks.path默认为/hooks,可根据需要自定义 - 🔒 建议使用强随机字符串作为 token,避免使用简单密码
2.3 认证机制
每个 Webhook 请求必须包含有效的认证令牌。OpenClaw 支持三种认证方式:
| 认证方式 | 格式 | 推荐程度 | 说明 |
|---|---|---|---|
| Authorization Header | Authorization: Bearer <token> |
⭐⭐⭐ 推荐 | 标准 OAuth 2.0 风格,最安全 |
| 自定义 Header | x-openclaw-token: <token> |
⭐⭐ 可用 | 兼容性方案 |
| Query 参数 | ?token=<token> |
⭐ 已弃用 | 会记录警告日志,未来版本将移除 |
安全建议 :始终使用 Authorization: Bearer 头传递令牌,避免令牌出现在 URL 或日志中。
2.4 端点详解
OpenClaw 提供了多个 Webhook 端点,满足不同场景的需求。
2.4.1 /hooks/wake - 唤醒主会话
/hooks/wake 端点用于向主会话添加系统事件,触发智能体处理:
json
{
"text": "收到新邮件,发件人:张三,主题:项目进度更新",
"mode": "now"
}
参数说明:
text(必填):事件描述文本,智能体将基于此理解发生了什么mode(可选):触发模式now(默认):立即触发心跳,智能体立即响应next-heartbeat:等待下一次定期心跳检查时处理
使用场景:
- 📧 邮件到达通知
- 📅 日程提醒
- 🔔 监控告警
- 📱 第三方应用通知
2.4.2 /hooks/agent - 运行隔离智能体
/hooks/agent 端点用于启动一个独立的智能体会话,适合需要隔离执行的任务:
json
{
"message": "总结今天收到的所有邮件,提取关键行动项",
"name": "Email",
"sessionKey": "hook:email:daily-summary",
"wakeMode": "now",
"deliver": true,
"channel": "last",
"model": "openai/gpt-5.2-mini",
"thinking": "low",
"timeoutSeconds": 120
}
参数详解:
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
message |
string | ✅ | 智能体要处理的提示或消息 |
name |
string | ❌ | Hook 的可读名称,用作会话摘要前缀 |
sessionKey |
string | ❌ | 会话标识键,默认为 hook:<uuid> |
wakeMode |
string | ❌ | 触发模式:now 或 next-heartbeat |
deliver |
boolean | ❌ | 是否发送响应到消息渠道,默认 true |
channel |
string | ❌ | 消息渠道:last、whatsapp、telegram 等 |
to |
string | ❌ | 接收者标识符(电话号码、聊天 ID 等) |
model |
string | ❌ | 模型覆盖,如 anthropic/claude-3-5-sonnet |
thinking |
string | ❌ | 思考级别:low、medium、high |
timeoutSeconds |
number | ❌ | 最大运行时间(秒) |
隔离执行的优势:
- 上下文隔离:不会污染主会话的历史记录
- 资源控制:可设置独立的超时和模型参数
- 会话持久化 :使用一致的
sessionKey可实现多轮对话 - 结果摘要:始终在主会话中发布执行摘要
2.4.3 /hooks/<name> - 自定义映射
通过配置 hooks.mappings,可以创建自定义的 Webhook 端点,将任意请求体转换为标准操作:
映射配置
POST /hooks/gmail
match.source=gmail
模板/代码转换
wake/agent
外部系统
映射匹配
请求体转换
生成标准请求
执行操作
match 条件
action 类型
transform 模块
2.5 响应状态码
| 状态码 | 含义 | 说明 |
|---|---|---|
200 |
成功 | /hooks/wake 请求成功 |
202 |
已接受 | /hooks/agent 异步任务已启动 |
401 |
认证失败 | 令牌无效或缺失 |
400 |
请求无效 | 请求体格式错误 |
413 |
请求过大 | 请求体超过大小限制 |
3. Hooks 机制详解 - 内部事件钩子
3.1 设计理念
Hooks 是 OpenClaw 内部的事件驱动系统,其设计灵感来源于 Unix 的管道哲学:做一件事,并做好它。每个 Hook 都是一个独立的小脚本,在特定事件发生时自动执行,无需修改核心代码。🚀
这种设计带来了几个关键优势:
- 可扩展性:通过添加新 Hook 扩展功能,而非修改核心
- 可组合性:多个 Hook 可以响应同一事件,互不干扰
- 可维护性:每个 Hook 独立开发、测试和部署
- 可发现性:自动发现机制,CLI 管理
3.2 Hook 发现机制
OpenClaw 从三个目录自动发现 Hooks,按优先级顺序加载:
发现流程
是
否
Gateway 启动
扫描目录
工作区 Hooks
/hooks/
托管 Hooks
~/.openclaw/hooks/
捆绑 Hooks
/dist/hooks/bundled/
解析 HOOK.md
检查资格
符合条件?
加载处理程序
跳过
注册事件监听
优先级规则:
- 工作区 Hooks(
<workspace>/hooks/):每智能体独立,最高优先级 - 托管 Hooks(
~/.openclaw/hooks/):用户安装,跨工作区共享 - 捆绑 Hooks(
<openclaw>/dist/hooks/bundled/):随 OpenClaw 附带
3.3 Hook 结构
每个 Hook 是一个独立的目录,包含两个核心文件:
my-hook/
├── HOOK.md # 元数据 + 文档
└── handler.ts # 处理程序实现
3.3.1 HOOK.md 格式
HOOK.md 文件使用 YAML frontmatter 定义元数据:
markdown
---
name: session-memory
description: "将会话上下文保存到记忆"
homepage: https://docs.openclaw.ai/automation/hooks#session-memory
metadata:
{
"openclaw": {
"emoji": "💾",
"events": ["command:new"],
"requires": { "config": ["workspace.dir"] }
}
}
---
# Session Memory Hook
当用户发出 `/new` 命令时,自动保存会话上下文到记忆文件。
## 功能
- 提取最后 15 行对话
- 使用 LLM 生成描述性文件名
- 保存到 `<workspace>/memory/YYYY-MM-DD-slug.md`
元数据字段详解:
| 字段 | 类型 | 说明 |
|---|---|---|
name |
string | Hook 的唯一标识符 |
description |
string | 简短描述 |
emoji |
string | CLI 显示的表情符号 |
events |
string[] | 监听的事件列表 |
requires.bins |
string[] | 需要的二进制文件 |
requires.env |
string[] | 需要的环境变量 |
requires.config |
string[] | 需要的配置路径 |
requires.os |
string[] | 支持的操作系统 |
3.3.2 处理程序实现
handler.ts 导出一个 HookHandler 函数:
typescript
import type { HookHandler } from "../../src/hooks/hooks.js";
const sessionMemoryHandler: HookHandler = async (event) => {
// 只处理 'new' 命令
if (event.type !== "command" || event.action !== "new") {
return;
}
console.log(`[session-memory] 保存会话上下文`);
console.log(` 会话: ${event.sessionKey}`);
console.log(` 时间: ${event.timestamp.toISOString()}`);
// 获取工作区目录
const workspaceDir = event.context.workspaceDir;
if (!workspaceDir) {
console.warn("[session-memory] 工作区目录未配置");
return;
}
// 提取会话上下文
const sessionEntry = event.context.sessionEntry;
if (!sessionEntry) {
console.warn("[session-memory] 无会话条目");
return;
}
// 保存到记忆文件
const memoryDir = path.join(workspaceDir, "memory");
await fs.mkdir(memoryDir, { recursive: true });
const date = new Date().toISOString().split("T")[0];
const memoryFile = path.join(memoryDir, `${date}-session.md`);
// 写入记忆
const content = generateMemoryContent(sessionEntry);
await fs.writeFile(memoryFile, content, "utf-8");
console.log(`[session-memory] 已保存到 ${memoryFile}`);
};
export default sessionMemoryHandler;
代码解析:
上述代码展示了 Hook 处理程序的核心模式。首先,通过事件类型和动作进行过滤,确保只在正确的时机执行。然后,从事件上下文中提取必要的信息(工作区目录、会话条目)。最后,执行实际的业务逻辑------创建记忆目录、生成文件名、写入内容。整个过程遵循"快速返回、优雅处理错误"的原则,避免阻塞主流程。
3.4 事件类型
OpenClaw 支持多种事件类型,覆盖智能体的完整生命周期:
3.4.1 命令事件
| 事件 | 触发时机 | 典型用途 |
|---|---|---|
command |
所有命令事件 | 审计日志、通用监听 |
command:new |
/new 命令 |
保存会话快照、重置前处理 |
command:reset |
/reset 命令 |
清理资源、状态重置 |
command:stop |
/stop 命令 |
取消任务、资源释放 |
3.4.2 智能体事件
| 事件 | 触发时机 | 典型用途 |
|---|---|---|
agent:bootstrap |
注入引导文件前 | 动态修改引导内容 |
3.4.3 Gateway 事件
| 事件 | 触发时机 | 典型用途 |
|---|---|---|
gateway:startup |
Gateway 启动后 | 初始化任务、运行 BOOT.md |
3.5 捆绑的 Hooks
OpenClaw 附带三个开箱即用的 Hooks:
3.5.1 session-memory
功能 :当用户发出 /new 时,自动保存会话上下文到记忆文件。
输出示例:
markdown
# Session: 2026-03-20 16:30:00 UTC
- **会话键**: agent:main:main
- **会话 ID**: abc123def456
- **来源**: telegram
- **发送者**: +1234567890
## 最后对话
用户: 帮我总结今天的邮件
智能体: 我已经检查了您的收件箱...
启用方式:
bash
openclaw hooks enable session-memory
3.5.2 command-logger
功能:将所有命令事件记录到审计日志文件。
日志格式(JSONL):
jsonl
{"timestamp":"2026-03-20T08:30:00.000Z","action":"new","sessionKey":"agent:main:main","senderId":"+1234567890","source":"telegram"}
{"timestamp":"2026-03-20T09:45:22.000Z","action":"stop","sessionKey":"agent:main:main","senderId":"user@example.com","source":"whatsapp"}
查看日志:
bash
# 查看最近命令
tail -n 20 ~/.openclaw/logs/commands.log
# 使用 jq 美化输出
cat ~/.openclaw/logs/commands.log | jq .
# 过滤特定动作
grep '"action":"new"' ~/.openclaw/logs/commands.log | jq .
3.5.3 boot-md
功能 :Gateway 启动时自动运行 BOOT.md 中的指令。
使用场景:
- 🌅 每日启动提醒
- 📊 自动检查系统状态
- 🔄 恢复中断的任务
4. Webhook 配置实战 - 飞书、GitHub、企业微信
4.1 飞书 Webhook 集成
飞书(Lark)是企业协作平台,通过 Webhook 可以将飞书机器人与 OpenClaw 连接。
4.1.1 配置步骤
第一步:创建飞书机器人
- 在飞书开放平台创建企业自建应用
- 获取 App ID 和 App Secret
- 配置事件订阅,设置请求地址为 OpenClaw Webhook 端点
第二步:配置 OpenClaw Webhook
json5
{
hooks: {
enabled: true,
token: "your-secure-token-here",
path: "/hooks",
mappings: {
"feishu": {
"match": {
"source": "feishu"
},
"action": "agent",
"transform": {
"template": {
"message": "{{body.event.message.content}}",
"name": "Feishu",
"sessionKey": "hook:feishu:{{body.event.sender.sender_id.user_id}}",
"channel": "last"
}
}
}
}
}
}
第三步:处理飞书事件
typescript
// transforms/feishu-transform.ts
export default function transformFeishuEvent(body: any) {
const eventType = body.header?.event_type;
const senderId = body.event?.sender?.sender_id?.user_id;
const message = body.event?.message?.content;
return {
message: `飞书消息: ${message}`,
name: "Feishu",
sessionKey: `hook:feishu:${senderId}`,
wakeMode: "now",
deliver: true,
channel: "last"
};
}
代码解析:
这个转换函数处理飞书推送的事件数据。首先提取事件类型、发送者 ID 和消息内容,然后构造 OpenClaw 标准的 agent 请求格式。sessionKey 使用发送者 ID 作为后缀,确保每个用户有独立的会话上下文。wakeMode: "now" 确保消息立即处理,deliver: true 让响应自动发送回飞书。
4.1.2 飞书事件处理流程
智能体 Transform 模块 OpenClaw Gateway 飞书服务器 智能体 Transform 模块 OpenClaw Gateway 飞书服务器 POST /hooks/feishu 事件数据 验证签名 转换请求体 标准 agent 请求 启动智能体 处理消息 响应结果 回调飞书 API
4.2 GitHub Webhook 集成
GitHub Webhook 可以让 OpenClaw 响应代码仓库事件,如 Push、Pull Request、Issue 等。
4.2.1 配置步骤
第一步:创建 GitHub Webhook
- 进入仓库 Settings → Webhooks → Add webhook
- Payload URL 设置为
http://your-server:18789/hooks/github - Content type 选择
application/json - Secret 设置为与 OpenClaw 配置相同的 token
- 选择要监听的事件类型
第二步:配置 OpenClaw
json5
{
hooks: {
enabled: true,
token: "github-webhook-secret",
mappings: {
"github": {
"match": {
"header": {
"x-github-event": "*"
}
},
"action": "agent",
"transform": {
"module": "./transforms/github-transform.ts"
}
}
}
}
}
第三步:实现转换模块
typescript
// transforms/github-transform.ts
interface GitHubWebhookBody {
action?: string;
repository: {
full_name: string;
html_url: string;
};
sender: {
login: string;
};
pull_request?: {
title: string;
number: number;
body: string;
};
issue?: {
title: string;
number: number;
body: string;
};
}
export default function transformGitHubEvent(
body: GitHubWebhookBody,
headers: Record<string, string>
) {
const eventType = headers["x-github-event"];
const repo = body.repository.full_name;
const sender = body.sender.login;
let message = "";
switch (eventType) {
case "pull_request":
message = `GitHub PR #${body.pull_request?.number}: ${body.pull_request?.title}\n` +
`仓库: ${repo}\n` +
`作者: ${sender}\n` +
`动作: ${body.action}`;
break;
case "issues":
message = `GitHub Issue #${body.issue?.number}: ${body.issue?.title}\n` +
`仓库: ${repo}\n` +
`作者: ${sender}\n` +
`动作: ${body.action}`;
break;
case "push":
message = `GitHub Push to ${repo}\n` +
`推送者: ${sender}`;
break;
default:
message = `GitHub 事件: ${eventType} on ${repo}`;
}
return {
message,
name: "GitHub",
sessionKey: `hook:github:${repo}`,
wakeMode: "now",
deliver: true
};
}
代码解析:
这个转换模块处理多种 GitHub 事件类型。通过检查 x-github-event 头判断事件类型,然后提取相关信息构造描述性消息。对于 Pull Request 事件,提取 PR 编号、标题和动作;对于 Issue 事件,提取 Issue 编号和标题;对于 Push 事件,提取推送者信息。sessionKey 使用仓库名作为后缀,确保每个仓库有独立的会话上下文。
4.3 企业微信 Webhook 集成
企业微信(WeCom)是企业级通讯工具,通过 Webhook 可以将企业微信机器人与 OpenClaw 连接。
4.3.1 配置步骤
第一步:创建企业微信机器人
- 在企业微信管理后台创建群机器人
- 获取 Webhook URL 和签名密钥
第二步:配置 OpenClaw
json5
{
hooks: {
enabled: true,
token: "wecom-webhook-secret",
mappings: {
"wecom": {
"match": {
"source": "wecom"
},
"action": "agent",
"transform": {
"module": "./transforms/wecom-transform.ts"
},
"deliver": true,
"channel": "last"
}
}
}
}
第三步:实现签名验证
typescript
// transforms/wecom-transform.ts
import crypto from "crypto";
function verifyWecomSignature(
body: string,
timestamp: string,
nonce: string,
signature: string,
token: string
): boolean {
// 企业微信签名验证算法
const arr = [token, timestamp, nonce].sort();
const sha1 = crypto.createHash("sha1");
sha1.update(arr.join(""));
return sha1.digest("hex") === signature;
}
export default function transformWecomEvent(
body: any,
headers: Record<string, string>
) {
const timestamp = headers["timestamp"];
const nonce = headers["nonce"];
const signature = headers["msg_signature"];
// 验证签名(可选,建议启用)
// if (!verifyWecomSignature(JSON.stringify(body), timestamp, nonce, signature, "your-token")) {
// throw new Error("签名验证失败");
// }
const message = body.Content || body.Event || "企业微信事件";
const fromUser = body.FromUserName;
return {
message: `企业微信消息: ${message}`,
name: "WeCom",
sessionKey: `hook:wecom:${fromUser}`,
wakeMode: "now",
deliver: true,
channel: "last"
};
}
代码解析:
企业微信的签名验证是安全的关键。这个模块实现了标准的签名验证算法:将 token、timestamp、nonce 按字典序排序后拼接,计算 SHA1 哈希值,与传入的签名比对。验证通过后,提取消息内容和发送者,构造 OpenClaw 标准请求格式。sessionKey 使用发送者 ID 作为后缀,确保每个用户有独立的会话。
5. Hooks 开发指南 - 自定义钩子函数
5.1 创建自定义 Hook
让我们通过一个实际案例------自动备份 Hook,来学习如何创建自定义 Hook。
5.1.1 需求分析
目标 :当用户发出 /reset 命令时,自动备份当前会话到指定目录。
要求:
- 备份文件以时间戳命名
- 包含会话元数据和对话历史
- 支持配置备份目录
5.1.2 创建 Hook 目录
bash
mkdir -p ~/.openclaw/hooks/session-backup
cd ~/.openclaw/hooks/session-backup
5.1.3 编写 HOOK.md
markdown
---
name: session-backup
description: "重置会话前自动备份会话数据"
metadata:
{
"openclaw": {
"emoji": "💾",
"events": ["command:reset"],
"requires": { "config": ["workspace.dir"] }
}
}
---
# Session Backup Hook
在用户发出 `/reset` 命令时,自动备份当前会话数据。
## 功能
- 创建备份目录(如果不存在)
- 生成带时间戳的备份文件名
- 保存会话元数据和对话历史
- 记录备份日志
## 配置
在 Gateway 配置中设置:
{
"hooks": {
"internal": {
"entries": {
"session-backup": {
"enabled": true,
"env": {
"BACKUP_DIR": "/path/to/backups"
}
}
}
}
}
}
## 输出
备份文件保存在 `$BACKUP_DIR/YYYY-MM-DD/HH-MM-SS-session.json`
5.1.4 编写处理程序
typescript
// handler.ts
import type { HookHandler } from "../../src/hooks/hooks.js";
import fs from "fs/promises";
import path from "path";
const sessionBackupHandler: HookHandler = async (event) => {
// 只处理 reset 命令
if (event.type !== "command" || event.action !== "reset") {
return;
}
console.log("[session-backup] 开始备份会话...");
// 获取配置
const backupDir = process.env.BACKUP_DIR ||
path.join(event.context.workspaceDir || "", "backups");
const sessionEntry = event.context.sessionEntry;
if (!sessionEntry) {
console.warn("[session-backup] 无会话条目,跳过备份");
return;
}
try {
// 创建备份目录
const now = new Date();
const dateDir = now.toISOString().split("T")[0];
const timeStr = now.toTimeString().split(" ")[0].replace(/:/g, "-");
const backupPath = path.join(backupDir, dateDir);
await fs.mkdir(backupPath, { recursive: true });
// 构造备份数据
const backupData = {
timestamp: now.toISOString(),
sessionKey: event.sessionKey,
sessionId: event.context.sessionId,
source: event.context.commandSource,
senderId: event.context.senderId,
history: sessionEntry.history || [],
metadata: {
messageCount: sessionEntry.history?.length || 0,
workspaceDir: event.context.workspaceDir
}
};
// 写入备份文件
const backupFile = path.join(backupPath, `${timeStr}-session.json`);
await fs.writeFile(
backupFile,
JSON.stringify(backupData, null, 2),
"utf-8"
);
console.log(`[session-backup] 备份完成: ${backupFile}`);
// 通知用户
event.messages.push(`💾 会话已备份到: ${backupFile}`);
} catch (err) {
console.error("[session-backup] 备份失败:", err);
// 不抛出错误,让其他 handlers 继续执行
}
};
export default sessionBackupHandler;
代码解析:
这个 Hook 实现了完整的会话备份功能。首先,通过事件过滤确保只在 /reset 命令时执行。然后,从环境变量或工作区目录获取备份路径,创建按日期组织的目录结构。备份数据包含会话的完整信息:时间戳、会话键、来源、发送者 ID、对话历史和元数据。最后,将数据写入 JSON 文件,并通过 event.messages 通知用户备份完成。整个过程使用 try-catch 包装,确保错误不会中断其他 Hooks 的执行。
5.2 测试与调试
5.2.1 验证 Hook 发现
bash
# 列出所有发现的 Hooks
openclaw hooks list
# 查看详细信息
openclaw hooks info session-backup
# 检查资格
openclaw hooks check
5.2.2 启用并测试
bash
# 启用 Hook
openclaw hooks enable session-backup
# 重启 Gateway
openclaw gateway restart
# 触发事件(发送 /reset 命令)
# 检查备份目录
ls -la ~/.openclaw/workspace/backups/
5.2.3 查看日志
bash
# 查看 Gateway 日志
tail -f ~/.openclaw/gateway.log | grep session-backup
# 或使用脚本(macOS)
./scripts/clawlog.sh -f | grep session-backup
5.3 最佳实践
5.3.1 保持处理程序快速
typescript
// ✅ 好的做法 - 异步执行,立即返回
const handler: HookHandler = async (event) => {
void processInBackground(event); // Fire and forget
};
// ❌ 不好的做法 - 阻塞命令处理
const handler: HookHandler = async (event) => {
await slowDatabaseQuery(event);
await evenSlowerAPICall(event);
};
5.3.2 优雅处理错误
typescript
const handler: HookHandler = async (event) => {
try {
await riskyOperation(event);
} catch (err) {
console.error("[my-handler] 失败:", err instanceof Error ? err.message : String(err));
// 不要抛出错误 - 让其他 handlers 继续执行
}
};
5.3.3 尽早过滤事件
typescript
const handler: HookHandler = async (event) => {
// 只处理特定事件
if (event.type !== "command" || event.action !== "new") {
return;
}
// 你的逻辑...
};
6. 安全与最佳实践
6.1 Webhook 安全
6.1.1 令牌管理
| 实践 | 说明 |
|---|---|
| 🔑 使用强随机令牌 | 至少 32 字符的随机字符串 |
| 🔄 定期轮换 | 建议每 90 天更换一次 |
| 🚫 不复用 | 不要复用 Gateway 认证令牌 |
| 📝 安全存储 | 使用环境变量或密钥管理服务 |
6.1.2 网络安全
安全边界
HTTPS
HTTP
外部系统
反向代理
Nginx/Caddy
OpenClaw Gateway
localhost:18789
防火墙规则
TLS 终止
请求过滤
建议配置:
- 将 Webhook 端点保持在 loopback、tailnet 或受信任的反向代理之后
- 使用 HTTPS 加密传输
- 配置 IP 白名单(如果可能)
- 启用请求速率限制
6.1.3 请求体验证
OpenClaw 默认将 Webhook 请求体视为不可信,使用安全边界包装。如果需要访问原始请求体:
json5
{
hooks: {
mappings: {
"trusted-source": {
"match": { "source": "internal-system" },
"allowUnsafeExternalContent": true // 危险!仅用于受信任的内部来源
}
}
}
}
6.2 Hooks 安全
6.2.1 权限隔离
Hooks 在 Gateway 进程内执行,继承 Gateway 的权限。建议:
- 📁 限制文件系统访问范围
- 🔐 避免在 Hooks 中存储敏感信息
- 📋 审计 Hook 代码,确保没有恶意操作
6.2.2 资源控制
typescript
// 设置超时
const handler: HookHandler = async (event) => {
const timeout = setTimeout(() => {
throw new Error("操作超时");
}, 5000);
try {
await yourOperation(event);
} finally {
clearTimeout(timeout);
}
};
6.3 监控与日志
6.3.1 日志配置
json5
{
logging: {
level: "info",
hooks: {
enabled: true,
includePayload: false // 不记录敏感的请求体
}
}
}
6.3.2 监控指标
| 指标 | 说明 | 告警阈值 |
|---|---|---|
| Webhook 请求量 | 每分钟请求数 | > 100/min |
| 认证失败率 | 401 响应占比 | > 5% |
| Hook 执行时间 | 平均执行时长 | > 1s |
| Hook 错误率 | 执行失败占比 | > 1% |
7. 总结
OpenClaw 的 Webhook 与 Hooks 机制共同构建了一套完整的事件驱动自动化体系,为智能体系统提供了强大的扩展能力。通过本文的深入剖析,我们可以得出以下关键结论:
Webhook 的核心价值 在于打通外部世界与 OpenClaw 的连接。它采用标准的 HTTP 协议,支持多种认证方式,提供了灵活的请求映射和转换机制。无论是飞书、GitHub 还是企业微信,都可以通过简单的配置实现与 OpenClaw 的无缝集成。/hooks/wake 端点适合轻量级的事件通知,而 /hooks/agent 端点则支持隔离的智能体执行,满足不同场景的需求。
Hooks 的核心价值在于提供内部事件的响应能力。通过自动发现机制和标准化的目录结构,开发者可以轻松创建、部署和管理自定义 Hooks。三个捆绑的 Hooks(session-memory、command-logger、boot-md)展示了 Hooks 的典型应用场景,为开发者提供了良好的参考范例。
安全是自动化的基石。无论是 Webhook 的令牌认证和网络隔离,还是 Hooks 的权限控制和资源限制,都需要开发者给予足够的重视。通过遵循最佳实践,可以在享受自动化便利的同时,确保系统的安全性和稳定性。
实践建议:从小规模开始,先启用捆绑的 Hooks 熟悉机制,再逐步添加自定义功能。对于 Webhook 集成,建议先在测试环境验证,确保签名验证和错误处理正确后再部署到生产环境。定期审查日志和监控指标,及时发现和解决潜在问题。
OpenClaw 的事件驱动架构体现了现代智能体系统的设计哲学:开放、可扩展、安全。掌握 Webhook 和 Hooks,就是掌握了 OpenClaw 自动化能力的钥匙。🚀