feat(constraints): 操作约束抽取→注入 planner/worker 提示→总览管理
- 新表 task_constraints(allow/deny),CREATE TABLE IF NOT EXISTS 无需迁移 - 新工具 set_constraints:目标拆解器 round-0 先抽约束再拆目标、主 agent 运行时可补; 拆解器提示词要求约束自包含(把「当前目标」等指代替换成具体域名/端口) - constraintBlock 按最高优先级注入 planner+worker 系统提示;去偏:多样性/拓面显式服从约束 - 注入范围可配置(constraints_inject_planner/worker,默认都开,每轮读、即时生效) - 总览「约束管理」卡片 + CRUD 接口(增删改不通知 planner,下轮规划自然读到) - goals 提示词把抽约束步并入可编辑正文;旧库经 flag 守卫的一次性 reseed 追加新默认版本
This commit is contained in:
1 parent
d8f34cc79d
commit
27c3248844
19 files changed
+820
-15
No files matched your search
@@ -0,0 +1,177 @@
|
||||
package server
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/Autumn-27/artex/db"
|
||||
)
|
||||
|
||||
// 总览「约束管理」的人工 CRUD 接口 + 注入范围开关的解析。操作约束(allow/deny)与 agent 侧的
|
||||
// set_constraints 工具写同一张 task_constraints 表;这里是人类在 UI 上直接增删改。约束仅是
|
||||
// 提示上下文——增删改后【不】通知 planner,下一轮规划自然读库生效(按产品决策)。每个变更 handler
|
||||
// 都走 beginTaskOperation/decInflight,避免与任务删除竞态(与目标/意图 CRUD 一致)。
|
||||
|
||||
// 注入范围开关的 settings key,默认都开(GetBool 第二参数 = true)。
|
||||
const (
|
||||
settingConstraintsInjectPlanner = "constraints_inject_planner"
|
||||
settingConstraintsInjectWorker = "constraints_inject_worker"
|
||||
)
|
||||
|
||||
// constraintInjectPlanner / constraintInjectWorker 报告是否把操作约束注入对应 agent 的
|
||||
// 系统提示(默认开)。作为 resolver 传给 planner/worker,每轮读 → 改开关即时生效。
|
||||
func (s *Server) constraintInjectPlanner() bool {
|
||||
return s.m.pg.GetBool(settingConstraintsInjectPlanner, true)
|
||||
}
|
||||
|
||||
func (s *Server) constraintInjectWorker() bool {
|
||||
return s.m.pg.GetBool(settingConstraintsInjectWorker, true)
|
||||
}
|
||||
|
||||
// listConstraints 返回本任务的全部操作约束(allow 在前、deny 在后)。
|
||||
func (s *Server) listConstraints(w http.ResponseWriter, r *http.Request) {
|
||||
t, ok := s.m.Task(r.PathValue("id"))
|
||||
if !ok {
|
||||
writeErr(w, 404, "task not found")
|
||||
return
|
||||
}
|
||||
rows, err := t.Store.ListConstraints()
|
||||
if err != nil {
|
||||
writeErr(w, 500, err.Error())
|
||||
return
|
||||
}
|
||||
writeJSON(w, 200, map[string]any{"constraints": constraintDTOs(rows)})
|
||||
}
|
||||
|
||||
// addConstraint 人工新增一条操作约束(kind=allow|deny)。不通知 planner。
|
||||
func (s *Server) addConstraint(w http.ResponseWriter, r *http.Request) {
|
||||
t, ok := s.m.Task(r.PathValue("id"))
|
||||
if !ok {
|
||||
writeErr(w, 404, "task not found")
|
||||
return
|
||||
}
|
||||
if !s.engine.beginTaskOperation(t.ID) {
|
||||
writeErr(w, 409, "任务正在删除,无法新增约束")
|
||||
return
|
||||
}
|
||||
defer s.engine.decInflight(t.ID)
|
||||
|
||||
var body struct {
|
||||
Text string `json:"text"`
|
||||
Kind string `json:"kind"`
|
||||
}
|
||||
if err := json.NewDecoder(r.Body).Decode(&body); err != nil {
|
||||
writeErr(w, 400, "invalid JSON")
|
||||
return
|
||||
}
|
||||
text := strings.TrimSpace(body.Text)
|
||||
if text == "" {
|
||||
writeErr(w, 400, "约束内容不能为空")
|
||||
return
|
||||
}
|
||||
kind := normalizeConstraintKind(body.Kind)
|
||||
if kind == "" {
|
||||
writeErr(w, 400, "kind 必须是 allow 或 deny")
|
||||
return
|
||||
}
|
||||
id, err := t.Store.AddConstraint(kind, text, "human")
|
||||
if err != nil {
|
||||
writeErr(w, 500, err.Error())
|
||||
return
|
||||
}
|
||||
writeJSON(w, 200, ConstraintDTO{ID: strconv.FormatInt(id, 10), Kind: kind, Text: text, Origin: "human"})
|
||||
}
|
||||
|
||||
// editConstraint 人工修改一条约束(kind + text)。不通知 planner。
|
||||
func (s *Server) editConstraint(w http.ResponseWriter, r *http.Request) {
|
||||
t, ok := s.m.Task(r.PathValue("id"))
|
||||
if !ok {
|
||||
writeErr(w, 404, "task not found")
|
||||
return
|
||||
}
|
||||
if !s.engine.beginTaskOperation(t.ID) {
|
||||
writeErr(w, 409, "任务正在删除,无法修改约束")
|
||||
return
|
||||
}
|
||||
defer s.engine.decInflight(t.ID)
|
||||
|
||||
cid, err := strconv.ParseInt(r.PathValue("cid"), 10, 64)
|
||||
if err != nil || cid <= 0 {
|
||||
writeErr(w, 400, "bad constraint id")
|
||||
return
|
||||
}
|
||||
var body struct {
|
||||
Text string `json:"text"`
|
||||
Kind string `json:"kind"`
|
||||
}
|
||||
if err := json.NewDecoder(r.Body).Decode(&body); err != nil {
|
||||
writeErr(w, 400, "invalid JSON")
|
||||
return
|
||||
}
|
||||
text := strings.TrimSpace(body.Text)
|
||||
if text == "" {
|
||||
writeErr(w, 400, "约束内容不能为空")
|
||||
return
|
||||
}
|
||||
kind := normalizeConstraintKind(body.Kind)
|
||||
if kind == "" {
|
||||
writeErr(w, 400, "kind 必须是 allow 或 deny")
|
||||
return
|
||||
}
|
||||
if err := t.Store.UpdateConstraint(cid, kind, text); err != nil {
|
||||
writeErr(w, 500, err.Error())
|
||||
return
|
||||
}
|
||||
writeJSON(w, 200, ConstraintDTO{ID: strconv.FormatInt(cid, 10), Kind: kind, Text: text})
|
||||
}
|
||||
|
||||
// deleteConstraint 人工删除一条约束。不通知 planner。
|
||||
func (s *Server) deleteConstraint(w http.ResponseWriter, r *http.Request) {
|
||||
t, ok := s.m.Task(r.PathValue("id"))
|
||||
if !ok {
|
||||
writeErr(w, 404, "task not found")
|
||||
return
|
||||
}
|
||||
if !s.engine.beginTaskOperation(t.ID) {
|
||||
writeErr(w, 409, "任务正在删除,无法删除约束")
|
||||
return
|
||||
}
|
||||
defer s.engine.decInflight(t.ID)
|
||||
|
||||
cid, err := strconv.ParseInt(r.PathValue("cid"), 10, 64)
|
||||
if err != nil || cid <= 0 {
|
||||
writeErr(w, 400, "bad constraint id")
|
||||
return
|
||||
}
|
||||
if err := t.Store.DeleteConstraint(cid); err != nil {
|
||||
writeErr(w, 500, err.Error())
|
||||
return
|
||||
}
|
||||
writeJSON(w, 200, map[string]bool{"ok": true})
|
||||
}
|
||||
|
||||
// normalizeConstraintKind lowercases + validates the kind; "" on invalid.
|
||||
func normalizeConstraintKind(k string) string {
|
||||
k = strings.TrimSpace(strings.ToLower(k))
|
||||
if k == "allow" || k == "deny" {
|
||||
return k
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// constraintDTOs converts db rows to the frontend shape.
|
||||
func constraintDTOs(in []db.Constraint) []ConstraintDTO {
|
||||
out := make([]ConstraintDTO, 0, len(in))
|
||||
for _, c := range in {
|
||||
out = append(out, ConstraintDTO{
|
||||
ID: strconv.FormatInt(c.ID, 10),
|
||||
Kind: c.Kind,
|
||||
Text: c.Text,
|
||||
Origin: c.Origin,
|
||||
TS: rfc3339(c.CreatedAt),
|
||||
})
|
||||
}
|
||||
return out
|
||||
}
|
||||
Reference in new issue
Block a user