0

ch-10

约 15000 字DraftOpenClaw Book

第 10 章 · 自动化体系

你雇佣了一个助理,但你还得每次手动告诉他"检查邮件"、"整理日报"、"跟进客户"——这不是助理,这是复读机。OpenClaw 的自动化体系,就是让 Agent 从"等你说话"进化到"自己知道什么时候该干什么"。

本章你将学到:

  • 理解 Heartbeat 和 Cron 两种调度机制的本质区别和选择策略
  • 掌握 Cron 作业的完整生命周期——从创建、执行、投递到重试
  • 学会用 Webhook 把外部系统接入 Agent 的工作流
  • 理解 Hooks 事件驱动系统,实现"发生了什么就自动做什么"
  • 能设计包含常驻指令(Standing Orders)的完整自主工作流

10.1 自动化的三种时间模型

在深入具体功能之前,你需要理解 OpenClaw 自动化的三种时间模型——它们解决的是不同的问题:

┌───────────────────────────────────────────────────────────┐
│                    自动化三模型                            │
├───────────────────┬───────────────────┬───────────────────┤
│    Heartbeat      │      Cron         │     Webhook       │
│   周期性"巡逻"     │   精确"闹钟"       │   事件"门铃"       │
├───────────────────┼───────────────────┼───────────────────┤
│ 每 30 分钟醒来     │ 每天早上 7:00     │ 收到邮件时触发      │
│ 看一眼有什么事     │ 执行指定任务       │ 外部系统推送事件    │
│ 没事就回去睡觉     │ 可以只跑一次       │ Agent 被动响应     │
│ 上下文共享         │ 可以隔离会话       │ 独立会话处理       │
└───────────────────┴───────────────────┴───────────────────┘

Heartbeat(心跳)是 Agent 的"自动巡逻"——每隔一段时间醒来,按检查清单扫一遍,有情况就报告,没情况就回去睡觉。

Cron(定时任务)是 Agent 的"精确闹钟"——在指定时间点触发指定任务,精确到分钟,支持一次性提醒和周期性执行。

Webhook(网络钩子)是 Agent 的"门铃"——外部系统(邮件、GitHub、监控)有事情发生时,通过 HTTP 请求"按门铃",Agent 被唤醒处理。

三者不是互斥的——一个成熟的自动化系统通常是三者的组合。本章的后续章节会逐一深入,但先记住这个决策框架:

需要精确时间? → Cron
需要批量检查? → Heartbeat
需要外部触发? → Webhook
不确定? → 继续读下去

10.2 Heartbeat:Agent 的"自动巡逻"

10.2.1 心跳的工作原理

Heartbeat 是 OpenClaw 中最"无感"的自动化机制。它的核心思路很简单:每隔一段时间,自动在主会话中插入一条系统消息,触发 Agent 执行一次检查。

时间线:
  08:00  [用户发消息] → Agent 正常回复
  08:30  [Heartbeat 触发] → Agent 检查 HEARTBEAT.md → 回复 HEARTBEAT_OK(无消息发送)
  09:00  [Heartbeat 触发] → Agent 检查 → 发现新邮件 → 发送提醒
  09:30  [Heartbeat 触发] → Agent 检查 → 回复 HEARTBEAT_OK
  10:00  [用户发消息] → Agent 正常回复

关键机制:如果 Agent 判断"没什么事",会回复 HEARTBEAT_OK。OpenClaw 识别到这个信号后,不会把消息发送给你——所以你完全感知不到这次心跳的发生。

10.2.2 配置心跳

默认心跳间隔是 30 分钟。如果你用的是 Anthropic OAuth 认证,默认间隔会自动调整为 1 小时(更省 Token)。

{
  agents: {
    defaults: {
      heartbeat: {
        every: "30m",           // 心跳间隔(设为 "0m" 禁用)
        target: "last",         // 投递到最近联系的通道(默认 "none" 不投递)
        activeHours: {          // 只在活跃时段运行
          start: "08:00",
          end: "22:00",
        },
      },
    },
  },
}

target 选项

行为
none 心跳照常运行,但不发送任何消息(默认)
last 发送到最近一次对话的通道
whatsapp / telegram / discord 发送到指定通道
具体通道 ID 发送到指定通道账号

10.2.3 HEARTBEAT.md:心跳检查清单

心跳的核心配置不是在 JSON 里,而是在工作空间中的 HEARTBEAT.md 文件。这个文件定义了 Agent 每次醒来要检查什么。

# Heartbeat checklist

- Quick scan: anything urgent in inboxes?
- If it's daytime, do a lightweight check-in if nothing else is pending.
- If a task is blocked, write down _what is missing_ and ask Peter next time.

写作原则

  • 保持简短——每次心跳都会把 HEARTBEAT.md 注入上下文,太长浪费 Token
  • 只写可执行的检查项,不写模糊的"关注一下"
  • 不要放 API Key、手机号等敏感信息

💡 提示:你可以随时让 Agent 修改 HEARTBEAT.md。比如在聊天中说"把日历检查加到心跳清单里",Agent 会直接更新文件。

10.2.4 节省心跳成本

心跳每次都是一次完整的 Agent Turn,默认间隔 30 分钟意味着每天 48 次。如果你用的是 Opus 模型,这就是一笔不小的开销。几个省钱的配置:

