OpenClaw 的 health 与 heartbeat 是两回事:一个查网关状态,一个按周期跑 Agent

2026-08-17

把 OpenClaw 的网关挂到监控系统上时,很多人会在文档里同时撞见两个词:healthheartbeat。中文里它们都容易被念成「健康检查」或「心跳」,于是就出现了那种很别扭的问题——我只想让 UptimeRobot 每五分钟看一眼网关活没活,为什么讲心跳那一页通篇在说 token 成本、模型选择、HEARTBEAT_OK

因为它们不是同一层的机制。health 是运维视角的只读探测:CLI 问网关要一份快照,HTTP 探针回一个状态码,全程不产生会话、不调模型。heartbeat 是定期唤醒:网关按你配的间隔在主会话里跑一次完整的 Agent 轮次,让模型自己判断有没有什么需要提醒你,这是要花钱的。

混起来最典型的后果有两种:拿 /v1/chat/completions 当健康检查端点,每次探测都建一个新会话;或者把「网关健康」理解成「消息一定进得来」,然后看着一个 health: healthy 的通道一条入站消息都收不到。

一句话分清:谁在被问,谁在被唤醒

openclaw health你去问网关:网关生成一份健康快照给你,CLI 不直连任何渠道的 socket。HTTP 探针同理,是外部系统来问网关活没活。

heartbeat 是网关去唤醒 Agent:它在主会话里发起周期性的 Agent 轮次。文档写得很直白——heartbeat 是「scheduled main-session turn」,它不会创建后台任务(background task)记录,那是留给分离式工作的(ACP 运行、子 Agent、隔离的自动化作业)。

还有第三个容易撞名的:日志里的 web-heartbeat,以及文档说的「同一个 heartbeat 驱动那个有界稳定性记录器(openclaw gateway stability)」——那是网关内部的存活节拍,跟 agents.*.heartbeat 不是一码事。

health 侧:先分清 status 和 health 两组命令

文档给的快速检查清单里,statushealth 是两组不同粒度的入口:

