安装
openclaw onboard 和 openclaw channels add --channel whatsapp 会在你第一次选择它时提示安装插件;如果插件缺失,openclaw channels login --channel whatsapp 也会提供相同的安装流程。开发检出版本使用本地插件路径;稳定版/测试版会先从 ClawHub 安装 @openclaw/whatsapp,然后回退到 npm。WhatsApp 运行时不包含在核心 OpenClaw npm 包中,因此其运行时依赖保留在外部插件中。手动安装:
@openclaw/whatsapp);只有为了可复现安装时才固定到精确版本。
配对
未知发送者的默认私信策略是配对。
频道故障排查
跨频道诊断和修复操作手册。
网关配置
完整的频道配置模式和示例。
快速设置
1
配置访问策略
2
绑定 WhatsApp(二维码)
3
启动网关
4
批准第一条私信访问请求(配对模式)
打开 设置 → 频道 → 私信访问请求,找到 WhatsApp 账号,
然后批准该发送者。如果你更喜欢使用命令行:私信访问请求会在 1 小时后过期;待处理请求每个账号最多 3 个。
这个批准与用于绑定账号本身的 WhatsApp 登录二维码是分开的。
建议使用独立的 WhatsApp 号码(相关设置和元数据已针对其优化),但个人号码/自聊配置也完全受支持。
部署模式
专用号码(推荐)
专用号码(推荐)
- 为 OpenClaw 单独使用一个 WhatsApp 身份
- 更清晰的私信白名单和路由边界
- 降低自聊混淆的可能性
个人号码回退模式
个人号码回退模式
引导流程支持个人号码模式,并会写入一个适合自聊的基础配置:
dmPolicy: "allowlist"、allowFrom 包含你的号码、selfChatMode: true。运行时的自聊保护会根据已绑定的自号码以及 allowFrom 来生效。运行时模型
- 网关拥有 WhatsApp socket 和重连循环。
- 看门狗独立跟踪两个信号:原始 WhatsApp Web 传输活动和应用消息活动。仅仅因为最近没有收到消息,并不会重启一个安静但已连接的会话;只有当传输帧在一个固定的内部窗口内停止到达(不可用户配置),或者应用消息在正常消息超时时间的 4 倍之后仍然保持沉默时,才会强制重连。对于最近活跃的会话,在刚完成重连后的第一个窗口中,会使用更短的正常消息超时,而不是 4 倍窗口。OpenClaw 可以自动回复 Baileys 在这次重连早期送达的离线消息,其范围受入站消息 ID 去重生存期限制;初始启动则继续使用较短的陈旧历史保护。
- 发送出站消息需要目标账号存在活跃的 WhatsApp 监听器;否则发送会快速失败。
- 群组发送会为文本和媒体标题中的
@+<digits>和@<digits>标记附加原生提及元数据,当该标记与当前参与者元数据匹配时也包括基于 LID 的群组。 - 状态和广播聊天(
@status、@broadcast)会被忽略。 - 直接聊天使用 DM 会话规则(
session.dmScope;默认main会将 DM 折叠到代理主会话中)。群组会话按 JID 隔离(agent:<agentId>:whatsapp:group:<jid>)。 - WhatsApp Channels/Newsletters 可以通过其原生
@newsletterJID 作为显式出站目标,使用频道会话元数据(agent:<agentId>:whatsapp:channel:<jid>)而不是 DM 语义。 - WhatsApp Web 传输遵守网关主机上的标准代理环境变量(
HTTPS_PROXY、HTTP_PROXY、NO_PROXY及其小写变体)。优先使用主机级代理配置,而不是按频道配置。
使用 MeowCaller 呼叫当前请求者(实验性)
该插件可以在源自 WhatsApp 的代理轮次中公开whatsapp_call。它使用 MeowCaller 向当前已授权的请求者发起 WhatsApp 语音通话,并在对方接听后播放一条 OpenClaw TTS 消息。该工具没有目标号码参数,因此提示无法重定向通话。默认禁用。
1
启用实验性通话
在 WhatsApp 通道配置中添加 当该项缺失或为
actions.calls: true,然后重启网关:false 时,OpenClaw 不会公开 whatsapp_call 工具。2
安装经过审查的 MeowCaller CLI
适配器期望网关主机的 确保
PATH 中存在一个 meowcaller 可执行文件。在 MeowCaller PR #7 合并之前,请构建已审查分支:$HOME/.local/bin 已加入网关服务的 PATH。这个修订版具有显式的 pair 和仅发送的 notify 命令;notify 不会打开麦克风、扬声器、视频设备或诊断采集。不要用上游示例 CLI 的 play 命令替代。3
配对 MeowCaller 已链接设备
让 WhatsApp 代理检查通话设置(交互式运行此命令,扫描 WhatsApp > 已链接设备 中的二维码,并等待
whatsapp_call 状态动作会报告账户特定的状态目录和配对命令)。对于默认账户:MeowCaller linked device ready。请保持 wa-voip.db 私密——它是 MeowCaller 会话。非默认账户会从状态动作中获得各自的存储路径;在 Windows 上,请运行其 PowerShell 命令。4
配置 TTS 并从 WhatsApp 发起通话
配置一个支持电话功能的 TTS 提供商,重启网关,然后发送类似
给我打电话并说构建已完成。 的请求。该工具会从受信任的入站上下文中解析发送者,合成一个临时的私有 WAV 文件,在受限的通话窗口内运行 MeowCaller,并在之后删除音频文件。OpenClaw 会显式传递该账户的存储路径,等待接听/播放/挂断后的零退出状态,并将超时或非零退出视为工具调用失败。审批提示
WhatsApp 可以将 exec 和 plugin 的审批提示渲染为👍/👎 反应,由顶层的审批转发配置控制:
approvals.exec 和 approvals.plugin 相互独立;仅将 WhatsApp 启用为一个通道,只会连接传输层,并不会发送任何内容,除非对应的审批家族已启用并路由到那里。session 模式仅对源自 WhatsApp 的审批传递原生 emoji 审批。target 模式使用共享的转发管道处理显式目标,不会创建单独的 approver-DM 扇出。
WhatsApp 审批反应要求在 allowFrom 中显式指定审批人(或使用 "*")。defaultTo 设置的是普通默认消息目标,而不是审批人列表。在审批解析之前,手动 /approve 命令仍会先经过正常的 WhatsApp 发送者授权路径。
问题反应
对于一个包含一个非机密、单选问题以及一到四个选项的ask_user 提示,WhatsApp 会在选项标签旁显示 1️⃣ 到 4️⃣。对已送达的提示使用对应的数字进行反应即可回答。OpenClaw 通过 Gateway 将该数字映射到规范选项;过期或重复的点击会被忽略。多问题、多选和自由文本提示仍然仅支持文本回复。普通 WhatsApp 私聊/群组准入规则会授权进行反应的发送者。
插件钩子与隐私
传入的 WhatsApp 消息可能包含个人内容、电话号码、群组标识符、发送者名称以及会话关联字段。除非你主动启用,否则 WhatsApp 不会将传入的message_received 钩子载荷广播给插件:
channels.whatsapp.accounts.<id>.pluginHooks.messageReceived 下的某一个账号。仅对你信任能够处理传入 WhatsApp 内容和标识符的插件启用此功能。
访问控制与激活
- 私信策略
- 群组策略与允许列表
- 提及与 /activation
channels.whatsapp.dmPolicy:allowFrom 接受 E.164 风格号码(内部会规范化)。它仅是私信发送者访问控制列表——不会限制显式发往群组 JID 或 @newsletter 频道 JID 的外发消息。多账号覆盖:channels.whatsapp.accounts.<id>.dmPolicy(以及 .allowFrom)会优先于该账号的频道级默认值。运行时说明:- 配对会保留在频道 allow-store 中,并与已配置的
allowFrom合并 - 定时自动化和心跳接收者回退会使用显式投递目标或已配置的
allowFrom;私信配对批准不会被视为隐式的 cron/heartbeat 接收者 - 如果未配置 allowlist,默认允许已绑定的本人号码
- OpenClaw 永远不会自动配对外发的
fromMe私信(你从已绑定设备自行发送的消息)
已配置的 ACP 绑定
WhatsApp 通过顶层的bindings[] 支持持久化 ACP 绑定:
个人号码与自聊行为
当已链接的自号码也出现在allowFrom 中时,自聊保护会生效:跳过自聊轮次的已读回执,忽略会向自己发送提醒的 mention-JID 自动触发行为,并在频道/账号的 responsePrefix 未设置时,默认回复为 [{identity.name}](或 [openclaw])。
消息规范化与上下文
传入封套与回复上下文
传入封套与回复上下文
传入消息会被包装在共享的传入封套中。引用回复会按以下形式附加上下文:回复元数据(
ReplyToId、ReplyToBody、ReplyToSender、sender JID/E.164)会在可用时被填充。如果被引用的目标是可下载媒体,OpenClaw 会通过常规传入媒体存储保存它,并暴露 MediaPath/MediaType,以便代理可以直接检查它,而不是只看到 <media:image>。媒体占位符与位置/联系人提取
媒体占位符与位置/联系人提取
仅媒体消息会规范化为占位符:
<media:image>、<media:video>、<media:audio>、<media:document>、<media:sticker>。仅当正文只有 <media:audio> 时,授权群组语音会在提及门控之前先转写,因此在语音中说出机器人提及可以触发回复。如果转写内容仍未提及机器人,它会保留在待处理群历史中,而不是原始占位符。位置正文会渲染为简洁的坐标文本。位置标签/备注和联系人/vCard 详情会渲染为带围栏的不受信任元数据,而不是内联提示文本。待处理群历史注入
待处理群历史注入
未处理的群消息会缓冲,并在机器人最终被触发时作为上下文注入。
- 默认限制:
50 - 配置:
channels.whatsapp.historyLimit,回退到messages.groupChat.historyLimit 0会禁用
[Chat messages since your last reply - for context] 和 [Current message - respond to this]。已读回执
已读回执
对已接受的传入消息默认启用。全局禁用:按账户覆盖:
channels.whatsapp.accounts.<id>.sendReadReceipts。即使全局启用,自身聊天的转发也不会发送已读回执。投递、分块与媒体
文本分块
文本分块
- 默认分块限制:
channels.whatsapp.textChunkLimit = 4000 channels.whatsapp.streaming.chunkMode = "length" | "newline";newline优先按段落边界(空行)分割,然后回退到按长度安全分块
出站媒体行为
出站媒体行为
- 支持图像、视频、音频(PTT 语音消息)和文档负载
- 音频会作为 Baileys 的
audio负载发送,并设置ptt: true,从而呈现为按住说话语音消息;audioAsVoice会保留在回复负载中,因此无论提供方的源格式如何,TTS 语音消息输出都会保持走这一路径 - 原生 Ogg/Opus 音频会以
audio/ogg; codecs=opus发送;其他任何格式(包括 Microsoft Edge TTS MP3/WebM 输出)都会在 PTT 投递前通过ffmpeg转码为 48 kHz 单声道 Ogg/Opus /tts latest会将最新的助手回复作为一条语音消息发送,并抑制对同一回复的重复发送;/tts chat on|off|default控制当前聊天的自动 TTS- 发送视频时设置
gifPlayback: true会启用动画 GIF 播放 forceDocument/asDocument会将出站图像、GIF 和视频通过 Baileys 文档负载路由,以避免 WhatsApp 的媒体压缩,并保留解析出的文件名和 MIME 类型- 标题会应用到多媒体回复中的第一项媒体,但 PTT 语音消息除外:音频先发送且不带标题,然后标题作为单独的文本消息发送(WhatsApp 客户端对语音消息标题的渲染并不一致)
- 媒体源可以是 HTTP(S)、
file://或本地路径
媒体大小限制与回退行为
媒体大小限制与回退行为
- 传入保存上限和出站发送上限:
channels.whatsapp.mediaMaxMb(默认50) - 按账户覆盖:
channels.whatsapp.accounts.<id>.mediaMaxMb - 图像会自动优化(调整大小/质量扫描)以适应限制,除非
forceDocument/asDocument请求以文档方式投递 - 媒体发送失败时,第一项的回退会发送文本警告,而不是静默丢弃回复
回复引用
channels.whatsapp.replyToMode 控制原生回复引用(出站回复会明显引用传入消息):
按账户覆盖:
channels.whatsapp.accounts.<id>.replyToMode。
反应级别
channels.whatsapp.reactionLevel 控制代理使用表情回应的广泛程度:
按账户覆盖:
channels.whatsapp.accounts.<id>.reactionLevel。
确认反应
messages.ackReaction 会在收到入站消息后立即发送反应,其受活跃 WhatsApp 账户的 reactionLevel 控制(当设置为 "off" 时会被抑制)。messages.ackReactionScope 用于选择私信、群组或两者:
messages.ackReaction 或将其设置为 "" 可不发送确认反应。发送失败会被记录,但不会阻止回复传递。默认范围为 "group-mentions";使用 "all" 可涵盖私信和所有符合条件的群组。
生命周期状态反应
设置messages.statusReactions.enabled: true,让 WhatsApp 在一次轮次中用状态反应替换 ack 反应,而不是保留静态的回执表情,状态会在 queued、thinking、tool activity、compaction、done 和 error 之间切换:
messages.ackReactionScope 仍控制私聊和群组的适用性;queued 状态使用与普通确认反应相同的有效表情。WhatsApp 每条消息只有一个机器人反应槽,因此生命周期更新会原地替换当前反应,并在最终的 done/error 状态后恢复确认反应。
活跃回合中的输入状态
对于允许输入状态的已接纳自动回合,代理执行开始时,WhatsApp 会发送一条composing 状态更新,并在回合保持活跃期间刷新该状态。运行完成时(包括终止失败或取消),刷新会停止。当回复分发器报告空闲时,控制器会封存并清理;如果未收到该信号,则会在一段较短的安全超时后执行。现有输入状态和抑制策略禁用输入状态的回合不会启动此活动。
输入状态是短暂的、尽力而为的活动反馈。它不是持久化消息、送达回执,也不保证每个 WhatsApp 客户端都会显示持续的活动状态;重新连接和客户端行为可能会使该指示器消失。生命周期状态反应仍然是上文所述的、具有持久外观的自愿选择状态界面。
多账户和凭据
账户选择和默认值
账户选择和默认值
账户 ID 来自
channels.whatsapp.accounts。如果存在,默认账户选择为 default;否则,使用第一个已配置的账户 ID(按字母顺序排序)。账户 ID 在内部会进行归一化以便查找。凭据路径和旧版兼容性
凭据路径和旧版兼容性
- 当前认证路径:
~/.openclaw/credentials/whatsapp/<accountId>/creds.json(备份:creds.json.bak) - 旧版默认认证位于
~/.openclaw/credentials/,对于默认账户流程仍会被识别/迁移
注销行为
注销行为
openclaw channels logout --channel whatsapp [--account <id>] 会清除该账户的 WhatsApp 认证状态。当网关可达时,注销会先停止该账户的实时监听,因此在下次重启前,已绑定的会话将不再接收消息。openclaw channels remove --channel whatsapp 也会在禁用或删除账户配置之前停止实时监听。在旧版认证目录中,oauth.json 会被保留,而 Baileys 认证文件会被移除。工具、动作与配置写入
- Agent 工具支持包括 WhatsApp 反应动作(
react)。 - 动作开关:
channels.whatsapp.actions.reactions、channels.whatsapp.actions.polls(现有动作默认值为true)、channels.whatsapp.actions.calls(默认false,见上面的 MeowCaller)。 - 频道发起的配置写入默认启用;可通过
channels.whatsapp.configWrites: false禁用。
故障排查
未绑定(需要 QR)
未绑定(需要 QR)
症状:通道状态报告未绑定。
已绑定但断开连接 / 重连循环
已绑定但断开连接 / 重连循环
症状:已绑定账号反复断开或尝试重连。静默账号可以在正常消息超时之后继续保持连接;看门狗仅在 WhatsApp Web 传输活动停止、socket 关闭,或应用层活动在更长的安全窗口内持续无响应时才会重启(参见上面的运行时模型)。修复:如果在主机连接性和时序都修复后循环仍然存在,请备份账号认证目录并重新关联:如果
~/.openclaw/logs/whatsapp-health.log 显示 Gateway inactive,但 openclaw gateway status 和 openclaw channels status --probe 都显示正常,请运行 openclaw doctor。在 Linux 上,doctor 会警告那些调用已弃用的 ~/.openclaw/bin/ensure-whatsapp.sh 脚本的旧 crontab 条目;请使用 crontab -e 删除这些条目——cron 可能缺少 systemd 用户总线环境,从而让旧脚本误报网关健康状态。代理后方的 QR 登录超时
代理后方的 QR 登录超时
症状:
openclaw channels login --channel whatsapp 在显示可用 QR 之前就失败,报 status=408 Request Time-out 或 TLS socket 断开。WhatsApp Web 登录使用网关主机的标准代理环境变量(HTTPS_PROXY、HTTP_PROXY、小写变体、NO_PROXY)。请确认网关进程继承了代理环境变量,并且 NO_PROXY 不会匹配 mmg.whatsapp.net。发送时没有活动监听器
发送时没有活动监听器
当目标账号不存在活动的网关监听器时,出站发送会快速失败。确认网关正在运行且该账号已关联。
回复出现在转录中但没有出现在 WhatsApp 中
回复出现在转录中但没有出现在 WhatsApp 中
转录行记录的是代理生成的内容;WhatsApp 投递会单独检查。OpenClaw 只有在 Baileys 为至少一条可见文本或媒体发送返回出站消息 id 之后,才会将自动回复视为已发送。确认表情回应是独立的、回复前的回执——成功的表情回应并不能证明后续文本/媒体回复已被接受。请检查网关日志中的
auto-reply delivery failed 或 auto-reply was not accepted by WhatsApp provider。群消息意外被忽略
群消息意外被忽略
请按以下顺序检查:
groupPolicy、groupAllowFrom/allowFrom、groups 白名单条目、提及门控(requireMention + 提及模式),以及 openclaw.json 中的重复键(JSON5 中后面的条目会覆盖前面的条目——每个作用域只保留一个 groupPolicy)。如果存在 channels.whatsapp.groups,WhatsApp 仍可能收到其他群组的消息,但 OpenClaw 会在会话路由之前将它们丢弃。将群组 JID 添加到 channels.whatsapp.groups,或者添加 groups["*"] 以允许所有群组,同时通过 groupPolicy/groupAllowFrom 保持发送者授权。Bun 运行时警告
Bun 运行时警告
OpenClaw 网关需要 Node。Bun 不提供 canonical 状态存储所使用的
node:sqlite API,而 doctor 会将旧的 Bun 服务迁移到 Node。系统提示词
WhatsApp 通过groups 和 direct 映射支持类似 Telegram 的群组与私聊系统提示词。
群组消息的解析方式:先确定最终有效的 groups 映射——如果某个账号定义了自己的 groups 键,那么它会完全替换根级 groups 映射(不会进行深度合并)。随后提示词查找只在这个单一结果映射上进行:
- 群组特定提示词(
groups["<groupId>"].systemPrompt):当群组条目存在并且其systemPrompt键已定义时使用。空字符串("")会抑制通配符并且不应用任何提示词。 - 群组通配符提示词(
groups["*"].systemPrompt):当特定群组条目不存在,或者存在但未定义systemPrompt键时使用。
direct 映射和 direct["*"]。
dms 仍然是轻量级的每个 DM 历史覆盖桶(dms.<id>.historyLimit)。提示词覆盖位于 direct 下。这种账号替换根级的提示词解析行为只是一个普通的浅层覆盖:任何账号级
groups/direct 键,包括显式的空对象,都会替换根级映射。它不同于上文描述的群成员白名单检查,后者在意外出现空的 groups: {} 时,对单账号提供了安全兜底。groups 使用相同的整个映射账号覆盖机制,但单个账号的空 groups: {} 会回退到根级 groups,作为迁移安全网。Telegram 的 direct 映射还具有独立的私聊主题语义。在 WhatsApp 中——或者在多个 Telegram 账号中的某一个账号中——当该账号不应继承根级群组默认设置时,请使用显式的空 groups: {}。
重要行为:
channels.whatsapp.groups既是每个群组的配置映射,也是聊天级别的群组白名单。在根级或账号级,groups["*"]都表示“该作用域接纳所有群组”。- 只有在你已经希望该作用域接纳所有群组时,才添加通配符
systemPrompt。如果你只想让固定的一组群组 ID 有资格被接纳,请在每个显式白名单条目上重复提示词,而不是使用groups["*"]。 - 群组接纳和发送者授权是两个独立检查。
groups["*"]只是扩大哪些群组会进入群组处理流程;它不会授权这些群组中的每一位发送者——这仍由groupPolicy/groupAllowFrom控制。 channels.whatsapp.direct对 DM 没有对应的副作用:direct["*"]只会在一个 DM 已经通过dmPolicy加上allowFrom或配对存储规则被接纳后,提供默认配置。