{
  agents: {
    defaults: {
      heartbeat: {
        every: "30m",
        lightContext: true,       // 只注入 HEARTBEAT.md,不注入其他工作空间文件
        isolatedSession: true,    // 每次心跳用全新会话,不发完整对话历史
        model: "openai/gpt-5.2-mini", // 用便宜模型做心跳检查
        target: "none",           // 只做内部状态更新,不发送消息
      },
    },
  },
}
配置 效果 省钱幅度
lightContext: true 只注入 HEARTBEAT.md 中等(省去 SOUL.md 等文件)
isolatedSession: true 不发送对话历史 巨大(100K → 2-5K Token)
model: "便宜模型" 用低配模型检查 巨大(Opus → Mini 差 10 倍)
target: "none" 不发送外部消息 中等(省去发送步骤)

⚠️ 踩坑记录

问题:开启了 isolatedSession: true 后,Agent 似乎"忘了"之前的对话。

原因:这正是预期行为。隔离会话每次都是全新的,Agent 看不到之前的聊天记录。

解决:如果你需要 Agent 在心跳中记住上下文,不要开 isolatedSession。或者把需要记住的关键信息写入工作空间的记忆文件。

10.2.5 活跃时段

你不会想让 Agent 凌晨三点发心跳消息吵醒你:

{
  agents: {
    defaults: {
      heartbeat: {
        every: "30m",
        activeHours: {
          start: "09:00",       // 包含(09:00 开始运行)
          end: "22:00",         // 不包含(22:00 停止运行)
          timezone: "America/New_York",
        },
      },
    },
  },
}

时区处理

  • timezone 省略时,使用 userTimezone 配置,再省略则用主机时区
  • startend 相同时(比如都是 08:00)会被视为零宽度窗口,心跳永远跳过

10.2.6 手动唤醒

有时候你不想等心跳,想让 Agent 立刻检查一下:

# 立即触发心跳
openclaw system event --text "Check for urgent follow-ups" --mode now

# 排队到下一次心跳
openclaw system event --text "Check battery level" --mode next-heartbeat

--mode now 会立即触发 Agent 运行。如果你有多个 Agent 配置了心跳,手动唤醒会同时触发所有 Agent。

10.2.7 多 Agent 心跳

当你的系统中有多个 Agent 时,可以精确控制哪些 Agent 跑心跳、各自的间隔和目标:

{
  agents: {
    defaults: {
      heartbeat: {
        every: "30m",
        target: "last",
      },
    },
    list: [
      { id: "main", default: true }, // main Agent 不单独配心跳,继承 defaults
      {
        id: "ops",
        heartbeat: {
          every: "1h",                 // ops Agent 每小时一次
          target: "whatsapp",
          to: "+15551234567",
          prompt: "Read HEARTBEAT.md. Check server health and alert queue. Report anomalies.",
        },
      },
    ],
  },
}

重要规则:一旦任何 agents.list[] 中包含了 heartbeat 配置,只有配置了心跳的 Agent 会运行心跳。没有配置的 Agent(如上面的 main)会被跳过。

10.3 Cron 作业:精确"闹钟"

如果说 Heartbeat 是"巡逻",Cron 就是"闹钟"——在精确的时间点执行精确的任务。

10.3.1 两种执行模式

Cron 作业有两种执行模式,对应不同的使用场景:

┌──────────────────────────────────────────────────────┐
│  主会话模式(systemEvent)                            │
│  ┌────────────────────────────────────────────────┐  │
│  │ [主会话] ← 系统事件入队 → 下次心跳时处理         │  │
│  │                                                │  │
│  │ 适合:提醒、轻量触发                             │  │
│  │ 优点:共享主会话上下文                           │  │
│  │ 缺点:依赖心跳周期                              │  │
│  └────────────────────────────────────────────────┘  │
├──────────────────────────────────────────────────────┤
│  隔离模式(agentTurn)                                │
│  ┌────────────────────────────────────────────────┐  │
│  │ [cron:jobId] → 独立 Agent Turn → 结果投递       │  │
│  │                                                │  │
│  │ 适合:复杂任务、定时报告                         │  │
│  │ 优点:不污染主会话、可覆盖模型                    │  │
│  │ 缺点:没有主会话上下文                           │  │
│  └────────────────────────────────────────────────┘  │
└──────────────────────────────────────────────────────┘

10.3.2 快速上手:创建第一个 Cron 作业

场景 1:一次性提醒(20 分钟后提醒我打电话)

openclaw cron add \
  --name "Call back client" \
  --at "20m" \
  --session main \
  --system-event "Reminder: call back the client about the proposal." \
  --wake now \
  --delete-after-run

执行后会看到作业 ID。验证一下:

openclaw cron list

预期输出:

Jobs (1):
  ✓ call-back-client  [at]  2026-03-28T14:20:00Z  session=main  wake=now

场景 2:每天早上的简报(隔离模式,发送到 Slack)

openclaw cron add \
  --name "Morning brief" \
  --cron "0 7 * * *" \
  --tz "America/New_York" \
  --session isolated \
  --message "Summarize overnight updates: check inbox, calendar, and project status." \
  --announce \
  --channel slack \
  --to "channel:C1234567890"

场景 3:每周深度分析(用更强的模型)

openclaw cron add \
  --name "Weekly deep analysis" \
  --cron "0 6 * * 1" \
  --tz "America/Los_Angeles" \
  --session isolated \
  --message "Weekly deep analysis of project progress and code quality." \
  --model "opus" \
  --thinking "high" \
  --announce \
  --channel whatsapp \
  --to "+15551234567"

10.3.3 三种调度类型

类型 用法 适用场景
at --at "2026-03-28T16:00:00Z"--at "20m" 一次性提醒
every --every "30m" 固定间隔轮询
cron --cron "0 7 * * *" 标准定时表达式