命令做什么
openclaw status本地摘要:网关可达性/模式、更新提示、已连渠道的鉴权时长、会话与近期活动
openclaw status --all完整本地诊断,只读,文档说可以安全贴出来给人排错
openclaw status --deep让网关做一次实时探测(带 probe:truehealth),支持的渠道上含逐账号探测
openclaw status --usage模型供应商的用量/配额快照
openclaw health向运行中的网关要一份健康快照(仅走 WS,CLI 不直连渠道 socket)
openclaw health --verbose(别名 --debug强制一次实时探测,并打印网关连接细节
openclaw health --json机器可读的快照输出

openclaw health 默认返回缓存快照,网关在后台刷新它,只有 --verbose 才强制实时探测——写告警脚本时要的若是「此刻的真实状态」,别用默认模式。命令在网关不可达、探测失败或超时时以非零码退出,可直接做判断;超时默认 10 秒,用 --timeout <ms> 改。快照包含 oktsdurationMs、逐渠道状态、Agent 可用性、会话存储摘要和可选的投递队列告警。

两个轻量入口:任意渠道里把 /status 作为独立聊天命令发出去,能拿到状态回复而不触发 Agent;查日志用 openclaw logs --follow(多 profile 加 --profile <profile>),过滤 web-heartbeatweb-reconnectweb-auto-replyweb-inbound

还有一个反直觉的点:会话行数不等于连接存活。对 Discord 这类供应商,openclaw sessionssessions.listsessions_list 读的都是存下来的会话状态;供应商可能已经重连、渠道状态已经健康,却还没有新的会话行被创建。实时连通性要用上面那组命令判断。

三对 HTTP 探针别接错

网关暴露了三对不需要鉴权的 GET/HEAD 探针,语义各不相同:

端点含义该给谁用
/health/healthzHTTP 服务活着进程存活判断、重启决策
/startup/startupz启动工作完成且网关未在排空(draining);不查渠道健康编排器的启动探针与流量准入
/ready/readyz启动完成、未排空,且已配置的渠道账号通过深度就绪检查需要暴露硬性渠道故障的运维监控

/startupz 的状态码语义很明确:启动 sidecar 没跑完时返回 503status: "starting",排空期间 503status: "draining",其余 200status: "started"。Kubernetes、Fly、Render 这类平台的流量准入应该挂在这个端点上。

区别的实际意义在于:一个坏掉的 Telegram 账号会让 /readyz 返回 503,但不会通过 /startupz 把本身健康的 Control UI 踢出服务。把 K8s 的 readiness 直接指到 /readyz,一个渠道掉线就会让整个 Pod 被摘流量——这大概率不是你想要的。

另外,远程未鉴权的启动响应只有 okstatusversionuptimeMspendingReason 和就绪细节只给本地直连或已鉴权的调用方,因为它们会点名哪个子系统失败。

别拿 /v1/chat/completions 当健康检查

这是文档专门列出来的反面做法。外部 uptime 监控应该用 /health:瞬时响应,不建会话,不调 LLM,返回 {"ok":true,"status":"live"}。BetterStack、UptimeRobot 都把地址填成 https://<your-gateway-host>:<port>/health 即可。

/v1/chat/completions 每次请求都会拉起完整的 Agent 会话,带技能快照、上下文组装和模型调用;请求里既没有 x-openclaw-session-key 头也没有 user 字段时还会生成新的随机会话。文档给的量级是:每 15 分钟 ping 一次,一天约制造 96 个会话,每个占 4–22KB,时间长了撑大会话存储,还可能导致上下文窗口溢出。

「渠道连着」不等于「消息进得来」

渠道连通性和入站准入是两个独立的失败域:一个渠道可以保持健康的传输连接、正常发回复,同时它的持久化入站队列(durable ingress queue)不可用,一条入站消息都进不来。

渠道打不开这个队列时启动失败,网关把该账号记为无法接收,openclaw channels statusChannel cannot admit inbound events; its durable ingress queue is unavailable. Outbound may still work.。这类账号无论传输层什么状态都算 unhealthy,就绪检查会报失败——文档同时说明这是修正后的行为,此前它会报 health: healthy 而健康监视器根本不碰它。

恢复是自动的:入站裁决描述上一次启动尝试的结果,下次启动会清除它,普通重启路径就是瞬时失败的恢复路径,日志写作 health-monitor: restarting (reason: ingress-unavailable) 而非通用的 stuck。重启一直重复就不是瞬时问题,去看记录的失败原因,比如某个插件拒绝了 openChannelIngressQueue 能力——这要运维介入而不是再重启。

还有一条能省掉很多误报:从不上报入站状态的渠道不受影响,没有信号不等于坏了。系统里没有「流量陈旧」这类启发式判断,很安静的渠道不会因为长期没收到消息被标成不健康。

健康监视器本身也有开关和让位规则:

  • channels.<provider>.healthMonitor.enabled:单独关掉某渠道的健康监视器重启,保留全局监控。
  • channels.<provider>.accounts.<accountId>.healthMonitor.enabled:多账号覆盖,优先级更高。
  • 暴露这些覆盖项的渠道:Discord、Google Chat、iMessage、IRC、Microsoft Teams、Signal、Slack、Telegram、WhatsApp。
  • 崩溃的渠道先走自己的自动重启退避阶梯(日志里 auto-restart attempt N/10),健康监视器不插手,直到阶梯以 giving up after 10 restart attempts 结束才接手当最后的重启拥有者。

ok: true 也不代表队列是干净的

健康 RPC 成功返回顶层 ok: true,含义只是「网关生成了这份快照」,不代表每条投递队列都通畅。要看 deliveryQueues.ingressPressure——没有受压车道时这个字段直接不出现。

判定用的是保守的内置诊断阈值,不是插件的权威重试策略:一条车道只有在活跃的 pending/claimed 行已至少尝试八次且记录了投递错误,或某个 claimed 行已 30 分钟没刷新认领时才出现,第 1 到 7 次普通重试不出现。结果按渠道账号分组给计数和最早受影响的接收时间。

诊断默认开着(diagnostics.enabled: false 才关闭),要提 bug 就跑 openclaw gateway diagnostics export,zip 里聊天文本、webhook 请求体、凭据、cookie 和密钥值都已省略或打码。展开写法见网关诊断与日志怎么开

heartbeat 侧:它是一次真的 Agent 轮次

调度权不在 heartbeat 自己手上。文档说明它的节拍由 Automations 调度器拥有:网关为每个启用 heartbeat 的 Agent 维护一个系统自有的自动化作业,在 openclaw cron list --all 里显示为 Heartbeat (agent-id)。heartbeat 配置只是「期望状态」输入,真正的 tick 和冷却由持久化的监视器计划表拥有;网关在启动和配置重载时写下变更,openclaw doctor --fix 能补出缺失或过期的监视器行。要改就改 agents.*.heartbeat,别去动那个自动化作业。

由此带来一个很容易踩的依赖:定时 heartbeat 依赖自动化cron.enabledfalse 或设了 OPENCLAW_SKIP_CRON=1 时,网关启动时打一条警告并且不跑定时 heartbeat,手动和事件驱动的唤醒仍可用——没有独立的兜底定时器。所以「heartbeat 不动了」第一步该查自动化是否被整体关掉,排查路径跟定时任务不触发是同一条。

默认值这几条建议记住:

  • 间隔默认 30m;解析出的鉴权模式是 Anthropic OAuth/token(含复用 Claude CLI)时,供应商默认值把它抬到 1h,但仅在 heartbeat.every 未设置时生效。用 0m 关闭。
  • 投递目标默认 owner:取 commands.ownerAllowFrom 第一个具体条目,然后是渠道 allowFrom,这条路由永远不会发到群里;没有可解析的 owner DM 时以 reason=no-route 跳过。想跟随最近一次外部会话(含群)设 target: "last",只跑内部状态设 target: "none"
  • 超时:未设置时用 agents.defaults.timeoutSeconds;再没有就用节拍本身,上限 600 秒。
  • 0m 关掉后,监视器作业保留但禁用,scratch 也留着等你重新打开节拍。
  • 主队列或自动化工作在跑、同 Agent 有回复或嵌入式运行、目标会话有活跃或排队工作时,定时 heartbeat 推迟。手动唤醒只绕过「同 Agent 有活跃运行」这条预检,其余守卫照旧。

一份最小配置长这样:

{
  commands: {
    ownerAllowFrom: ["telegram:123456789"],
  },
  agents: {
    defaults: {
      heartbeat: {
        every: "30m",
        target: "owner",
        directPolicy: "allow",
        lightContext: true,
        isolatedSession: true,
        // activeHours: { start: "08:00", end: "24:00" },
      },
    },
  },
}

作用域上有一条容易翻车的规则:agents.entries.*.heartbeat 叠加在 agents.defaults.heartbeat 之上,但只要任一 Agent 带了 heartbeat 块,就只有这些 Agent 跑 heartbeat——给某个 Agent 单独调间隔,可能顺手把其它 Agent 的全停了。

activeHours 还有个零宽陷阱:startend 不能相同,08:0008:00 会被当成零宽窗口,heartbeat 永远跳过。想全天跑就别写它,或写 { start: "00:00", end: "24:00" }

回复契约:HEARTBEAT_OK 的位置是有讲究的

默认提示词刻意收窄:有 scratch 就跟着走,周期性工作交给自动化作业,不要从旧聊天里推断或重复老任务,没事就回 HEARTBEAT_OK。这条约束是默认装完它不会天天翻旧账的原因。具体契约:

  • 没事就回 HEARTBEAT_OK;发告警时不要带它,只返回告警文本。
  • 也可以调 heartbeat_respondnotify: false 不出可见更新,notify: truenotificationText 表示告警。结构化响应优先级高于文本兜底。
  • notify: false 且有实质内容的结果保持静默,但会作为有界内部上下文供下一个用户轮次使用。
  • HEARTBEAT_OK 出现在开头或结尾才当 ack:token 被剥掉,剩余内容不超过 300 字符时整条丢弃,预算固定不可配;出现在中间不作特殊处理。

可见性由 heartbeatVisibility 的三个开关控制:showOk(默认 false)、showAlerts(默认 true)、useIndicator(默认 true),优先级 per-account → per-channel → channel defaults → 内置默认。三个全 false 时整次运行被跳过(reason=alerts-disabled),连模型调用都不发生。

成本这块必须提前设好

heartbeat 跑的是完整 Agent 轮次,间隔越短烧得越多。文档给的省钱开关:isolatedSession: true(每次全新会话不带历史,量级是从约 100K token 降到每次约 2–5K)、lightContext: true(跳过工作区 bootstrap 文件)、设更便宜的 model、scratch 保持很小、只要内部状态更新就设 target: "none"

还有一个不好排查的连带问题:heartbeat 运行结束后会保留共享会话已有的运行时模型,所以一次切到小模型(比如 32k 窗口的 Ollama 模型)的 heartbeat,可能把它留给下一个主会话轮次。下一轮若报上下文溢出、且会话最后的运行时模型正好等于 heartbeat.model,恢复消息会点出这是模型串味。规避同样是 isolatedSession: true,或选个窗口够大的模型。

scratch 是提示词上下文,不是调度器

每个监视器作业有一份私有 scratch,存在时被追加到提示词后面,用 openclaw cron scratch <jobId>--set / --file / --unset 管理(jobId 来自 openclaw cron list --all,有 --expected-revision 并发保护,上限 256 KiB)。别把密钥写进去。

一个容易看漏的静默跳过:scratch 存在但实质为空(只有空行、注释、# 标题、围栏标记或空清单占位)时运行被跳过以省 API 调用,原因记作 reason=empty-heartbeat-file;反而是压根没有 scratch 时照常运行。

临时唤醒用 openclaw system event --text "..." --mode nownow 立即跑一次,next-heartbeat(默认)等下个 tick;不给 --session-key 时,配了 heartbeat 的 Agent 会被全部跑一遍。

两侧对照速查

维度health(探针与快照)heartbeat(周期 Agent 轮次)
谁发起你或外部监控网关按计划(Automations 调度器拥有)
是否调模型是,跑完整 Agent 轮次
是否建会话主会话;isolatedSession: true 则每次新会话
主要入口openclaw status / openclaw health / /health/startupz/readyzagents.*.heartbeat 配置、openclaw system event
关掉的方式探针是内建端点every: "0m";或整体关自动化(作业保留但禁用)
排查起点openclaw status --allchannels status、日志openclaw cron list --all 里的 Heartbeat (agent-id)
花不花钱文档描述为无 LLM 调用会,间隔越短越贵

什么时候这两套都不够用

第一,heartbeat 不是调度器。「每天早上九点扫一遍收件箱」这类周期检查,官方口径是建自动化作业——它有自己的节拍、启用状态和运行历史,scratch 只是提示词上下文。两者怎么选见cron 与 heartbeat 怎么选

第二,健康快照定位不到具体消息。ingressPressure 刻意剔掉了车道 ID、载荷和错误文本,只回答「有没有车道受压」,往下追得走日志和诊断导出。

第三,探针答不了「渠道为什么连不上」。/readyz 只告诉你有账号没过深度就绪检查;登录态问题走另一套:日志出现 logged out 或 409–515 状态码时,用 openclaw channels logoutopenclaw channels login 重新绑定(二维码流程在配对后遇到 515 会自动重启一次)。完整排查表见渠道连不上怎么查

第四,heartbeat 的很多行为是固定策略。文档写明它的配置是严格的:只接受列出的字段,确认抑制、推理可见性、忙碌推迟和工具错误告警都是固定运行时策略,没有配置层的口子。

最后,这两套都是「网关还能响应」前提下的工具。网关起不来时先解决进程、锁文件和端口(openclaw gateway --port 18789,端口被占用加 --force),再回来读探针输出。

延伸阅读


本文依据 OpenClaw 官方仓库(github.com/openclaw/openclawdocs/ 下的官方文档整理,核对日 2026-08-17。 我们没有安装或运行过 OpenClaw,因此不涉及界面外观、操作手感与实测耗时的任何描述; 文中的默认值、命令与配置项均为文档口径,不构成对实际运行结果的保证。 该项目迭代很快,请以仓库最新内容为准。接入即时通讯平台前,请自行确认所在平台的规则与合规要求。

想系统学会用 AI?报名体系课或加入会员,照着学、照着用。