OpenClaw 群里两个机器人互相刷屏:bot loop 保护与群消息准入怎么配

2026-08-17

把 OpenClaw 接进一个已经有别的机器人的群,很容易撞上这么一幕:对方 bot 发一条播报,OpenClaw 觉得该回一句,对方 bot 又把这句当成新消息触发了自己的规则,于是两边你一句我一句,群成员只能看着屏幕往上滚。这种事故的麻烦之处在于,它不是配置写错了,而是两套自动化系统各自都在”正常工作”。

很多人的第一反应是把 OpenClaw 在这个群里彻底闭嘴。但真要闭嘴,你其实是在放弃它进群的意义。OpenClaw 文档里对这件事的处理分成两层:一层是核心的 pair loop protection,专门针对”两个机器人身份互相回复”这个具体形态;另一层更靠前,是群消息本身的准入与唤醒规则,决定了消息压根有没有资格走到需要 loop 保护的那一步。

这两层经常被混在一起讨论,结果是有人把 groupPolicy 关成 disabled 来”解决” bot 刷屏,也有人调了半天 botLoopProtection 却发现群里根本没开 allowBots、守卫从来没被触发过。下面按官方文档把两层分开讲清楚。

先弄清一条群消息要过几道闸

OpenClaw 对 Discord、iMessage、Matrix、Microsoft Teams、QQBot、Signal、Slack、Telegram、WhatsApp、Zalo 这些支持群聊的渠道套用同一套群规则。文档给出的判定流程是这样的:

groupPolicy? disabled -> drop
groupPolicy? allowlist -> group allowed? no -> drop
requireMention? yes -> mentioned? no -> store for context only
mention/reply/command/DM -> user request
always-on group chatter -> user request, or room event when configured

文档把它归纳成三个互相独立的控制点,官方的 TL;DR 写得很直白:私聊访问由 *.allowFrom 控制,群访问由 *.groupPolicy 加名单(*.groups*.groupAllowFrom)控制,回复触发由 mention 门控(requireMention/activation)控制。评估顺序是 groupPolicy → 群名单 → mention 门控。

三个默认值也值得记一下:群默认是受限的(groupPolicy: "allowlist",没加名单前群发送者一律被拦),回复默认需要 mention,最终回复文本默认自动发到房间(visibleReplies: "automatic")。还有一条运行时安全兜底:当某个渠道的配置块整个缺失(channels.<provider> 不存在)时,群策略会 fail closed 退到 allowlist,而不是去继承 channels.defaults.groupPolicy,网关会按账号各记录一次这个回退。

策略行为
"open"群绕过名单,但 mention 门控仍然生效
"disabled"完全屏蔽群消息
"allowlist"只放行匹配名单的群/房间

弄清这三道闸之后你会发现:如果 OpenClaw 从来没被对方 bot 的消息唤醒过,那不是 loop 保护的功劳,而是这条消息在更早的地方就被丢了。

bot loop 保护到底守的是什么

OpenClaw 可以接收其它机器人发的消息——前提是渠道支持 allowBots 且你打开了它。文档说得很明确:当这条路径被启用后,pair loop protection 才用来阻止两个 bot 身份无限互相回复。

守卫本身由核心的 inbound reply runner 执行,不是各渠道各写一份。每个支持的渠道负责把自己的入站事件映射成一组通用事实:账号或 scope、会话 id、发送方 bot id、接收方 bot id。核心按”参与者对”来记账,而且是双向合并的——A 到 B 和 B 到 A 算同一对;然后套一个滑动窗口预算,超预算之后在冷却期内压制这一对。

内置默认值只有四个键:

默认值含义
enabledtrue对支持该守卫的渠道生效
maxEventsPerWindow20一对 bot 在窗口内可交换的事件数
windowSeconds60滑动窗口长度
cooldownSeconds60超预算后的压制时长

有几件事这个守卫明确不管:人写的消息、单 bot 部署、自消息过滤,以及没超预算的 bot 回复。换句话说它是一个”刹车”而不是”闸门”——前 20 条来回照样发得出去,它只保证不会无限循环下去。如果你的困扰是”一分钟内来回二十条也受不了”,那要调的是 maxEventsPerWindow,而不是指望默认值救场。

共享基线写在 channels.defaults.botLoopProtection,一次设定,所有支持的渠道拿同一份底:

{
  channels: {
    defaults: {
      botLoopProtection: {
        maxEventsPerWindow: 20,
        windowSeconds: 60,
        cooldownSeconds: 60,
      },
    },
  },
}

文档特别提醒:只有当你的渠道策略是有意允许 bot 之间对话、且不需要自动压制时,才把 enabled 设成 false

覆盖优先级:五层,从窄到宽

支持该守卫的渠道会把自己的配置逐键叠加在共享默认之上。官方给的优先级顺序(最窄优先)是:

  1. channels.<channel>.<room-or-space>.botLoopProtection(渠道支持按会话覆盖时)
  2. channels.<channel>.accounts.<account>.botLoopProtection(渠道支持多账号时)
  3. channels.<channel>.botLoopProtection(渠道支持顶层默认时)
  4. channels.defaults.botLoopProtection
  5. 内置默认值

