OpenClaw 的 health 与 heartbeat 是两回事:一个查网关状态,一个按周期跑 Agent
把 OpenClaw 的网关挂到监控系统上时,很多人会在文档里同时撞见两个词:health 和 heartbeat。中文里它们都容易被念成「健康检查」或「心跳」,于是就出现了那种很别扭的问题——我只想让 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 两组命令
文档给的快速检查清单里,status 和 health 是两组不同粒度的入口:
| 命令 | 做什么 |
|---|---|
openclaw status | 本地摘要:网关可达性/模式、更新提示、已连渠道的鉴权时长、会话与近期活动 |
openclaw status --all | 完整本地诊断,只读,文档说可以安全贴出来给人排错 |
openclaw status --deep | 让网关做一次实时探测(带 probe:true 的 health),支持的渠道上含逐账号探测 |
openclaw status --usage | 模型供应商的用量/配额快照 |
openclaw health | 向运行中的网关要一份健康快照(仅走 WS,CLI 不直连渠道 socket) |
openclaw health --verbose(别名 --debug) | 强制一次实时探测,并打印网关连接细节 |
openclaw health --json | 机器可读的快照输出 |
openclaw health 默认返回缓存快照,网关在后台刷新它,只有 --verbose 才强制实时探测——写告警脚本时要的若是「此刻的真实状态」,别用默认模式。命令在网关不可达、探测失败或超时时以非零码退出,可直接做判断;超时默认 10 秒,用 --timeout <ms> 改。快照包含 ok、ts、durationMs、逐渠道状态、Agent 可用性、会话存储摘要和可选的投递队列告警。
两个轻量入口:任意渠道里把 /status 作为独立聊天命令发出去,能拿到状态回复而不触发 Agent;查日志用 openclaw logs --follow(多 profile 加 --profile <profile>),过滤 web-heartbeat、web-reconnect、web-auto-reply、web-inbound。
还有一个反直觉的点:会话行数不等于连接存活。对 Discord 这类供应商,openclaw sessions、sessions.list、sessions_list 读的都是存下来的会话状态;供应商可能已经重连、渠道状态已经健康,却还没有新的会话行被创建。实时连通性要用上面那组命令判断。
三对 HTTP 探针别接错
网关暴露了三对不需要鉴权的 GET/HEAD 探针,语义各不相同:
| 端点 | 含义 | 该给谁用 |
|---|---|---|
/health、/healthz | HTTP 服务活着 | 进程存活判断、重启决策 |
/startup、/startupz | 启动工作完成且网关未在排空(draining);不查渠道健康 | 编排器的启动探针与流量准入 |
/ready、/readyz | 启动完成、未排空,且已配置的渠道账号通过深度就绪检查 | 需要暴露硬性渠道故障的运维监控 |
/startupz 的状态码语义很明确:启动 sidecar 没跑完时返回 503 且 status: "starting",排空期间 503 且 status: "draining",其余 200 且 status: "started"。Kubernetes、Fly、Render 这类平台的流量准入应该挂在这个端点上。
区别的实际意义在于:一个坏掉的 Telegram 账号会让 /readyz 返回 503,但不会通过 /startupz 把本身健康的 Control UI 踢出服务。把 K8s 的 readiness 直接指到 /readyz,一个渠道掉线就会让整个 Pod 被摘流量——这大概率不是你想要的。
另外,远程未鉴权的启动响应只有 ok 和 status;version、uptimeMs、pendingReason 和就绪细节只给本地直连或已鉴权的调用方,因为它们会点名哪个子系统失败。
别拿 /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 status 报 Channel 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.enabled 为 false 或设了 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 还有个零宽陷阱:start 和 end 不能相同,08:00 到 08:00 会被当成零宽窗口,heartbeat 永远跳过。想全天跑就别写它,或写 { start: "00:00", end: "24:00" }。
回复契约:HEARTBEAT_OK 的位置是有讲究的
默认提示词刻意收窄:有 scratch 就跟着走,周期性工作交给自动化作业,不要从旧聊天里推断或重复老任务,没事就回 HEARTBEAT_OK。这条约束是默认装完它不会天天翻旧账的原因。具体契约:
- 没事就回
HEARTBEAT_OK;发告警时不要带它,只返回告警文本。 - 也可以调
heartbeat_respond:notify: false不出可见更新,notify: true配notificationText表示告警。结构化响应优先级高于文本兜底。 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 now:now 立即跑一次,next-heartbeat(默认)等下个 tick;不给 --session-key 时,配了 heartbeat 的 Agent 会被全部跑一遍。
两侧对照速查
| 维度 | health(探针与快照) | heartbeat(周期 Agent 轮次) |
|---|---|---|
| 谁发起 | 你或外部监控 | 网关按计划(Automations 调度器拥有) |
| 是否调模型 | 否 | 是,跑完整 Agent 轮次 |
| 是否建会话 | 否 | 主会话;isolatedSession: true 则每次新会话 |
| 主要入口 | openclaw status / openclaw health / /health、/startupz、/readyz | agents.*.heartbeat 配置、openclaw system event |
| 关掉的方式 | 探针是内建端点 | every: "0m";或整体关自动化(作业保留但禁用) |
| 排查起点 | openclaw status --all、channels status、日志 | openclaw cron list --all 里的 Heartbeat (agent-id) |
| 花不花钱 | 文档描述为无 LLM 调用 | 会,间隔越短越贵 |
什么时候这两套都不够用
第一,heartbeat 不是调度器。「每天早上九点扫一遍收件箱」这类周期检查,官方口径是建自动化作业——它有自己的节拍、启用状态和运行历史,scratch 只是提示词上下文。两者怎么选见cron 与 heartbeat 怎么选。
第二,健康快照定位不到具体消息。ingressPressure 刻意剔掉了车道 ID、载荷和错误文本,只回答「有没有车道受压」,往下追得走日志和诊断导出。
第三,探针答不了「渠道为什么连不上」。/readyz 只告诉你有账号没过深度就绪检查;登录态问题走另一套:日志出现 logged out 或 409–515 状态码时,用 openclaw channels logout 再 openclaw channels login 重新绑定(二维码流程在配对后遇到 515 会自动重启一次)。完整排查表见渠道连不上怎么查。
第四,heartbeat 的很多行为是固定策略。文档写明它的配置是严格的:只接受列出的字段,确认抑制、推理可见性、忙碌推迟和工具错误告警都是固定运行时策略,没有配置层的口子。
最后,这两套都是「网关还能响应」前提下的工具。网关起不来时先解决进程、锁文件和端口(openclaw gateway --port 18789,端口被占用加 --force),再回来读探针输出。
延伸阅读
- 从头读起:OpenClaw 架构总览:网关、Agent、渠道、节点、插件五层各管什么,出问题该往哪查
- 本专题共 40 篇,完整分组目录见专题页
- OpenClaw 网关重启后会话还在不在?哪些状态能自动恢复、哪些直接消失
- OpenClaw 远程访问三条路:SSH 隧道、Tailscale Serve 与直连绑定该怎么选
本文依据 OpenClaw 官方仓库(github.com/openclaw/openclaw)docs/ 下的官方文档整理,核对日 2026-08-17。
我们没有安装或运行过 OpenClaw,因此不涉及界面外观、操作手感与实测耗时的任何描述;
文中的默认值、命令与配置项均为文档口径,不构成对实际运行结果的保证。
该项目迭代很快,请以仓库最新内容为准。接入即时通讯平台前,请自行确认所在平台的规则与合规要求。