Cron 表达式:支持 5 字段(分 时 日 月 周)和 6 字段(秒 分 时 日 月 周),使用 croner 库解析。

时区注意:如果 ISO 时间戳不带时区,会被当作 UTC。建议始终用 --tz 明确指定。

10.3.4 四种会话目标

Cron 作业可以运行在不同的会话上下文中:

// 主会话——注入系统事件,由心跳处理
{ sessionTarget: "main" }

// 隔离会话——每次全新上下文
{ sessionTarget: "isolated" }

// 当前会话——绑定到创建时的会话(创建时解析为 session:<key>)
{ sessionTarget: "current" }

// 自定义持久会话——跨次运行保持上下文
{ sessionTarget: "session:project-alpha-monitor" }

自定义持久会话是最容易被忽视但最强大的选项。它让 Cron 作业在多次运行之间保持上下文——比如每日站会可以"记住"昨天的进度:

{
  "name": "Daily standup",
  "schedule": { "kind": "cron", "expr": "0 9 * * 1-5" },
  "sessionTarget": "session:daily-standup",  // 持久会话,上下文累积
  "payload": {
    "kind": "agentTurn",
    "message": "Summarize yesterday's progress and plan today's priorities."
  }
}

10.3.5 投递控制

隔离模式的 Cron 作业默认会把结果摘要发送到指定通道(announce 模式)。三种投递模式:

模式 行为
announce 发送摘要到指定通道 + 在主会话中记录(默认)
webhook POST 结果到指定 URL
none 内部运行,不发送任何消息

Announce 投递细节

  • 使用隔离运行的外发内容(文本/媒体),走正常的分块和通道格式化
  • 如果 Agent 只回复了 HEARTBEAT_OK(没有实质内容),不会投递
  • 如果 Agent 已经通过 message 工具发了消息到同一目标,不会重复投递

Webhook 投递

{
  "delivery": {
    "mode": "webhook",
    "to": "https://your-api.example.com/cron-results"
  }
}

如果配置了 cron.webhookToken,请求会携带 Authorization: Bearer <token>

10.3.6 模型和思考级别覆盖

隔离模式的 Cron 作业可以覆盖模型和思考级别——这是 Heartbeat 做不到的:

# 用便宜模型做日常检查
openclaw cron add --name "Health check" --cron "*/30 * * * *" \
  --session isolated --model "openai/gpt-5.2-mini" \
  --message "Check service health."

# 用强模型做周报分析
openclaw cron add --name "Weekly review" --cron "0 9 * * 1" \
  --session isolated --model "opus" --thinking "high" \
  --message "Deep weekly analysis."

模型覆盖的优先级:作业 payload 覆盖 > Hook 特定默认值 > Agent 配置默认值。

10.3.7 负载分散(Stagger)

如果多个 Cron 作业都设在整点运行(0 * * * *),会造成瞬间 API 压力。OpenClaw 会自动给整点周期的任务加最多 5 分钟的随机偏移。

手动控制偏移:

# 强制精确时间(不偏移)
openclaw cron add --name "Exact" --cron "0 * * * *" --exact ...

# 自定义 30 秒偏移
openclaw cron add --name "Staggered" --cron "0 * * * *" --stagger "30s" ...

⚠️ 注意:固定小时表达式(如 0 7 * * * 每天 7 点)不受自动偏移影响,时间精确。

10.3.8 重试策略

Cron 作业失败时,OpenClaw 会区分可恢复错误永久错误

可恢复错误(会重试):

  • 速率限制(429)
  • Provider 过载(529)
  • 网络错误(超时、连接重置)
  • 服务器错误(5xx)

永久错误(立即禁用):

  • 认证失败(API Key 无效)
  • 配置或验证错误

默认行为

