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 一节里;想让重要的事真的能找到你,通知与提醒指南讲的是收端。