fix(backthink): 主动推送降频(15min心跳+60min≤1次+推后30min冷却+刚聊过10min冷避) + 情绪约束(分类限频/活跃感知) + 剥离'我决定:push'内部标记
This commit is contained in:
1 parent
03d7050e1e
commit
12031bd13c
14 files changed
+662
-149
No files matched your search
@@ -307,3 +307,59 @@ func TestPusher_SpecificPlatform_NoIM_FallsBackToActive(t *testing.T) {
|
||||
t.Fatalf("Detail = %q, want it to include the kook hint", res.Detail)
|
||||
}
|
||||
}
|
||||
|
||||
// TestStripInternalMarkers_CleanBody verifies the internal decision/channel
|
||||
// markers are removed from a proactive body in every shape a model may emit
|
||||
// (bracketed, full-width, bare), so a marker can never reach the user-facing text.
|
||||
func TestStripInternalMarkers_CleanBody(t *testing.T) {
|
||||
t.Parallel()
|
||||
cases := []struct {
|
||||
name, in, want string
|
||||
}{
|
||||
{name: "bracketed own lines", in: "[我决定:push]\n[渠道:kook]\n我想提醒你明天有体检", want: "我想提醒你明天有体检"},
|
||||
{name: "inline bracketed", in: "[我决定:push] [渠道:WEB] 晚上早点睡", want: "晚上早点睡"},
|
||||
{name: "bare markers", in: "我决定:push\n渠道:kook\n记得喝水哦", want: "记得喝水哦"},
|
||||
{name: "full-width brackets", in: "【我决定:push】【渠道:telegram】早点休息", want: "早点休息"},
|
||||
{name: "marker trailing body", in: "今天风很大[我决定:push][渠道:kook]", want: "今天风很大"},
|
||||
{name: "no marker", in: "洗衣机洗好了,衣服可以收了", want: "洗衣机洗好了,衣服可以收了"},
|
||||
{name: "empty", in: "", want: ""},
|
||||
}
|
||||
for _, tc := range cases {
|
||||
tc := tc
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
if got := stripInternalMarkers(tc.in); got != tc.want {
|
||||
t.Fatalf("stripInternalMarkers(%q) = %q, want %q", tc.in, got, tc.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// TestThink_BareMarkersStrippedFromBody is the regression for the KOOK leak: a
|
||||
// conclusion carrying bare (bracket-less) "我决定:push"/"渠道:..." markers must
|
||||
// still deliver a clean warm body, never the structured decision text.
|
||||
func TestThink_BareMarkersStrippedFromBody(t *testing.T) {
|
||||
t.Parallel()
|
||||
conclusion := "我决定:push\n渠道:kook\n记得喝水哦"
|
||||
thinker := &mockThinker{text: conclusion}
|
||||
im := &mockIMAdapter{}
|
||||
b, _ := newBackthink(t, Config{}, Deps{
|
||||
Thinker: thinker,
|
||||
State: &mockState{state: UserState{}},
|
||||
IM: im,
|
||||
Gate: &mockGate{allow: true},
|
||||
Now: func() time.Time { return dayAt() },
|
||||
})
|
||||
|
||||
b.think(context.Background(), heartbeatTrigger())
|
||||
|
||||
if im.count() != 1 {
|
||||
t.Fatalf("im calls = %d, want 1", im.count())
|
||||
}
|
||||
if im.last.Content != "记得喝水哦" {
|
||||
t.Fatalf("im content = %q, want the clean body", im.last.Content)
|
||||
}
|
||||
if strings.Contains(im.last.Content, "我决定") || strings.Contains(im.last.Content, "渠道") {
|
||||
t.Fatalf("internal marker leaked into the message body: %q", im.last.Content)
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user