refactor(channelbridge): 冷避例外②收窄为「真故障/求救」严格子集
塔 2026-10-15 改裁定:例外给了命中词「穿透 30 分钟冷避」的权力,噪声不再只是多回 一句话,而是直接削弱冷避本身 —— 而实测全表 14 条在 598 条真实 user 消息里只命中 1 次,且那次是假阳性(QQ 机器人卡片文案里的 `bug`,而该群本来就在放这种卡片)。 - 新增 coldShoulderPierceKeywords(具名常量,注释写明为什么比全表严、依据即那条 卡片假阳性)与 hasColdShoulderPierceKeyword:保留 报错/出错/故障/紧急/救命/求助/ 帮帮我/urgent,剔出 ASCII `bug`/`error`/`help`(会命中结构化载荷/URL/卡片)与 情绪歧义的 `崩溃`/`坏了`/`坏掉`。 - 例外② 改用该子集;hasHighValueKeyword(基线,ruleLightJudge 用)**一字未改**, 两张表的分工写进注释,并有断言钉住「基线不许被这次收窄波及」。 - 例外矩阵新增三行:机器人卡片含 `bug` ⇒ 不穿透;英文 error/help ⇒ 不穿透; 情绪「我崩溃了」⇒ 不穿透(三行对上一提交均为红)。第 1/2 行继续用保留词 报错, 第 3/4/5/6 行原样保留;另加子集单元护栏(正例六条 / 反例六条)。 - 按项目「文件 ≤500 行」把冷避规则簇拆到 engage_coldshoulder.go(engage.go 528→376)。 红前绿证据:把例外判据临时还原成上一提交的 hasHighValueKeyword 全表(与该提交逐字 等价)⇒ 恰好上述三行变红、其余行与护栏保持绿;已还原(无残留)。 已知代价如实登记在案:用英文写的真求救不再穿透 —— 塔裁定以「宁可少穿透」为准; 全表(highValueKeywords)本身的收窄由塔另排切片,不在本片。 验收:go build ./... + go vet ./... + go test -count=1 ./... 全绿(73 包); channelbridge / channel / cmd/cyrene 单独跑绿;darwin/arm64 交叉编译通过。
This commit is contained in:
1 parent
13a0519115
commit
28967d3435
4 files changed
+250
-115
No files matched your search
@@ -13,7 +13,9 @@ import (
|
||||
// 由塔同步,这里只钉行为):
|
||||
//
|
||||
// ① 同句负面态度优先于例外:一句里既有「安静点」又有「报错」⇒ 仍进冷却、仍静默;
|
||||
// ② 高价值关键词(真故障)**穿透**冷却 —— 只放行这一条,且**不解除**冷却;
|
||||
// ② **真故障/求救信号**(严格子集 coldShoulderPierceKeywords)穿透冷却 ——
|
||||
// 只放行这一条,且**不解除**冷却;**不是**高价值关键词全表(`bug`/`error`/`help`
|
||||
// 与情绪歧义的 `崩溃`/`坏了`/`坏掉` 被剔出去,矩阵里有对应行);
|
||||
// ③ @她 / @全体 **不穿透**(那正是主人 2026-10-14「给」的实质);
|
||||
// ④ 主人无需例外:owner 车道在 shouldRespond 第一步就分岔,走不到冷避闸。
|
||||
//
|
||||
@@ -23,7 +25,7 @@ import (
|
||||
// TestColdShoulderCooldownMatrix 是塔 2026-10-15 例外口径定稿后的**冷却期矩阵**:
|
||||
// 逐个钉住「谁能穿透、谁不能」,行序刻意把「穿透之后仍在冷却」紧排在高价值关键词行之后。
|
||||
//
|
||||
// 每行对「本轮修前」= M18 提交 fe53e02 的性质用 bite 标出(红前红 / 护栏)——
|
||||
// 每行的 bite 标出它对**上一提交**(例外放宽为全表的那一版)的性质:红前红 / 护栏 ——
|
||||
// 不得把护栏吹成证据。
|
||||
func TestColdShoulderCooldownMatrix(t *testing.T) {
|
||||
t.Parallel()
|
||||
@@ -46,6 +48,32 @@ func TestColdShoulderCooldownMatrix(t *testing.T) {
|
||||
wantReply: true,
|
||||
bite: "本轮红前红;例外②按**正文**判定,@她 不在排除项里(与裁定 ①的差别在于本条含真故障)",
|
||||
},
|
||||
{
|
||||
// 真机实证(群 531082168,2026-10-14):机器人卡片文案里出现 `bug`。
|
||||
// 例外②收窄前(用 hasHighValueKeyword 全表)这条会**穿透冷却**;
|
||||
// 收窄成严格子集后不穿透 —— 这正是塔 2026-10-15 改裁定的依据。
|
||||
name: "bot card containing bug does NOT pierce",
|
||||
msg: func() adapter.UnifiedMessage {
|
||||
return nonOwnerGroupMsg("【猪工智能】Lv.2 正在学习 —— 唯一的bug是你至今没搞懂为什么猪喜欢拱泥")
|
||||
},
|
||||
wantReply: false,
|
||||
bite: "**本轮红前红**(上一提交用全表 ⇒ 会放行这张卡片)",
|
||||
},
|
||||
{
|
||||
// ASCII `error` / `help` 同属被剔的三类。如实记录代价:用英文写的
|
||||
// 真求救不再穿透(塔裁定以「宁可少穿透」为准)。
|
||||
name: "english error/help does NOT pierce",
|
||||
msg: func() adapter.UnifiedMessage { return nonOwnerGroupMsg("there is an error in my code, help me") },
|
||||
wantReply: false,
|
||||
bite: "**本轮红前红**(上一提交用全表 ⇒ 会放行);同时如实记录严格子集的已知代价",
|
||||
},
|
||||
{
|
||||
// 情绪/口语歧义的 `崩溃` 同属被剔的三类(「我崩溃了」= 情绪,不是故障)。
|
||||
name: "emotional 崩溃 does NOT pierce",
|
||||
msg: func() adapter.UnifiedMessage { return nonOwnerGroupMsg("这周稿子又没写完,我崩溃了") },
|
||||
wantReply: false,
|
||||
bite: "**本轮红前红**(上一提交用全表 ⇒ 会放行)",
|
||||
},
|
||||
{
|
||||
name: "cooldown is NOT lifted by the exception",
|
||||
msg: func() adapter.UnifiedMessage { return coldShoulderMention("晚上有空吗") },
|
||||
@@ -124,3 +152,47 @@ func TestColdShoulderHighValueKeywordBaselineOutsideCooldown(t *testing.T) {
|
||||
t.Fatalf("冷却过后的普通消息不该被驱动:session = %q", chat.sessionID)
|
||||
}
|
||||
}
|
||||
|
||||
// TestColdShoulderPierceKeywordSubset 直接钉住例外②的**严格子集**本身:保留的八条命中,
|
||||
// 被剔的三类(ASCII `bug`/`error`/`help`、情绪歧义的 `崩溃`、口语「糟糕」义的 `坏了`/`坏掉`)
|
||||
// 不命中。这是防「后人顺手把全表并回来」的第一道闸(第二道是矩阵里的卡片/英文/情绪三行)。
|
||||
//
|
||||
// 同时钉住**分工边界**:`hasHighValueKeyword` 全表一个字未改 —— `bug` 在 ruleLightJudge
|
||||
// 眼里仍是高价值词(基线行为),只是不再拥有「穿透冷避」的权力。
|
||||
func TestColdShoulderPierceKeywordSubset(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
cases := []struct {
|
||||
name string
|
||||
text string
|
||||
want bool
|
||||
}{
|
||||
{"report error", "服务报错了,谁知道怎么回事", true},
|
||||
{"error occurred", "这里出错了", true},
|
||||
{"fault", "群文件传输故障", true},
|
||||
{"urgent", "this is urgent", true},
|
||||
{"rescued", "救命啊", true},
|
||||
{"asking for help", "帮帮我看看这个", true},
|
||||
{"help me", "谁能帮我一下", false}, // 「帮我」不在表里,只有「帮帮我」在
|
||||
{"bug in a bot card", "唯一的bug是你至今没搞懂为什么猪喜欢拱泥", false},
|
||||
{"english error/help", "there is an error in my code, help me", false},
|
||||
{"emotional collapse", "这周稿子又没写完,我崩溃了", false},
|
||||
{"colloquial oops", "坏了,我忘了", false},
|
||||
{"plain chatter", "今晚吃点什么", false},
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
tc := tc
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
if got := hasColdShoulderPierceKeyword(tc.text); got != tc.want {
|
||||
t.Fatalf("hasColdShoulderPierceKeyword(%q) = %v, want %v", tc.text, got, tc.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// 分工边界:全表(基线)不许被这次收窄波及。
|
||||
if !hasHighValueKeyword("唯一的bug是你至今没搞懂为什么猪喜欢拱泥") {
|
||||
t.Fatal("hasHighValueKeyword 被改动了:它是基线行为(ruleLightJudge 用),本片不许动")
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user