“逐键”这三个字是关键:你在房间级只写了 maxEventsPerWindowwindowSecondscooldownSeconds 依然从上层继承,不会因为你写了一个更窄的块就整段被替换。官方示例把几种粒度放在一起了:

{
  channels: {
    defaults: {
      botLoopProtection: {
        maxEventsPerWindow: 20,
      },
    },
    discord: {
      botLoopProtection: {
        maxEventsPerWindow: 8,
      },
      accounts: {
        secondary: {
          allowBots: true,
          botLoopProtection: {
            maxEventsPerWindow: 5,
            cooldownSeconds: 90,
          },
        },
      },
    },
    googlechat: {
      allowBots: true,
      groups: {
        "spaces/AAAA": {
          botLoopProtection: {
            maxEventsPerWindow: 5,
          },
        },
      },
    },
    matrix: {
      allowBots: "mentions",
      groups: {
        "!roomid:example.org": {
          botLoopProtection: {
            maxEventsPerWindow: 5,
          },
        },
      },
    },
    slack: {
      allowBots: "mentions",
      botLoopProtection: {
        maxEventsPerWindow: 8,
      },
    },
  },
}

顺带留意 allowBots 的取值不只是布尔:示例里 Matrix 和 Slack 用的是 "mentions",也就是只接受提到自己的 bot 消息。这本身就是一道比调窗口更有效的减噪手段。

哪些渠道真的支持这个守卫

不是所有渠道都能开。文档列出的支持情况如下:

渠道依据的入站事实记账维度
Discord原生 author.botDiscord 账号、频道、bot 对
Feishu已准入的 bot 群消息的 sender_type=botFeishu 账号、会话、bot 对
Google Chat已接受消息的 sender.type=BOT账号、space、bot 对
Matrix已配置的 Matrix bot 账号Matrix 账号、房间、已配置的 bot 对
Slack已接受消息的原生 bot_idSlack 账号、频道、bot 对

其中飞书是个特例:文档写明它有意只用 channels.defaults.botLoopProtection 这一份共享基线,不提供更窄的覆盖。所以如果你在飞书群遇到 bot 互刷,别去找渠道级或群级的 botLoopProtection 键——按文档它就不存在,你能调的只有 defaults。渠道怎么配、准入怎么走,可以先过一遍渠道接入的通用流程与配对机制

至于清单之外的渠道,文档的态度是:那些无法暴露可靠入站 bot 身份的渠道,继续沿用它们原有的自消息过滤和访问策略过滤,在能同时识别出 bot 对的两个参与者之前,不应该接入这个守卫。这句话反过来读也有用——你在某个渠道上找不到 botLoopProtection 生效的迹象,先怀疑这个渠道压根没接。渠道侧连不上、消息进不来的排查另有一份对照表,见渠道连不上的官方排查表

上游减噪:别让它被唤醒才是最省事的

loop 保护是最后一道刹车,真正决定群里安不安静的是唤醒规则。

mention 门控。 群消息默认要求 mention,除非按群覆盖。文档列出了几类”隐式 mention 事实”及其内置生产者:回复 bot 的消息(Discord、Microsoft Teams、QQBot、Slack、Telegram),引用 bot 的消息(WhatsApp、Zalo 个人号),bot 加入线程(Mattermost、Slack、Tlon)。每一项在渠道能产出时默认启用,把对应的 implicitMentions 标志设成 false 就能让这条事实不再绕过门控,原生显式 mention 不受影响;对不产出该事实的渠道,这个标志没有作用。

mentionPatterns 的误伤。 配置的 mention 正则是兜底触发器,用在平台没有原生 bot mention、或者你想让 openclaw: 这类纯文本也算 mention 的场合。默认情况下,只要渠道把 provider 和会话事实传进了 mention 检测,这些正则就在所有地方生效——这正是”一个宽泛的词把 agent 在每个群都叫醒”的来源。收窄的写法是按渠道设作用域策略:

{
  messages: {
    groupChat: {
      mentionPatterns: ["\\bopenclaw\\b", "\\bops bot\\b"],
    },
  },
  channels: {
    slack: {
      mentionPatterns: {
        mode: "deny",
        allowIn: ["C0123OPS"],
      },
    },
  },
}

mode: "deny" 表示该渠道默认关掉正则 mention,只在 allowIn 列出的会话里开;默认的 mode: "allow" 反过来,用 denyIn 在吵闹的房间关掉。两者同时命中同一个 id 时 denyIn 赢。支持作用域策略的渠道及其 id 形式:Discord 用频道 id,Matrix 用房间 id,Slack 用频道 id,Telegram 用群聊 id 或论坛话题的 chatId:topic:threadId,WhatsApp 用 123@g.us 这类会话 id。支持多账号的渠道还可以把同一份策略写在 channels.<channel>.accounts.<accountId>.mentionPatterns,账号级优先于渠道顶层。

