feat: 提示词新增全局变量 DataDir + 触发合并/种子意图默认值调整

- prompt: 新增全局运行时变量 {{.DataDir}}(服务端数据根目录 s.m.dir),
  与 {{.Now}} 一样对所有 agent(planner/worker/mainagent/goals/chat 及自定义)可用;
  各 Vars 结构体加 DataDir 字段并在渲染时填入根 workDir,globalPromptVars 登记以便
  校验/UI 变量列表/预览识别(纯运行时变量,不入库,无需迁移)。
- trigger_merge_mode 默认 by_task→all(schema/config/后端兜底/前端初值同步)。
- seed_first_intent 默认 true→false(走标准先规划再执行);spawn_task 不再暴露该参数,
  工具建任务一律 false。
This commit is contained in:
Autumn-27 committed 2026-08-15 00:19:14 -04:00
1 parent 4c33317c6a
commit 80fcf528dc
18 files changed
+61 -43

No files matched your search

+2 -2
View File
@@ -363,9 +363,9 @@ type triggerBehavior struct {
}
// readTriggerBehavior loads an agent's策略, falling back to safe defaults
// (serial / by_task / 5) on any error or unknown enum value.
// (serial / all / 5) on any error or unknown enum value.
func (s *Server) readTriggerBehavior(agentKey string) triggerBehavior {
b := triggerBehavior{runMode: "serial", mergeMode: "by_task", maxParallel: 5}
b := triggerBehavior{runMode: "serial", mergeMode: "all", maxParallel: 5}
if s.m.pg == nil {
return b
}
+1 -1
View File
@@ -65,7 +65,7 @@ func (s *Server) createGoals(ctx context.Context, t *Task, emit func(db.Activity
as = s.m.Assets()
}
taskID, _ := strconv.ParseInt(t.ID, 10, 64)
for _, g := range agent.DecomposeGoals(ctx, cfg, t.Goal, t.Description, as, taskID, emit) {
for _, g := range agent.DecomposeGoals(ctx, cfg, s.m.dir, t.Goal, t.Description, as, taskID, emit) {
if strings.TrimSpace(g.Text) != "" {
specs = append(specs, goalSpec{Text: g.Text, VulnClass: g.VulnClass})
}
+3 -4
View File
@@ -240,7 +240,6 @@ func (s *Server) toolSpawnTask() actool.CoreTool {
"llm_profile_id": map[string]any{"type": "integer", "description": "可选:指定本子任务 planner/worker 用的 LLM 配置 id(见 list_llm_profiles);留空则继承父任务、再回退全局激活配置"},
"timeout_seconds": map[string]any{"type": "integer", "description": "可选:任务级超时(秒)。到点后触发优雅收尾并进入 timeout 终态;留空或 0 = 不限时"},
"plan_heartbeat_seconds": map[string]any{"type": "integer", "description": "可选:planner 心跳触发间隔(秒)。距上轮规划结束/任务开始满该值且期间无触发 → 触发一轮规划(兜底死锁 + 唤醒去监督飞行中的 worker)。留空或 0 = 默认 600(10min);"},
"seed_first_intent": map[string]any{"type": "boolean", "description": "可选:默认 true。true=创建即把「描述+目标」作为一条种子意图下发,worker 免等首轮 planner 直接开跑(CTF 等常一个 work 解决的场景推荐);false=走标准的先规划再执行"},
}, "description", "goal"),
func(_ context.Context, in json.RawMessage) (actool.Result, error) {
var a struct {
@@ -248,7 +247,6 @@ func (s *Server) toolSpawnTask() actool.CoreTool {
LLMProfileID json.RawMessage `json:"llm_profile_id"`
TimeoutSeconds int `json:"timeout_seconds"`
PlanHeartbeatSeconds int `json:"plan_heartbeat_seconds"`
SeedFirstIntent *bool `json:"seed_first_intent"`
}
_ = json.Unmarshal(in, &a)
if strings.TrimSpace(a.Description) == "" {
@@ -283,8 +281,9 @@ func (s *Server) toolSpawnTask() actool.CoreTool {
}
}
// 共享的建后流程,与 HTTP 建任务(server.go createTask)复用同一段 launchTask:
// seed + 种子意图 + 后台可见地做目标分解(第0轮/LLM步骤/逐条goal) + engine.Run。
s.launchTask(t, a.Description+" "+a.Goal, a.SeedFirstIntent == nil || *a.SeedFirstIntent)
// seed + 后台可见地做目标分解(第0轮/LLM步骤/逐条goal) + engine.Run。
// spawn_task 不暴露 seed_first_intent:工具创建的任务一律走标准先规划再执行(false)。
s.launchTask(t, a.Description+" "+a.Goal, false)
return actool.Text(fmt.Sprintf("task created: %s", t.ID)), nil
})
}
+2 -2
View File
@@ -1018,7 +1018,7 @@ type createTaskReq struct {
LLMProfileID *int64 `json:"llm_profile_id,omitempty"` // 指定运行本任务的 LLM 配置;省略/null=用激活配置
TimeoutSeconds int `json:"timeout_seconds"` // 任务级超时(秒);0/省略=不限时
PlanHeartbeatSeconds int `json:"plan_heartbeat_seconds"` // planner 心跳触发间隔(秒);0/省略=默认600(10min);下限=默认=600,低于自动抬到600
SeedFirstIntent *bool `json:"seed_first_intent,omitempty"` // 创建时直接下发一条种子意图(内容=描述+目标),让 worker 免等首轮 planner 直接开跑;省略/null=默认开启。CTF 常一 work 解决,省掉开跑前的 planner 轮。
SeedFirstIntent *bool `json:"seed_first_intent,omitempty"` // 创建时直接下发一条种子意图(内容=描述+目标),让 worker 免等首轮 planner 直接开跑;省略/null=默认关闭,走标准先规划再执行。显式传 true 才开(CTF 常一 work 解决时可省掉开跑前的 planner 轮)。
}
func (s *Server) createTask(w http.ResponseWriter, r *http.Request) {
@@ -1049,7 +1049,7 @@ func (s *Server) createTask(w http.ResponseWriter, r *http.Request) {
log.Printf("[task] 新建任务 #%s «%s» 目标: %s", t.ID, req.Description, req.Goal)
// 共享的建后流程(seed + 种子意图 + 后台目标分解 + engine.Run),与 spawn_task 复用同一段。
// launchTask 内部异步,不阻塞 UI —— 目标分解在后台可见地进行。
s.launchTask(t, req.Description+" "+req.Goal, req.SeedFirstIntent == nil || *req.SeedFirstIntent)
s.launchTask(t, req.Description+" "+req.Goal, req.SeedFirstIntent != nil && *req.SeedFirstIntent)
writeJSON(w, 201, t)
}
+1
View File
@@ -1749,6 +1749,7 @@ func (s *Server) pgListModels(w http.ResponseWriter, r *http.Request) {
// {{.Now}} — e.g. subtract it from a fixed start stamp to reason about elapsed time.
var globalPromptVars = []db.PromptVar{
{Name: "Now", Description: "服务端当前时间(每次运行实时刷新;可与固定起始时间相减判断已用时长)", Example: "2026-08-11 14:30:00 CST", Source: "runtime"},
{Name: "DataDir", Description: "服务端数据根目录(所有任务/会话产物的根;各 agent 实际写盘在其下的子目录,如 <DataDir>/<taskID>)", Example: "/app/data", Source: "runtime"},
}
// withGlobalVars appends the universal runtime vars onto an agent's own catalog,