作业类型 重试策略
一次性(at 指数退避重试 3 次(30s → 1m → 5m),永久错误立即禁用
周期性(cron/every 指数退避(30s → 1m → 5m → 15m → 60m),下次成功后重置

自定义重试策略:

{
  cron: {
    retry: {
      maxAttempts: 5,
      backoffMs: [30000, 60000, 120000, 300000, 600000],
      retryOn: ["rate_limit", "overloaded", "network", "server_error"],
    },
  },
}

10.3.9 存储、历史和维护

Cron 作业的存储结构:

~/.openclaw/cron/
├── jobs.json              # 作业定义(Gateway 管理)
└── runs/
    ├── <jobId>.jsonl      # 每次运行的日志(JSONL 格式)
    └── ...

维护配置

{
  cron: {
    sessionRetention: "24h",    // 隔离运行会话保留时间
    runLog: {
      maxBytes: "2mb",          // 运行日志文件最大大小
      keepLines: 2000,          // 裁剪后保留的行数
    },
  },
}

查看运行历史:

openclaw cron runs --id <jobId> --limit 50

⚠️ 踩坑记录

问题:手动编辑 jobs.json 后,Gateway 重启时 Cron 作业丢失或冲突。

原因:Gateway 运行时会加载 jobs.json 到内存,修改后写回。如果 Gateway 正在运行,手动编辑会被覆盖。

解决:始终用 openclaw cron add/edit 或 Cron 工具 API 修改作业。如果必须手动编辑,先停止 Gateway。

10.3.10 Agent 绑定

在多 Agent 系统中,可以把 Cron 作业绑定到特定 Agent:

# 创建时绑定
openclaw cron add --name "Ops sweep" --cron "0 6 * * *" \
  --session isolated --message "Check ops queue" --agent ops

# 修改现有作业的绑定
openclaw cron edit <jobId> --agent ops
openclaw cron edit <jobId> --clear-agent

如果指定的 Agent 不存在,作业会回退到默认 Agent。

10.4 Heartbeat vs Cron:选择指南

这是 OpenClaw 自动化中最常见的困惑点。用一个决策树来帮你快速判断:

任务需要精确时间执行吗?
├─ YES → 用 Cron
└─ NO → 继续...
    │
    任务需要和主会话隔离吗?
    ├─ YES → 用 Cron(隔离模式)
    └─ NO → 继续...
        │
        能和其他检查批量执行吗?
        ├─ YES → 用 Heartbeat(加到 HEARTBEAT.md)
        └─ NO → 用 Cron
            │
            是一次性提醒吗?
            ├─ YES → 用 Cron(--at)
            └─ NO → 用 Heartbeat
                │
                需要不同的模型或思考级别?
                ├─ YES → 用 Cron(隔离模式 + --model)
                └─ NO → 用 Heartbeat

实际场景对照表

场景 推荐 原因
每 30 分钟检查收件箱 Heartbeat 批量检查,上下文感知
每天早上 9 点发日报 Cron(隔离) 精确时间,独立任务
20 分钟后提醒我开会 Cron(主会话) 一次性精确提醒
监控日历即将到来的事件 Heartbeat 周期性感知,自然适配
每周深度代码审查 Cron(隔离) 独立任务,用强模型
后台项目健康检查 Heartbeat 搭载已有周期

最佳实践:两者结合

最高效的自动化系统同时使用两种机制:

  1. Heartbeat 处理日常监控(收件箱、日历、通知),每次心跳一个 Turn 批量完成
  2. Cron 处理精确调度(日报、周报、一次性提醒)

这样既不会错过精确时间要求,又能把多个零散检查合并成一次 API 调用。

成本对比

机制 成本特征
Heartbeat 每个 N 分钟一次 Turn;成本随 HEARTBEAT.md 大小增长
Cron(主会话) 下次心跳时处理,不产生额外的隔离 Turn
Cron(隔离) 每个作业一次完整 Turn;可以用便宜模型

10.5 Webhook:外部系统的"门铃"

Heartbeat 和 Cron 都是 Agent 主动发起的。但如果外部系统(邮件、GitHub、监控)有事情发生呢?你需要一个"门铃"——Webhook。

10.5.1 启用 Webhook

{
  hooks: {
    enabled: true,
    token: "your-shared-secret",  // 必须!每个请求都需要携带
    path: "/hooks",                // 默认路径
  },
}

⚠️ 安全警告hooks.token 是 Webhook 的唯一认证。把它当作密码——不要和 Gateway 认证 Token 复用,定期更换。

10.5.2 两个核心端点

POST /hooks/wake — 唤醒主会话

curl -X POST http://127.0.0.1:18789/hooks/wake \
  -H 'Authorization: Bearer SECRET' \
  -H 'Content-Type: application/json' \
  -d '{"text":"New email received","mode":"now"}'

效果:向主会话注入一个系统事件。mode: "now" 立即触发心跳,mode: "next-heartbeat" 等待下次心跳。

POST /hooks/agent — 运行独立 Agent Turn

curl -X POST http://127.0.0.1:18789/hooks/agent \
  -H 'Authorization: Bearer SECRET' \
  -H 'Content-Type: application/json' \
  -d '{
    "message": "Summarize new emails",
    "name": "Email",
    "sessionKey": "hook:email:msg-123",
    "wakeMode": "now",
    "deliver": true,
    "channel": "last",
    "model": "openai/gpt-5.2-mini"
  }'

效果:在独立会话中运行 Agent Turn,结果摘要发布到主会话。

关键参数

参数 说明
message 发送给 Agent 的提示(必填)
name Hook 名称,用于会话摘要前缀
sessionKey 自定义会话键(默认拒绝,需开启 allowRequestSessionKey
wakeMode now(立即唤醒)或 next-heartbeat
deliver 是否把 Agent 回复发送到聊天通道
channel / to 投递通道和目标
model 模型覆盖
thinking 思考级别覆盖
timeoutSeconds 超时时间
agentId 路由到特定 Agent

10.5.3 会话键安全策略

sessionKey 允许调用者自定义会话,这是一个安全风险。OpenClaw 默认拒绝请求中的 sessionKey

推荐配置

{
  hooks: {
    enabled: true,
    token: "${OPENCLAW_HOOK_TOKEN}",
    defaultSessionKey: "hook:ingress",       // 所有 Hook 使用固定会话
    allowRequestSessionKey: false,            // 禁止调用者自定义
    allowedSessionKeyPrefixes: ["hook:"],    // 即使开启,也限制前缀
  },
}

10.5.4 自定义 Hook 映射

POST /hooks/<name> 端点通过映射表把外部请求转换为 Agent 操作:

{
  hooks: {
    enabled: true,
    token: "${OPENCLAW_HOOK_TOKEN}",
    presets: ["gmail"],  // 启用内置 Gmail 映射
    mappings: [
      {
        match: { path: "github" },
        action: "agent",
        name: "GitHub",
        messageTemplate: "New GitHub event: {{action}} on {{repo}}\n{{body}}",
        deliver: true,
        channel: "slack",
        to: "channel:C1234567890",
      },
    ],
  },
}

映射可以把任意外部 payload 转换为 Agent 可理解的 prompt。你还可以用 JS/TS 模块做更复杂的转换:

{
  hooks: {
    transformsDir: "~/.openclaw/hooks/transforms",
    mappings: [
      {
        match: { path: "custom" },
        action: "agent",
        transform: { module: "my-transform" },
        deliver: true,
      },
    ],
  },
}

10.5.5 Gmail Pub/Sub 集成实战

Gmail 是最常见的 Webhook 场景——收到邮件时自动让 Agent 处理。OpenClaw 提供了完整的 Gmail Pub/Sub 集成。

一键设置(推荐)

openclaw webhooks gmail setup --account openclaw@gmail.com

这个命令会自动:

  1. 安装依赖(macOS 通过 Homebrew)
  2. 创建 GCP Pub/Sub Topic
  3. 配置 Gmail Watch
  4. 通过 Tailscale Funnel 暴露公共 HTTPS 端点
  5. 写入 hooks.gmail 配置

手动配置

{
  hooks: {
    enabled: true,
    token: "${OPENCLAW_HOOK_TOKEN}",
    presets: ["gmail"],
    mappings: [
      {
        match: { path: "gmail" },
        action: "agent",
        wakeMode: "now",
        name: "Gmail",
        sessionKey: "hook:gmail:{{messages[0].id}}",
        messageTemplate: "New email from {{messages[0].from}}\nSubject: {{messages[0].subject}}\n{{messages[0].snippet}}",
        model: "openai/gpt-5.2-mini",  // 用便宜模型处理邮件
        deliver: true,
        channel: "last",
      },
    ],
  },
}

Gmail 专用配置

{
  hooks: {
    gmail: {
      model: "openrouter/meta-llama/llama-3.3-70b-instruct:free",
      thinking: "off",
    },
  },
}

⚠️ 踩坑记录

问题:Gmail Hook 收到邮件后没有触发 Agent。

原因:Pub/Sub Topic 和 OAuth Client 不在同一个 GCP 项目中。

解决:确保 gcloud config set project 指向的是 OAuth Client 所属的项目。Gmail Watch 要求 Topic 必须和 OAuth Client 在同一项目中。

10.6 Hooks:事件驱动的"自动化胶水"

注意:这里的 Hooks 和上一节的 Webhook 是两个不同的概念。Webhook 是外部 HTTP 触发器;Hooks 是内部事件监听器——当 Agent 内部发生了某些事件时,自动执行你定义的逻辑。

10.6.1 两种 Hooks 的区别

┌───────────────────────────────────────────────────────┐
│  Hooks(内部事件监听)                                  │
│  触发源:Agent 内部事件                                │
│  例子:/new、/reset、会话压缩、消息收发                 │
│  实现:TypeScript 函数,运行在 Gateway 进程内           │
│                                                       │
│  Webhook(外部 HTTP 触发)                              │
│  触发源:外部系统 HTTP 请求                             │
│  例子:邮件推送、GitHub Webhook、监控告警               │
│  实现:HTTP 端点 + 映射规则                            │
└───────────────────────────────────────────────────────┘

10.6.2 四个内置 Hook

OpenClaw 自带四个内置 Hook:

Hook 事件 作用
💾 session-memory command:newcommand:reset 会话重置时自动保存上下文到记忆文件
📎 bootstrap-extra-files agent:bootstrap 注入额外的工作空间引导文件
📝 command-logger command 记录所有命令到审计日志
🚀 boot-md gateway:startup Gateway 启动时执行 BOOT.md 中的指令

查看和管理:

openclaw hooks list           # 列出所有 Hook
openclaw hooks enable session-memory  # 启用
openclaw hooks info session-memory    # 查看详情
openclaw hooks check                  # 检查资格

10.6.3 session-memory Hook 详解

这是最有实用价值的内置 Hook。每次你执行 /new/reset 时,它会自动把当前会话的最后 15 条消息保存到记忆文件:

# Session: 2026-03-28 14:30:00 UTC

- **Session Key**: agent:main:main
- **Session ID**: abc123def456
- **Source**: telegram

## Conversation Summary

user: Can you help me design the API?
assistant: Sure! Let's start with the endpoints...

文件保存在 <workspace>/memory/ 目录下,文件名由 LLM 自动生成(如 2026-03-28-api-design.md)。

为什么这很重要:没有 session-memory,每次 /new 都会丢失当前会话的上下文。有了它,Agent 可以在未来的会话中通过记忆系统回忆起之前的对话。

10.6.4 事件类型完整列表

命令事件

  • command:new/new 命令
  • command:reset/reset 命令
  • command:stop/stop 命令

会话事件

  • session:compact:before — 压缩前
  • session:compact:after — 压缩后
  • session:patch — 会话属性被修改

Agent 事件

  • agent:bootstrap — 工作空间引导文件注入前

Gateway 事件

  • gateway:startup — Gateway 启动完成后

消息事件

  • message:received — 收到消息(媒体可能尚未处理)
  • message:transcribed — 音频消息完成转录
  • message:preprocessed — 消息完成所有预处理(媒体理解、链接理解)
  • message:sent — 发送消息成功

10.6.5 创建自定义 Hook

一个 Hook 由两个文件组成:

my-hook/
├── HOOK.md          # 元数据 + 文档
└── handler.ts       # 处理函数

步骤 1:创建 Hook 目录

mkdir -p ~/.openclaw/hooks/my-hook

步骤 2:编写 HOOK.md

---
name: my-hook
description: "Logs all /new commands with context"
metadata:
  openclaw:
    emoji: "🔍"
    events:
      - "command:new"
---

# My Custom Hook

Logs session reset events with context information.

步骤 3:编写 handler.ts

const handler = async (event) => {
  // 只处理 /new 命令
  if (event.type !== "command" || event.action !== "new") {
    return;
  }

  const { sessionKey, timestamp } = event;
  const { workspaceDir, commandSource } = event.context;

  console.log(`[my-hook] Session reset`);
  console.log(`  Key: ${sessionKey}`);
  console.log(`  Source: ${commandSource}`);
  console.log(`  Time: ${timestamp.toISOString()}`);
  console.log(`  Workspace: ${workspaceDir}`);

  // 可选:向用户发送消息
  event.messages.push("🔍 Session context saved to memory.");
};

export default handler;

步骤 4:启用并测试

openclaw hooks list                # 确认 Hook 被发现
openclaw hooks enable my-hook      # 启用
# 重启 Gateway
# 然后在聊天中发送 /new 触发 Hook

10.6.6 Hook 发现顺序

Hook 从四个目录按优先级被发现:

1. Bundled Hooks(<openclaw>/dist/hooks/bundled/)
   └─ OpenClaw 自带,优先级最低

2. Plugin Hooks(插件内置)
   └─ 安装的插件中的 Hook

3. Managed Hooks(~/.openclaw/hooks/)
   └─ 用户安装的,可以覆盖 Bundled 和 Plugin

4. Workspace Hooks(<workspace>/hooks/)
   └─ 工作空间级,默认禁用,不能覆盖上级

安全边界

  • Bundled、Plugin、Managed Hook 被视为可信本地代码,自动加载
  • Workspace Hook 是仓库本地代码,需要显式启用才加载

10.6.7 Hook 最佳实践

保持 Handler 轻量

// ✓ 好——异步执行,立即返回
const handler = async (event) => {
  void processInBackground(event);
};

// ✗ 坏——阻塞命令处理
const handler = async (event) => {
  await slowDatabaseQuery(event);
  await evenSlowerAPICall(event);
};

优雅处理错误

const handler = async (event) => {
  try {
    await riskyOperation(event);
  } catch (err) {
    console.error("[hook] Failed:", err.message);
    // 不要抛出——让其他 Hook 继续执行
  }
};

尽早过滤不相关事件

const handler = async (event) => {
  if (event.type !== "message" || event.action !== "received") {
    return;  // 不相关,立即退出
  }
  // 处理逻辑...
};

10.6.8 消息事件实战:构建消息审计系统

一个实际有用的场景:记录所有收发的消息,用于合规审计。

// handler.ts — 消息审计 Hook
const handler = async (event) => {
  if (event.type !== "message") return;

  if (event.action === "received") {
    const { from, content, channelId } = event.context;
    console.log(JSON.stringify({
      direction: "inbound",
      from,
      channel: channelId,
      content: content.substring(0, 200), // 截断长消息
      timestamp: new Date().toISOString(),
    }));
  } else if (event.action === "sent") {
    const { to, content, channelId, success } = event.context;
    console.log(JSON.stringify({
      direction: "outbound",
      to,
      channel: channelId,
      success,
      content: content.substring(0, 200),
      timestamp: new Date().toISOString(),
    }));
  }
};

export default handler;

对应的 HOOK.md

---
name: message-audit
description: "Audit log for all inbound and outbound messages"
metadata:
  openclaw:
    emoji: "📋"
    events:
      - "message:received"
      - "message:sent"
---

10.7 Standing Orders:常驻指令(让 Agent 自主运行)

Cron 和 Webhook 解决了"什么时候做"的问题。但如果 Agent 不知道"做什么"和"怎么做",自动化就是空谈。

Standing Orders(常驻指令)就是给 Agent 的一份"常驻操作手册"——定义它被授权做什么、什么时候做、什么需要你批准、什么情况要停下来请示。

10.7.1 为什么需要 Standing Orders

没有 Standing Orders

  • 你每次都要手动告诉 Agent 做什么
  • Agent 在两次指令之间处于空闲状态
  • 例行工作容易被遗忘

有了 Standing Orders

  • Agent 在定义的边界内自主执行
  • 例行工作按计划自动完成
  • 你只需要处理异常和审批

打个比方:Standing Orders 就是把"每天提醒我发周报"变成"你负责周报,每周五下午 4 点自动发,只有异常时才通知我"。

10.7.2 Standing Orders 的结构

Standing Orders 写在 AGENTS.md 中(每次会话自动注入)或单独的 standing-orders.md 文件中。每个程序(Program)包含四个要素:

## Program: Weekly Status Report

**Authority:** Compile data, generate report, deliver to stakeholders
**Trigger:** Every Friday at 4 PM (enforced via cron job)
**Approval gate:** None for standard reports. Flag anomalies for human review.
**Escalation:** If data source is unavailable or metrics look unusual (>2σ from norm)

### Execution Steps

1. Pull metrics from configured sources
2. Compare to prior week and targets
3. Generate report in Reports/weekly/YYYY-MM-DD.md
4. Deliver summary via configured channel
5. Log completion to Agent/Logs/

### What NOT to Do

- Do not send reports to external parties
- Do not modify source data
- Do not skip delivery if metrics look bad — report accurately

10.7.3 Execute-Verify-Report 模式

Standing Orders 中最常见的失败模式是 Agent "嘴上说要做但没有做"。解决方法是在每个任务中强制执行 EVR 模式

### Execution Rules

- Every task follows Execute-Verify-Report. No exceptions.
- "I'll do that" is not execution. Do it, then report.
- "Done" without verification is not acceptable. Prove it.
- If execution fails: retry once with adjusted approach.
- If still fails: report failure with diagnosis. Never silently fail.
- Never retry indefinitely — 3 attempts max, then escalate.

这个模式防止 Agent 犯最常见的错误:确认了任务但没有实际完成。

10.7.4 Standing Orders + Cron:完美配合

Standing Orders 定义"做什么",Cron 定义"什么时候做":

Standing Order: "你负责每日收件箱分类"
    ↓
Cron Job (每天 8:00): "按 Standing Orders 执行收件箱分类"
    ↓
Agent: 读取 Standing Orders → 执行步骤 → 报告结果

Cron 的 prompt 应该引用 Standing Orders,而不是重复指令:

openclaw cron add \
  --name daily-inbox-triage \
  --cron "0 8 * * 1-5" \
  --tz America/New_York \
  --timeout-seconds 300 \
  --announce \
  --channel bluebubbles \
  --to "+1XXXXXXXXXX" \
  --message "Execute daily inbox triage per standing orders. Check mail for new alerts. Parse, categorize, and persist each item. Report summary to owner. Escalate unknowns."

10.7.5 多程序架构

当 Agent 管理多个领域时,把 Standing Orders 拆分成独立的程序:

# Standing Orders

## Program 1: Inbox Management (Daily)

**Authority:** Read, categorize, and respond to emails
**Trigger:** Daily at 8 AM (cron)
**Approval gate:** External replies require review
**Escalation:** Unknown senders, sensitive topics

## Program 2: Financial Processing (Monthly + Event)

**Authority:** Process transactions, generate reports
**Trigger:** Monthly cycle OR new data file detected
**Approval gate:** Analysis automatic, recommendations need approval
**Escalation:** Single item > $500

## Program 3: System Monitoring (Continuous)

**Authority:** Check health, restart services, send alerts
**Trigger:** Every heartbeat cycle
**Approval gate:** Restart automatic. Escalate if fails twice.
**Escalation:** Service down > 5 minutes

## Escalation Rules (All Programs)

- Any action affecting external systems → notify owner
- Uncertainty → ask, don't guess
- 3 failed attempts → stop and escalate

10.7.6 从小权限开始

不要第一天就给 Agent 最大权限。先给最小权限,观察表现,逐步扩大:

第 1 周:只读(检查报告,不发送)
第 2 周:允许内部操作(写文件,整理数据)
第 3 周:允许有限发送(发到指定通道)
第 4 周:全自主(按 Standing Orders 运行,异常时请示)

10.8 综合实战:构建完整的自动化工作流

场景:个人助理的完整自动化系统

假设你是一个独立开发者,需要 Agent 帮你管理日常事务。以下是一个完整的自动化系统设计:

第一步:在 AGENTS.md 中定义 Standing Orders

# Standing Orders

## Program 1: Morning Briefing (Daily 7:30 AM)

**Authority:** Check inbox, calendar, weather, news; compile brief
**Trigger:** Cron job at 7:30 AM
**Approval gate:** None
**Escalation:** Calendar conflicts within 2 hours

### Execution

1. Check email inbox for overnight messages
2. Review calendar for today's events
3. Check weather for outdoor plans
4. Compile morning brief
5. Deliver to Telegram

## Program 2: Code Review (On-demand)

**Authority:** Review PR diffs, flag issues, suggest fixes
**Trigger:** Webhook from GitHub
**Approval gate:** None for review; comment posting requires approval
**Escalation:** Security vulnerabilities → immediate alert

## Program 3: Weekly Report (Fridays 4 PM)

**Authority:** Compile weekly progress, generate report
**Trigger:** Cron job Fridays at 4 PM
**Approval gate:** Review before sending
**Escalation:** Missing data sources

第二步:配置 HEARTBEAT.md

# Heartbeat checklist

- Check for urgent emails
- Review calendar for events in next 2 hours
- If idle for 8+ hours, send a check-in
- Check if any standing orders need attention

第三步:创建 Cron 作业

# 每日早报
openclaw cron add \
  --name "Morning brief" \
  --cron "30 7 * * *" \
  --tz "Asia/Shanghai" \
  --session isolated \
  --message "Execute morning briefing per standing orders." \
  --model "opus" \
  --announce \
  --channel telegram \
  --to "123456789"

# 周报
openclaw cron add \
  --name "Weekly report" \
  --cron "0 16 * * 5" \
  --tz "Asia/Shanghai" \
  --session isolated \
  --message "Execute weekly report per standing orders." \
  --announce \
  --channel telegram \
  --to "123456789"

# 一次性提醒示例
openclaw cron add \
  --name "Team meeting" \
  --at "2026-03-28T14:00:00+08:00" \
  --session main \
  --system-event "Team standup starts in 30 minutes. Prepare notes." \
  --wake now \
  --delete-after-run

第四步:配置 Webhook(GitHub 代码审查)

{
  hooks: {
    enabled: true,
    token: "${OPENCLAW_HOOK_TOKEN}",
    mappings: [
      {
        match: { path: "github" },
        action: "agent",
        name: "GitHub",
        sessionKey: "hook:github:{{body.pull_request.number}}",
        messageTemplate: "New PR #{{body.pull_request.number}}: {{body.pull_request.title}}\nAuthor: {{body.pull_request.user.login}}\nAction: {{action}}\nReview per standing orders Program 2.",
        deliver: true,
        channel: "telegram",
        to: "123456789",
      },
    ],
  },
}

第五步:启用关键 Hooks

openclaw hooks enable session-memory    # 会话重置时保存记忆
openclaw hooks enable command-logger    # 命令审计日志

第六步:配置心跳

{
  agents: {
    defaults: {
      heartbeat: {
        every: "30m",
        target: "last",
        activeHours: { start: "08:00", end: "23:00" },
        lightContext: true,
        isolatedSession: true,
      },
    },
  },
}

这个系统的工作流:

07:30  Cron → 隔离会话生成早报 → 发送到 Telegram
08:00  Heartbeat → 检查收件箱和日历 → 无异常(HEARTBEAT_OK)
08:30  Heartbeat → 检查 → 发现日历冲突 → 发送提醒
09:15  GitHub Webhook → 新 PR → 隔离会话审查代码 → 发送审查结果
14:00  Cron → 主会话提醒 → "准备站会笔记"
...
16:00  Cron → 隔离会话生成周报 → 发送到 Telegram

10.9 自动化故障排查

自动化系统最大的问题不是配置错误,而是"静默失败"——你以为它在运行,其实没有。

10.9.1 诊断命令梯

从简单到复杂,逐步排查:

# 第一层:基础状态
openclaw status
openclaw gateway status
openclaw cron status

# 第二层:自动化状态
openclaw cron list
openclaw system heartbeat last
openclaw hooks list

# 第三层:详细日志
openclaw logs --follow
openclaw cron runs --id <jobId> --limit 20

10.9.2 Cron 没有运行

症状:预期时间到了,但什么都没发生。

排查步骤

# 1. 确认 Cron 已启用
openclaw cron status
# 预期输出:enabled: true, nextWakeAtMs: <future timestamp>

# 2. 确认作业存在且启用
openclaw cron list
# 检查作业状态是否为 enabled

# 3. 确认时区正确
openclaw cron list  # 检查 --tz 设置

常见签名

日志信息 原因 解决
scheduler disabled cron.enabled: falseOPENCLAW_SKIP_CRON=1 启用 Cron
timer tick failed 调度器崩溃 查看堆栈日志
reason: not-due 手动运行但作业未到时间 --force

10.9.3 Cron 运行了但没有投递

症状cron runs 显示成功,但没收到消息。

openclaw cron runs --id <jobId> --limit 20
openclaw channels status --probe  # 检查通道连接

常见原因

原因 检查方法
投递模式是 none openclaw cron list 检查 delivery
通道目标无效 确认 channelto 格式正确
通道认证失败 openclaw channels status --probe
Agent 回复了 HEARTBEAT_OK 正常行为,无实质内容不投递

10.9.4 Heartbeat 被跳过

openclaw system heartbeat last
openclaw logs --follow

常见签名

日志信息 原因
reason=quiet-hours 当前时间在 activeHours 之外
requests-in-flight 主队列忙,心跳延迟
empty-heartbeat-file HEARTBEAT.md 为空,跳过以节省 API 调用
alerts-disabled 通道可见性设置抑制了心跳消息

10.9.5 时区陷阱

时区问题是自动化排查中最常见的坑:

# 检查心跳时区
openclaw config get agents.defaults.heartbeat.activeHours
openclaw config get agents.defaults.heartbeat.activeHours.timezone

# 检查用户时区
openclaw config get agents.defaults.userTimezone

规则

  • Cron 不指定 --tz 时使用主机时区
  • Heartbeat activeHours 优先使用 userTimezone,然后是主机时区
  • ISO 时间戳不带时区 = UTC
  • 主机时区变化后,已创建的 Cron 作业的执行时间会跟着变

⚠️ 踩坑记录

问题:Cron 作业总是在错误的时间运行。

原因:服务器时区是 UTC,但你的 --tz 设的是本地时区——看起来没问题,但 ISO 时间戳没有带时区后缀,被解析为 UTC。

解决:始终在 ISO 时间戳中明确时区(如 2026-03-28T14:00:00+08:00),或者在 CLI 中用 --tz 参数。不要混用。

10.10 进阶:Lobster 工作流引擎

对于需要多步骤、审批门、可恢复的复杂自动化流水线,OpenClaw 提供了 Lobster 工作流引擎。

Lobster 适合的场景

  • 多步骤自动化——需要固定的工具调用流水线,不是单次 Agent Turn
  • 审批门——副作用操作需要暂停等待你批准
  • 可恢复运行——暂停后可以继续,不用从头开始

Lobster 和 Heartbeat/Cron 的关系

  • Heartbeat/Cron 决定什么时候运行
  • Lobster 定义运行什么步骤

Lobster 作为可选插件启用:

{
  tools: {
    alsoAllow: ["lobster"],
  },
}

本章小结

  • 自动化三模型:Heartbeat(周期巡逻)、Cron(精确闹钟)、Webhook(事件门铃),三者互补而非互斥
  • Heartbeat 适合批量检查,通过 HEARTBEAT.md 定义检查清单,HEARTBEAT_OK 信号避免无意义通知
  • Cron 有主会话和隔离两种模式,隔离模式支持模型覆盖、自定义会话、三种投递方式
  • 一次性任务用 at,周期任务用 cron 表达式,隔离运行用 isolated
  • Webhook 通过 /hooks/wake/hooks/agent 两个端点接入外部系统
  • Hooks 是内部事件监听器,支持命令、会话、消息等多种事件类型
  • Standing Orders 是自动化的"操作手册",定义 Agent 的授权范围、触发条件和升级规则
  • EVR(Execute-Verify-Report)模式防止 Agent "只说不做"
  • 从小权限开始,观察表现,逐步扩大 Agent 的自主权限

下一步

Agent 有了自动化能力,可以"自己干活"了。但一个能自主运行的 Agent,安全问题是绕不开的。下一章深入 OpenClaw 的安全体系——从沙盒隔离到凭证管理,从威胁模型到生产部署,让你放心地把 Agent 放到生产环境。