另外两条容易踩:把某个群或某个发送者加进名单,并不会关掉 mention 门控,要让所有消息都触发得把那个群的 requireMention 设成 false;而原生平台 mention 和配置正则是两码事,Discord、Slack、Telegram、Matrix、Signal 等渠道能证明消息显式提到了 bot 时,即使正则被 deny,原生 mention 照样触发。

/activation 群主可以用独立消息切换本群激活模式,/activation mention/activation always。这是核心的 owner 门控命令、只在群聊里生效;owner 指发送者匹配 commands.ownerAllowFrom,渠道的 allowFrom 只管普通渠道与命令访问。存下来的模式会覆盖那个群的 requireMention(在会读取它的渠道上:Google Chat、QQBot、Telegram、WhatsApp),群系统提示的开场白也会反映当前模式。

让模型自己决定说不说话

还有一条不太一样的思路:不去卡消息进不进来,而是卡回复出不出去。

群聊默认 messages.groupChat.visibleReplies: "automatic",模型的最终文本直接发到房间。改成 "message_tool" 之后,共享房间里由 agent 自己调用 message(action=send) 来决定何时开口;文档说这种模式在工具调用可靠的模型上效果最好,并举了 GPT-5.6 Sol 作为例子(这是官方文档的说法)。如果模型漏掉了工具却返回了实质性的最终文本,OpenClaw 会把这段文本留作私有而不发到房间。

{
  messages: {
    groupChat: {
      visibleReplies: "message_tool",
    },
  },
}

文档同时给了几条边界:当前工具策略下消息工具不可用时,OpenClaw 会退回自动可见回复,而不是悄悄把回复吞掉,openclaw doctor 会对这种不一致发出警告;命令回合绕过 message_tool,原生斜杠命令和已授权的文本 /... 命令都会把响应发到来源会话。另外,工具专用模式取代了过去那种逼模型回 NO_REPLY 的做法——在这个模式下提示词里根本不定义 NO_REPLY 契约,“不说话”就是不调用消息工具。

群会话与上下文,别顺手把隔离弄丢

群会话默认按群独立:agent:<agentId>:<channel>:group:<id>,房间/频道用 channel: 段,Telegram 论坛话题再加 :topic:<threadId>。全局 session.groupScope 支持 "per-group"(默认)和 "main",也可以在 binding 里把某个可信房间设成 "main"。文档在这里挂了一个警告:设成 "main" 的房间就是主会话,不在 sandbox mode: "non-main" 的覆盖范围内,依赖那条沙箱边界时不要把不可信或公开房间并进 main。沙箱、工具策略、提权三者的边界另见沙箱、工具策略与提权的区别,会话键与主会话的模型见会话模型

上下文这一侧也有开关。默认 contextVisibility: "all",即名单只决定谁能触发动作,不决定模型能看到哪些引用与历史片段;改成 "allowlist" 则只注入名单内发送者的历史/线程/引用/转发上下文,"allowlist_quote" 在此基础上保留任意发送者的显式引用消息。未知的策略组合按 fail closed 处理,直接省略上下文。群历史窗口用 messages.groupChat.historyLimit 设全局默认,channels.<channel>.historyLimit 或账号级覆盖,设 0 关闭。

以 WhatsApp 为例,待处理群消息(默认 50 条)中未触发运行的那些,会以 [Chat messages since your last reply - for context] 前缀注入,触发行放在 [Current message - respond to this] 下;运行后清空待处理窗口,已经在会话里的消息不重复注入。窗口解析顺序是 channels.whatsapp.accounts.<id>.historyLimitchannels.whatsapp.historyLimitmessages.groupChat.historyLimit → 50。

什么时候这套办法不解决问题

有几种情况得说清楚。

pair loop protection 只针对能识别出双方 bot 身份的场景。如果对面不是标准的 bot 身份,而是某人用普通账号跑的转发脚本,在渠道看来那就是人写的消息,守卫按文档明确不生效——这时候你只能靠 groupAllowFrom 之类的发送者名单去处理。

守卫也不保证”一条都不重复”。它是滑动窗口加冷却,默认允许一对 bot 在 60 秒内交换 20 个事件。冷却期结束后如果双方的触发条件没变,同样的来回可能再来一轮。真正的根治还是在唤醒规则上:把 allowBots 收成 "mentions",或者干脆不开 bot 消息路径。

跨产品的横向对比这篇不做。官方文档没有给出该守卫的性能开销、记账的内存占用、多大规模的群会有影响这类数字,也没有说明冷却期内被压制的事件是否会留下可查询的计数,这些都别自己脑补。

最后一条实践建议:动这些配置前先想清楚你要的是”完全静音”还是”少说话”。要完全静音,groupPolicy: "disabled" 一行就够,不用碰 loop 保护;要它继续在群里干活只是别失控,那顺序应该是先收 allowBots 与 mention 作用域,再调 botLoopProtection 的窗口和冷却,最后才考虑 visibleReplies: "message_tool" 把开口权交给模型。三层的排查成本从低到高,倒着来会很费时间。

延伸阅读


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

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