From b39284f1c67fd2f7b5ccf5ec9e98cbc8776a1309 Mon Sep 17 00:00:00 2001 From: Autumn-27 <53844290+Autumn-27@users.noreply.github.com> Date: Tue, 8 Sep 2026 11:19:18 -0400 Subject: [PATCH] =?UTF-8?q?refactor(overview):=20graph=5Foverview=20?= =?UTF-8?q?=E4=B8=8D=E5=86=8D=E5=B9=B3=E9=93=BA=20hosts=EF=BC=8C=E6=96=B0?= =?UTF-8?q?=E5=A2=9E=20done=5Fintents=5Ftotal?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - coverage 块移除 hosts 列表(大范围任务里每轮携带最多 500 个 host 字符串, 对规划决策价值有限),只保留 host_count;具体主机按需 list_assets 查 - 新增顶层 done_intents_total:已结束意图(done/blocked/exhausted)总数,与 recent_done_intents(≤15 截断视图)平行命名,让 planner 去重时知道有无被截, key 即自描述、无需写进提示词 - db: 新增 ExplorationStore.CountFinishedIntents - 测试:blackboard 继承用例改用 host_count 断言,移除随之无用的 containsString --- agent/blackboard_inheritance_test.go | 24 ++++++------------------ agent/tools.go | 17 ++++++++--------- db/exploration.go | 12 ++++++++++++ 3 files changed, 26 insertions(+), 27 deletions(-) diff --git a/agent/blackboard_inheritance_test.go b/agent/blackboard_inheritance_test.go index 4549ff5..9f55359 100644 --- a/agent/blackboard_inheritance_test.go +++ b/agent/blackboard_inheritance_test.go @@ -103,9 +103,8 @@ func TestGraphOverviewExpandsAssociatedCompanyScope(t *testing.T) { if !ok || len(keywords) != 1 || keywords[0] != keyword { t.Fatalf("company keywords missing: %#v", scopeRows[0]["company_keywords"]) } - hosts, ok := coverage["hosts"].([]string) - if !ok || !containsString(hosts, domain) { - t.Fatalf("company asset host missing from agent context: %#v", coverage["hosts"]) + if hc, _ := coverage["host_count"].(int); hc < 1 { + t.Fatalf("company asset host not counted in agent context: %#v", coverage["host_count"]) } untested := callReadJSON(t, tools.listUntestedAssets(), `{"type":"root_domain","page":1,"page_size":10}`) if !strings.Contains(fmt.Sprint(untested), domain) { @@ -153,9 +152,8 @@ func TestGraphOverviewExpandsAssociatedCompanyScope(t *testing.T) { if !ok { t.Fatalf("coverage-disabled task lost asset context: %#v", disabledOverview["coverage"]) } - disabledHosts, ok := disabledCoverage["hosts"].([]string) - if !ok || !containsString(disabledHosts, domain) { - t.Fatalf("coverage-disabled task lost company asset host: %#v", disabledCoverage["hosts"]) + if hc, _ := disabledCoverage["host_count"].(int); hc < 1 { + t.Fatalf("coverage-disabled task lost company asset host count: %#v", disabledCoverage["host_count"]) } if _, exists := disabledCoverage["denominator"]; exists { t.Fatalf("coverage-disabled task unexpectedly exposed metrics: %#v", disabledCoverage) @@ -176,15 +174,6 @@ func TestGraphOverviewExpandsAssociatedCompanyScope(t *testing.T) { } } -func containsString(items []string, target string) bool { - for _, item := range items { - if item == target { - return true - } - } - return false -} - func companiesName(t *testing.T, companies *db.CompanyStore, companyID int64) string { t.Helper() company, err := companies.GetCompany(companyID) @@ -321,9 +310,8 @@ func TestBlackboardToolsReadDirectSources(t *testing.T) { if !ok { t.Fatalf("coverage missing from overview: %#v", overview["coverage"]) } - hosts, ok := coverage["hosts"].([]string) - if !ok || len(hosts) != 1 || hosts[0] != host || coverage["host_count"] != 1 { - t.Fatalf("inherited host context missing: %#v", coverage) + if coverage["host_count"] != 1 { + t.Fatalf("inherited host context missing (want host_count=1): %#v", coverage) } facts := callReadJSON(t, tools.listFacts(), `{}`).(map[string]any)["facts"].([]any) diff --git a/agent/tools.go b/agent/tools.go index c6894aa..2f1f0e5 100644 --- a/agent/tools.go +++ b/agent/tools.go @@ -412,6 +412,12 @@ func (t *ToolSet) graphOverviewData() map[string]any { } out["running_intents"] = compactIntents(running, parentsOf, yieldsOf) out["recent_done_intents"] = compactIntents(recentDone, parentsOf, yieldsOf) + // done_intents_total:已结束意图(done/blocked/exhausted)总数,与 recent_done_intents + // 平行命名——后者只是它的最新窗口(≤15)截断视图。两键并排即自描述:"看到的是 N/总数", + // 让 planner 去重时别把"没显示"当成"没派过",无需在提示词里另行解释。 + if dt, err := t.ts.CountFinishedIntents(); err == nil { + out["done_intents_total"] = dt + } out["frontier_open"] = len(fr) // findings (confirmed vulns) and facts (worker exploration results) are // now distinct node kinds. recent_facts surfaces fact summaries (esp. @@ -532,16 +538,9 @@ func (t *ToolSet) graphOverviewData() map[string]any { m["scope"] = scope } if hosts, err := t.as.HostsByTaskWithSources(t.taskID); err == nil { - const hostContextLimit = 500 - visible := hosts - if len(visible) > hostContextLimit { - visible = visible[:hostContextLimit] - } - m["hosts"] = visible + // 只给主机总数,不再把 host 列表平铺进 graph_overview(大范围任务里那是每轮 + // 都重复携带的大量字符串,对规划决策价值有限);具体主机按需 list_assets 查。 m["host_count"] = len(hosts) - if len(visible) < len(hosts) { - m["hosts_truncated"] = true - } } if len(m) > 0 { out["coverage"] = m diff --git a/db/exploration.go b/db/exploration.go index eb0e677..a4260c2 100644 --- a/db/exploration.go +++ b/db/exploration.go @@ -706,6 +706,18 @@ func (s *ExplorationStore) countByKindFiltered(kind, q string) (int, error) { return n, err } +// CountFinishedIntents counts this exploration's intents in a terminal explored +// state (done/blocked/exhausted) — graph_overview's done_intents_total, so the planner +// knows recent_done_intents (capped at a recent window) is a truncated view and +// stays cautious about "already tried" dedup. Excludes 'stopped' (killed/deleted), +// matching exactly what recent_done_intents surfaces. +func (s *ExplorationStore) CountFinishedIntents() (int, error) { + var n int + err := s.db.QueryRow(`SELECT COUNT(*) FROM exploration_nodes +WHERE exploration_id=$1 AND kind='intent' AND state IN ('done','blocked','exhausted')`, s.expID).Scan(&n) + return n, err +} + // GetNode returns one node of this exploration by id (nil, nil if not found). func (s *ExplorationStore) GetNode(id int64) (*Node, error) { n, err := scanNode(s.db.QueryRow(`SELECT `+nodeCols+` FROM exploration_nodes WHERE id=$1 AND exploration_id=$2`, id, s.expID))