Slack 线程里它老是插嘴报警:thread_followup_silence 让机器人真的闭嘴


线上故障处理完了,同事在 Slack 线程里互相道谢:“eu 集群恢复了吧?”“恢复完了,09:12 切回来的。”“辛苦了,我关事故单。“你的机器人在这条线程里插了一句:⚠️ 模型只返回了沉默标记,没有给出回复。

它说得没错——它确实决定不说话。但这句话对线程里的三个人来说,纯属噪音。

那句警告本来是为了什么

Hermes 有一个保护机制:如果模型对一个明显该回答的消息只回了沉默标记([SILENT] 之类),网关不会让沉默就这么过去,而是在聊天里贴一条提示。逻辑很朴素:宁可多一句提示,也不能让一个真正的问题没人回答。

Slack 上的判断逻辑是这样的:机器人在一条频道线程里参与过之后,这条线程里的后续回复会被当成“可能是在跟机器人说话”,判定结果是“不确定是否期待回复”。于是当模型判断“这不是问我的”、只回一个沉默标记时,这个标记会被拒绝,那张警告卡片就出现了。

问题在于真实世界的样子:一条已经热起来的线程里,人和人之间的对话远多于人对机器人的对话。

新开关做什么

thread_followup_silence 是一个可选开关,默认关闭。打开之后,非私聊线程里、没有点名机器人的回复会被判定为“不期待回复”,于是沉默标记可以正常生效,什么提示都不发。

# config.yaml —— 顶层 slack: 块,或 platforms.slack.extra,两处都有时 extra 生效
slack:
  thread_followup_silence: true

也可以用环境变量:

export SLACK_THREAD_FOLLOWUP_SILENCE=true

对照表:

消息类型 开关关闭(默认) 开关打开
频道线程里未被点名的回复 沉默会被替换成警告 沉默生效,什么都不发
@ 提及机器人 正常回答 正常回答
1:1 私聊 正常回答 正常回答
群聊私信(多人 DM) 按共享频道规则 不变
命令、表情回应触发 正常处理 正常处理
平坦频道(reply_in_thread: false) 保持原有提示 保持原有提示

也就是说它只动了一个很窄的口子:频道线程里、既没 @ 你、也不是私聊的那类“旁观消息”。

为什么默认还是关

因为原来的默认行为解决的是一个真问题。如果有人在一条线程里问了机器人一句、却没 @ 它,机器人沉默掉、又不给任何提示,这个问题就消失了——没人知道它被跳过。

所以这个开关面向的是特定工作区:机器人参与的线程大部分是人与人之间的对话,那张警告卡就是纯噪音。这类团队打开它,其余团队什么都不用做,默认行为逐字节不变。

还有个更窄的替代品:ignore_other_user_mentions。它只跳过“开头 @ 了别人”的消息,普通跟帖照常回答。如果你的痛点是“同事互相点名时机器人别插嘴”,那个更合适;如果痛点是“整条线程的人聊得很热闹,机器人每条都要报一次沉默”,就是这次这个开关。

怎么确认它生效了

打开之后,找一条机器人参与过的线程,发一条不带 @ 的回复,等模型的判断:

  • 如果模型决定不说话:线程里应该什么都不多出来,没有那张警告卡。
  • @ 机器人再问一句:应该照常回答——如果这时它也不说话了,说明有别的东西在拦(比如频道级的提及策略),不是这个开关。

想看更底层的判定,可以在网关日志里观察这类线程回复的“是否期待回复”字段:开关打开后它从“不确定”变成“否”。

现在能用吗

这个改动随 v0.22.7(11 月 3 日)发布,已经是正式版本里的配置项,hermes update 之后就能设。

如果你还想处理另一种噪音——多 agent 房间里同一个“沉默”判定在不同场景下的取舍,站内的按房间允许沉默那篇是同一套机制的另一面;Slack 平台的完整配置项在命令参考的 Slack 一节里;想让重要的事真的能找到你,通知与提醒指南讲的是收端。