From abc551cc7660d38cc48408e09373b30469a45d66 Mon Sep 17 00:00:00 2001 From: Autumn-27 <53844290+Autumn-27@users.noreply.github.com> Date: Tue, 28 Jul 2026 11:52:53 -0400 Subject: [PATCH] =?UTF-8?q?fix(worker):=20=E5=86=99=E5=9B=9E=E8=AE=A1?= =?UTF-8?q?=E6=95=B0=E6=8C=89=E4=BA=8B=E5=AE=9E/=E8=B5=84=E4=BA=A7/?= =?UTF-8?q?=E6=BC=8F=E6=B4=9E=E5=88=86=E5=BC=80=E7=BB=9F=E8=AE=A1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 原先 record_fact/insert_assets/report_finding 共用一个 wrote 计数器, 日志却统一打印成"写回 N 条事实",把资产、漏洞也算进"事实"里,容易误导 (如枚举意图插入 25 个资产 + 1 条 fact 会显示"写回 26 条事实")。 改为 WriteCounts{Facts,Assets,Findings} 分类计数,日志打印 "写回 事实1 资产25 漏洞0",Total() 保留"是否落库任何东西"的判定。 Co-Authored-By: Claude Opus 4.8 (1M context) --- agent/tools.go | 32 ++++++++++++++++++++++++++------ agent/tools_insert.go | 2 +- agent/worker.go | 9 +++++---- server/engine.go | 8 ++++---- 4 files changed, 36 insertions(+), 15 deletions(-) diff --git a/agent/tools.go b/agent/tools.go index cf78dcd..f30cdd5 100644 --- a/agent/tools.go +++ b/agent/tools.go @@ -58,7 +58,7 @@ type ToolSet struct { ownerNode int64 GoalMet bool Reason string - wrote int + writes WriteCounts // killWork, if set, terminates a running work by intent id (engine callback, // wired by the planner). nil = the kill_work tool reports unavailable. killWork func(intentID int64) error @@ -86,9 +86,29 @@ type EnrichTrigger interface { ProbeSite(id int64, url string) } -// Wrote reports how many facts this run wrote back (so the engine can tell -// "explored but persisted nothing" apart from a completed intent). -func (t *ToolSet) Wrote() int { return t.wrote } +// WriteCounts breaks down what a worker persisted this run, by node kind, so the +// engine can log an accurate "wrote back" summary instead of lumping assets and +// findings under "facts" (record_fact → Facts, insert_assets → Assets, +// report_finding → Findings; each element of a batch counts once). +type WriteCounts struct { + Facts int + Assets int + Findings int +} + +// Total is every node persisted this run, regardless of kind — the +// "explored but persisted nothing" signal (Total == 0). +func (w WriteCounts) Total() int { return w.Facts + w.Assets + w.Findings } + +// String renders the per-kind breakdown for logs, e.g. "事实1 资产25 漏洞0". +func (w WriteCounts) String() string { + return fmt.Sprintf("事实%d 资产%d 漏洞%d", w.Facts, w.Assets, w.Findings) +} + +// Writes reports what this run wrote back, split by node kind (so the engine can +// tell "explored but persisted nothing" apart from a completed intent, and log an +// honest breakdown instead of calling assets/findings "facts"). +func (t *ToolSet) Writes() WriteCounts { return t.writes } func NewToolSet(ts *db.ExplorationStore, worker string) *ToolSet { return &ToolSet{ts: ts, worker: worker} @@ -594,7 +614,7 @@ func (t *ToolSet) addFinding() actool.CoreTool { // conversation context: no exploration store available, cannot record finding return actool.Errorf("report_finding 需要任务上下文(exploration store 未初始化)"), nil } - t.wrote++ + t.writes.Findings++ return actool.Text(fmt.Sprintf("finding recorded: %d", id)), nil }) } @@ -642,7 +662,7 @@ func (t *ToolSet) recordOneFact(it factItem, defaultIntent int64) (int64, error) if intent > 0 { _ = t.ts.Link(intent, db.RelYields, id) // chain: intent -> fact } - t.wrote++ + t.writes.Facts++ return id, nil } diff --git a/agent/tools_insert.go b/agent/tools_insert.go index ba7c7c4..f1df3b0 100644 --- a/agent/tools_insert.go +++ b/agent/tools_insert.go @@ -272,7 +272,7 @@ func (t *ToolSet) insertAssets() actool.CoreTool { continue } results = append(results, result{Index: i, ID: id, Type: typ}) - t.wrote++ + t.writes.Assets++ t.anchorOwner(id) } diff --git a/agent/worker.go b/agent/worker.go index c476889..2370ce7 100644 --- a/agent/worker.go +++ b/agent/worker.go @@ -193,9 +193,10 @@ func renderWorkerGraphOverview(data map[string]any) string { // Execute runs one intent. hooks (the per-task Guard) gates every tool call; may // be nil. emit, if non-nil, receives one ActivityRecord per execution step. // Returns the terminal reason (so the engine can distinguish completed vs -// max_turns) and how many facts were written back (so an intent that explored but -// persisted nothing isn't mistaken for done). -func (w *Worker) Execute(ctx context.Context, name string, taskID int64, as *db.AssetStore, ts *db.ExplorationStore, intent *db.Node, hooks harness.HookRunner, emit func(db.Activity), enr EnrichTrigger) (harness.TerminalReason, int, error) { +// max_turns) and a per-kind breakdown of what was written back (so an intent that +// explored but persisted nothing isn't mistaken for done, and the engine can log +// facts/assets/findings separately instead of lumping them under "facts"). +func (w *Worker) Execute(ctx context.Context, name string, taskID int64, as *db.AssetStore, ts *db.ExplorationStore, intent *db.Node, hooks harness.HookRunner, emit func(db.Activity), enr EnrichTrigger) (harness.TerminalReason, WriteCounts, error) { tsx := NewToolSet(ts, name) tsx.SetTaskID(taskID) if as != nil { @@ -298,5 +299,5 @@ func (w *Worker) Execute(ctx context.Context, name string, taskID int64, as *db. defer runCancel() } _, reason, err := captureRunSession(runCtx, s, input, emitWrap) - return reason, tsx.Wrote(), err + return reason, tsx.Writes(), err } diff --git a/server/engine.go b/server/engine.go index 690b2bd..9694911 100644 --- a/server/engine.go +++ b/server/engine.go @@ -532,7 +532,7 @@ func (e *Engine) workerLoop(ctx context.Context, t *Task, name string) { // 不要误标 blocked。此时 worker 通常已在 settlement 阶段把结果写回。 if ectx.Err() != nil && e.isSettling(t.ID) { _ = t.Store.SetIntentState(intent.ID, "exhausted") - log.Printf("[worker %s] task %s 意图 #%d 因任务超时收尾结束(exhausted),写回 %d 条", name, t.ID, intent.ID, wrote) + log.Printf("[worker %s] task %s 意图 #%d 因任务超时收尾结束(exhausted),写回 %s", name, t.ID, intent.ID, wrote) e.touch(t.ID) continue } @@ -563,13 +563,13 @@ func (e *Engine) workerLoop(ctx context.Context, t *Task, name string) { state = "blocked" case reason == harness.ReasonMaxTurns: state = "exhausted" - log.Printf("[worker %s] intent %d 撞步数上限(exhausted),本次写回事实 %d 条", name, intent.ID, wrote) + log.Printf("[worker %s] intent %d 撞步数上限(exhausted),本次写回 %s", name, intent.ID, wrote) case reason == harness.ReasonTimeout: state = "exhausted" - log.Printf("[worker %s] intent %d 运行超时(exhausted),收尾后写回事实 %d 条", name, intent.ID, wrote) + log.Printf("[worker %s] intent %d 运行超时(exhausted),收尾后写回 %s", name, intent.ID, wrote) } _ = t.Store.SetIntentState(intent.ID, state) - log.Printf("[worker %s] task %s 意图 #%d 结束: %s (写回 %d 条事实)", name, t.ID, intent.ID, state, wrote) + log.Printf("[worker %s] task %s 意图 #%d 结束: %s (写回 %s)", name, t.ID, intent.ID, state, wrote) e.touch(t.ID) t.NotifyDone(intent.ID) // results changed the graph -> wake the planner (with the just-finished intent id) }