From 5244500feec5b31500e389653eeee13b59320a92 Mon Sep 17 00:00:00 2001 From: Autumn-27 <53844290+Autumn-27@users.noreply.github.com> Date: Wed, 19 Aug 2026 09:17:04 -0400 Subject: [PATCH] =?UTF-8?q?fix(scheduler):=20=E4=BA=8B=E4=BB=B6=E6=B0=B4?= =?UTF-8?q?=E4=BD=8D=E6=AF=8F=20tick=20=E9=83=BD=E6=8E=A8=E8=BF=9B,?= =?UTF-8?q?=E9=81=BF=E5=85=8D=E6=96=B0=E5=BB=BA=E8=A7=A6=E5=8F=91=E5=99=A8?= =?UTF-8?q?=E5=9B=9E=E6=94=BE=E5=8E=86=E5=8F=B2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 原来 fire{Findings,Goals,TaskTimeouts,TaskCreates,ToolCalls} 在没有 对应触发器时直接 return,不推进水位。于是水位会一直停在旧值;等用户 新建/启用触发器(如预置的 reporter on_tool_call),下一个 tick 就把 水位之后的全部历史事件当成新事件触发一遍(逐条唤起 reporter 写报告)。 改为:即便当前没有对应触发器,也照常把水位推进到最新(静默消费), 触发动作只在存在触发器时执行。这样触发器只对启用之后出现的事件生效, 不再回放历史。goal 用 fired-set 同理:无触发器时也标记为已消费。 --- server/scheduler.go | 38 +++++++++++++++++++++++--------------- 1 file changed, 23 insertions(+), 15 deletions(-) diff --git a/server/scheduler.go b/server/scheduler.go index 1aeeb17..01fb6d1 100644 --- a/server/scheduler.go +++ b/server/scheduler.go @@ -155,19 +155,22 @@ func (sc *Scheduler) fireFindings(triggers []*db.AgentTrigger) { want = append(want, tr) } } - if len(want) == 0 { - return - } last, _ := strconv.ParseInt(sc.mustState(schedKeyLastFinding), 10, 64) events, err := sc.pg.NewFindingsSince(last) if err != nil || len(events) == 0 { return } + // Advance the watermark whether or not any on_finding trigger is active: a + // finding fires only for triggers live at the moment it appears. Otherwise a + // trigger enabled later would replay the entire historical backlog at once. maxID := last for _, e := range events { if e.NodeID > maxID { maxID = e.NodeID } + if len(want) == 0 { + continue + } msgCtx := fmt.Sprintf("\n\n【本次由任务发现 finding 触发】\n任务: #%d %s(目标:%s)\n发现: [%s/%s] %s", e.TaskID, e.TaskDesc, e.TaskGoal, e.VulnClass, e.Severity, e.Summary) for _, tr := range want { @@ -185,13 +188,12 @@ func (sc *Scheduler) fireGoals(triggers []*db.AgentTrigger) { want = append(want, tr) } } - if len(want) == 0 { - return - } events, err := sc.pg.MetGoals() if err != nil || len(events) == 0 { return } + // Mark goals as consumed whether or not a trigger is active, so enabling an + // on_goal_met trigger later doesn't replay every already-met goal. fired := sc.firedGoalSet() changed := false for _, e := range events { @@ -200,6 +202,9 @@ func (sc *Scheduler) fireGoals(triggers []*db.AgentTrigger) { } fired[e.NodeID] = true changed = true + if len(want) == 0 { + continue + } msgCtx := fmt.Sprintf("\n\n【本次由任务完成目标触发】\n任务: #%d %s(目标:%s)\n达成目标: %s", e.TaskID, e.TaskDesc, e.TaskGoal, e.Summary) for _, tr := range want { @@ -220,19 +225,20 @@ func (sc *Scheduler) fireTaskTimeouts(triggers []*db.AgentTrigger) { want = append(want, tr) } } - if len(want) == 0 { - return - } last, _ := strconv.ParseInt(sc.mustState(schedKeyLastTimeout), 10, 64) events, err := sc.pg.TimedOutTasksSince(last) if err != nil || len(events) == 0 { return } + // Advance the watermark even with no active trigger — see fireFindings. maxID := last for _, e := range events { if e.NodeID > maxID { maxID = e.NodeID } + if len(want) == 0 { + continue + } msgCtx := fmt.Sprintf("\n\n【本次由任务超时触发】\n任务: #%d %s(目标:%s)", e.TaskID, e.TaskDesc, e.TaskGoal) for _, tr := range want { @@ -251,19 +257,20 @@ func (sc *Scheduler) fireTaskCreates(triggers []*db.AgentTrigger) { want = append(want, tr) } } - if len(want) == 0 { - return - } last, _ := strconv.ParseInt(sc.mustState(schedKeyLastTaskCreate), 10, 64) events, err := sc.pg.NewTasksSince(last) if err != nil || len(events) == 0 { return } + // Advance the watermark even with no active trigger — see fireFindings. maxID := last for _, e := range events { if e.NodeID > maxID { maxID = e.NodeID } + if len(want) == 0 { + continue + } msgCtx := fmt.Sprintf("\n\n【本次由任务创建触发】\n任务ID: #%d\n任务描述: %s\n任务目标: %s", e.TaskID, e.TaskDesc, e.TaskGoal) for _, tr := range want { @@ -283,19 +290,20 @@ func (sc *Scheduler) fireToolCalls(triggers []*db.AgentTrigger) { want = append(want, tr) } } - if len(want) == 0 { - return - } last, _ := strconv.ParseInt(sc.mustState(schedKeyLastToolCall), 10, 64) events, err := sc.pg.NewToolCallsSince(last) if err != nil || len(events) == 0 { return } + // Advance the watermark even with no active trigger — see fireFindings. maxID := last for _, e := range events { if e.NodeID > maxID { maxID = e.NodeID } + if len(want) == 0 { + continue + } errTag := "" if e.ToolIsErr { errTag = "[error] "