feat: goal_met 默认不绑任何 agent + 去掉 planner 相关提示词
- goal_met 绕过逐个 prove_goal 直接宣布整个任务完成,权重大、有误判风险,又与 「prove_goal 标记最后一个目标→自动收官」重复,故默认不给任何 agent(仍入目录、 web 可见、可手动按需绑)。 - toolcatalog 新增 defaultUnbound 集合:goal_met seed 时绑定强制为空 [](保留在 PlannerTools base 里,手动绑回后运行时仍可用)。 - db 新增 RemoveAgentFromTool(agentKey,toolKey);server 新增一次性迁移 unbindGoalMetDefault(flag goal_met_unbind_default_v1)剥掉旧库 goal_met 的 planner 绑定,只剥 planner、不动用户手动加的其它绑定,flag 守卫故重启不重复。 - planner 提示词去掉两处 goal_met 引导(system 模板 + 每轮 input 结尾),保留 prove_goal 与自动收官说明。任务收官不受影响(prove_goal 自动置 GoalMet)。
This commit is contained in:
1 parent
8e03043548
commit
bf2410baee
4 files changed
+44
-5
No files matched your search
+2
-2
@@ -210,7 +210,7 @@ const plannerDefaultTmpl = `你是一个授权渗透测试系统的"规划者"
|
||||
- open_intents / running_intents / recent_done_intents——"哪些方向已经有意图在覆盖/已尝试"。每个意图还带 **parents(上游:它派生自哪些事实/意图)和 yields(下游:它产生了哪些事实/发现)**——这就是探索图的**血缘关系**,据此理解"哪些事实来自哪个方向、能否综合成新方向"。recent_facts 里每个事实带 **from_intent**(由哪个意图产生)。
|
||||
- sites_without_endpoints / findings——"哪些方向【可能】需要探索"。
|
||||
- 只有需要某一片的细节时,才**按需**调 list_assets(pull 模式:可用 q 关键字搜索,可叠加 type/company_id/task_id 过滤,分页 limit/offset;或用 id/ids 直接取)、asset_neighbors、list_findings。资产图全局共享,别默认拉全量。资产中可能包含非本次任务涉及到的资产,所以需要主要出现非本次任务相关的资产时忽略这些资产。
|
||||
2. 判目标(核心职责):graph_overview 的 goals 字段已含目标与状态;对已被某发现/事实证明的未达成目标,调 prove_goal(goal_id,evidence_id,reason) 标记 met。**当你用 prove_goal 标记的这一个恰好是最后一个未完成目标时,系统会自动判定整个任务完成、无需你再调 goal_met**。goal_met 仅在你想【绕过逐个 prove_goal、直接从全局判定任务已达成】时才用。
|
||||
2. 判目标(核心职责):graph_overview 的 goals 字段已含目标与状态;对已被某发现/事实证明的未达成目标,调 prove_goal(goal_id,evidence_id,reason) 标记 met。**当你用 prove_goal 标记的这一个恰好是最后一个未完成目标时,系统会自动判定整个任务完成**——收官完全由逐个 prove_goal 驱动,你无需、也没有别的“一键完成”手段。
|
||||
2.5. **(可选,仅限开局、极轻量)探测理解**:你具备 Bash 等执行能力,它的**唯一正当用途**是——当**图里几乎还没有事实**(recent_facts 基本为空、任务刚开始)、仅凭态势无法把初始意图描述具体时,对目标做**极少量、只读**的探测(如 1–2 次 curl 看首页/指纹),据此产出更精准的**初始意图**。
|
||||
⚠️ **牢记你的身份边界:你是"规划者",不是"执行者"。你在这里做的一切都只为【生成/说清意图】,绝不是【在 plan 里把活干了】。** 探测的**唯一合法产物是一句更精准的意图描述**——绝不能是漏洞的发现、验证、利用,也不能是端点/目录/参数的枚举结果。任何"我顺手把这个也测了/确认了"的念头都是越界:那是 worker 在 work 阶段该做的事,你只需把它**写成一个意图派下去**。
|
||||
**三条硬性边界,务必守住**:
|
||||
@@ -328,7 +328,7 @@ func (p *Planner) Plan(ctx context.Context, taskID int64, as *db.AssetStore, ts
|
||||
if len(triggers) == 0 {
|
||||
lead = "本轮是**定时巡检(心跳到点)/无具体变动信号**的唤醒——图不一定有新变动。顺带复查在跑意图:长时间无进展或跑偏的用 steer_work 纠偏、方向整个错的用 kill_work 止损;再判定目标、决定是否补方向:"
|
||||
}
|
||||
input := lead + situational + "\n\n据上面的态势,判定目标。**本轮若无未被覆盖的新方向,直接结束即可(生成 0 个意图是正常且常见的,尤其刚派完意图在等 worker 产出时)。绝不要为了“结束本轮”去调 goal_met——goal_met 会【立即结束整个任务】,只在你确认目标已【真正达成】(已拿到目标成果/已确认目标漏洞)时才调;未达成就用 prove_goal 逐个标记、或什么都不调直接结束。**" +
|
||||
input := lead + situational + "\n\n据上面的态势,判定目标。**本轮若无未被覆盖的新方向,直接结束即可(生成 0 个意图是正常且常见的,尤其刚派完意图在等 worker 产出时)。目标已【真正达成】(已拿到目标成果/已确认目标漏洞)时用 prove_goal 逐个标记;未达成就什么都不调、直接结束本轮。**" +
|
||||
renderPlannerTodos(opts.Todos.List())
|
||||
// 有 deadline 夹逼时加硬 ctx 兜底(软预算 + grace),防单轮卡死绕过轮边界软超时。
|
||||
runCtx := ctx
|
||||
|
||||
+16
-3
@@ -49,8 +49,17 @@ func builtinToolsByAgent() map[string][]actool.CoreTool {
|
||||
}
|
||||
}
|
||||
|
||||
// BuiltinToolSeeds 把三个 agent 的内置工具集去重合并成 seed 列表:同名工具(如
|
||||
// list_assets 三者都有)合成一条,Agents 取并集。顺序稳定(mainagent→planner→worker)。
|
||||
// defaultUnbound:这些 system 工具会照常入目录(web 端可见、可手动按 agent 勾选),但
|
||||
// 默认【不绑任何 agent】——ToolResolve 对空绑定的工具对所有 agent 一律丢弃,须显式 opt-in。
|
||||
// 之所以仍留在某个 agent 的 base 工具集里(如 goal_met 在 PlannerTools):一是让 seed 能
|
||||
// 构造它拿到 desc/schema,二是用户手动绑回后运行时 base 里有它、ToolResolve 才留得住。
|
||||
//
|
||||
// goal_met:绕过逐个 prove_goal、直接从全局宣布【整个任务完成】,权重大且有误判风险,又与
|
||||
// 「prove_goal 标记最后一个目标 → 自动收官」重复,故默认不给任何 agent,需要时再手动绑。
|
||||
var defaultUnbound = map[string]bool{"goal_met": true}
|
||||
|
||||
// BuiltinToolSeeds 把各 agent 的内置工具集去重合并成 seed 列表:同名工具(如 list_assets
|
||||
// 多个 agent 都有)合成一条,Agents 取并集;defaultUnbound 里的工具则强制绑定为空。
|
||||
func BuiltinToolSeeds() []ToolSeed {
|
||||
byAgent := builtinToolsByAgent()
|
||||
order := []string{"mainagent", "goals", "planner", "worker", "auto", "pentest"}
|
||||
@@ -76,11 +85,15 @@ func BuiltinToolSeeds() []ToolSeed {
|
||||
out := make([]ToolSeed, 0, len(keys))
|
||||
for _, k := range keys {
|
||||
a := m[k]
|
||||
agents := a.agents
|
||||
if defaultUnbound[k] {
|
||||
agents = []string{} // 入目录、可手动绑,但默认不给任何 agent(存 [] 而非 null,与其它工具一致)
|
||||
}
|
||||
out = append(out, ToolSeed{
|
||||
Key: k,
|
||||
Desc: a.tool.Description(),
|
||||
Schema: a.tool.InputSchema(),
|
||||
Agents: a.agents,
|
||||
Agents: agents,
|
||||
})
|
||||
}
|
||||
return out
|
||||
|
||||
@@ -61,6 +61,14 @@ func (d *DB) RemoveAgentFromToolBindings(agentKey string) error {
|
||||
return err
|
||||
}
|
||||
|
||||
// RemoveAgentFromTool strips one agent key from a SINGLE tool's `agents` array —
|
||||
// used by one-time migrations that change a tool's default binding on existing DBs
|
||||
// (SeedTool is first-insert-only, so a changed default never reaches a seeded row).
|
||||
func (d *DB) RemoveAgentFromTool(agentKey, toolKey string) error {
|
||||
_, err := d.Exec(`UPDATE tools SET agents = agents - $1 WHERE key=$2 AND agents ? $1`, agentKey, toolKey)
|
||||
return err
|
||||
}
|
||||
|
||||
// UpsertToolForce overwrites a tool row with the given code-default values (used by
|
||||
// the per-tool "reset" action). It resets description/schema/agents and re-enables
|
||||
// the tool, but keeps system=true.
|
||||
|
||||
@@ -397,6 +397,7 @@ func (s *Server) seedOrchestrationTools() {
|
||||
s.seedAutoDefaultBindings()
|
||||
s.seedPlannerDefaultBindings()
|
||||
s.seedAutoReportFindingBinding()
|
||||
s.unbindGoalMetDefault()
|
||||
// 注:pentest 的默认工具绑定无需迁移——BuiltinToolSeeds 在全新初始化时就把
|
||||
// list_assets/insert_assets/report_finding/list_findings/list_companies 连同
|
||||
// pentest 一起 seed 好了(项目尚无旧库,不做迁移)。
|
||||
@@ -434,6 +435,23 @@ func (s *Server) refreshBuiltinToolSchemas() {
|
||||
log.Printf("[tools] 已刷新 orchestration/platform 工具 schema 到代码默认(一次性)")
|
||||
}
|
||||
|
||||
// unbindGoalMetDefault removes goal_met's default "planner" binding ONCE (guarded by
|
||||
// a settings flag), so existing DBs match the new default of NO agent. goal_met bypasses
|
||||
// per-goal prove_goal to declare the whole task done — powerful/risky and redundant with
|
||||
// the prove_goal→auto-complete path — so it ships unbound; users can re-bind it per agent
|
||||
// in the UI. A user's own binding to another agent is untouched (we only strip planner).
|
||||
func (s *Server) unbindGoalMetDefault() {
|
||||
const flag = "goal_met_unbind_default_v1"
|
||||
if v, _, _ := s.m.pg.GetSetting(flag); v == "true" {
|
||||
return
|
||||
}
|
||||
if err := s.m.pg.RemoveAgentFromTool("planner", "goal_met"); err != nil {
|
||||
log.Printf("[tools] goal_met 解绑 planner 失败: %v", err)
|
||||
return
|
||||
}
|
||||
_ = s.m.pg.SetSetting(flag, "true")
|
||||
}
|
||||
|
||||
// seedAutoReportFindingBinding adds "auto" to report_finding's binding ONCE so
|
||||
// conversation-context agents can call it without requiring an intent_id.
|
||||
func (s *Server) seedAutoReportFindingBinding() {
|
||||
|
||||
Reference in new issue
Block a user