云端 Agent 此前只能新建节点与连线,既不会排版也不能移动已有节点:模型只能用几十个
update_node 手算坐标,或者让用户自己拖。
- 新增 backend/internal/canvas/layout:把前端既有的画布布局算法(web/src/lib/canvas/
canvas-layout.ts,本 PR 不改该文件)按同一套常量移植到服务端,提供 Kahn 拓扑分层 flow、
媒体泳道 byType、row/column/grid、对齐与等距、分组分带、AABB 重叠检测与空位搜索。
- 新增写工具 canvas_arrange_nodes:snapshotHash 必需,mode∈{auto,flow,byType,row,column,
grid},可传 nodeIds、groups(只做横向位置分带,不建容器、不改层级)、align、gap、dryRun。
只改坐标,不改内容、不建连线、不增删节点;一次最多 50 个节点 / 12 组。锁定节点、背板等
容器、隐藏批次子节点与已归属背板的子节点保持不动并在回执里说明;整批复用现有 mutation
记录与事件管线,审批与撤销走同一条路径。
- add_node 省略坐标时不再落到 (0,0):优先贴合本批连线的对端(输入在左、输出在右),否则
追加到同泳道同类节点之后,冲突则扫描空位;只给一个轴时保留该轴。agentCanvasOp.X/Y 改为
指针以区分"没给坐标"与"坐标 0"。
- update_node 的 patch 新增 x/y(PatchField.Limit 校验 ±1e6),供模型微调单个节点。
- 系统行为策略新增「布局与整理」一节并升版到 8;docs 补工具说明与工具全集;前端同步审批
预览联合类型、面板文案与徽标。
- 工具 schema 预算按实测从 24000 上调到 25000(21 个工具 24788 字节),并改正注释里
"上游约 23.4 KB"的说法(上游 20 个工具实测 22777 字节)。
63 lines
2.5 KiB
Go
63 lines
2.5 KiB
Go
package app
|
||
|
||
import (
|
||
"encoding/json"
|
||
"reflect"
|
||
"testing"
|
||
)
|
||
|
||
// 工具 schema 是每一步都要发出去(并且是前缀缓存的第一段)的固定开销,因此值得钉住体积:
|
||
// 平台工具全集按 auto + canvas + 技能构造(覆盖条件暴露的工具)。
|
||
func platformToolSchema(t *testing.T) ([]map[string]any, []byte) {
|
||
t.Helper()
|
||
req := CloudAgentRequest{PermissionMode: "auto", ContextScope: []string{"canvas"}, SkillIDs: []string{"capability-list"}}
|
||
req.Budget.MaxGenerationTasks = 1
|
||
tools := cloudAgentTools(req)
|
||
raw, err := json.Marshal(tools)
|
||
if err != nil {
|
||
t.Fatal(err)
|
||
}
|
||
return tools, raw
|
||
}
|
||
|
||
func TestCloudAgentToolSchemaStaysCompact(t *testing.T) {
|
||
tools, raw := platformToolSchema(t)
|
||
t.Logf("platform tool schema: %d tools, %d bytes", len(tools), len(raw))
|
||
|
||
// 条件必填不是 type 枚举能表达的约束,压缩描述不能删掉协议语义。
|
||
found := false
|
||
for _, tool := range tools {
|
||
function, _ := tool["function"].(map[string]any)
|
||
if function["name"] != "canvas_apply_ops" {
|
||
continue
|
||
}
|
||
found = true
|
||
parameters, _ := function["parameters"].(map[string]any)
|
||
ops, _ := parameters["properties"].(map[string]any)
|
||
items, _ := ops["ops"].(map[string]any)
|
||
opItem, _ := items["items"].(map[string]any)
|
||
want := []map[string]any{
|
||
{"properties": map[string]any{"type": map[string]any{"const": "add_node"}}, "required": []string{"nodeType"}},
|
||
{"properties": map[string]any{"type": map[string]any{"const": "update_node"}}, "required": []string{"patch"}},
|
||
{"properties": map[string]any{"type": map[string]any{"const": "connect_nodes"}}, "required": []string{"fromNodeId", "toNodeId"}},
|
||
}
|
||
if !reflect.DeepEqual(opItem["oneOf"], want) {
|
||
t.Fatalf("操作类型条件必填约束丢失: %#v", opItem["oneOf"])
|
||
}
|
||
if props, ok := opItem["properties"].(map[string]any); !ok || len(props) == 0 {
|
||
t.Fatal("canvas_apply_ops 的 ops.items 必须保留 properties")
|
||
}
|
||
}
|
||
if !found {
|
||
t.Fatal("canvas_apply_ops 未暴露")
|
||
}
|
||
|
||
// 体积预算:上游 20 个工具实测 22,777 字节(预算 24000);本 PR 新增 canvas_arrange_nodes
|
||
// 并把描述、patch 字段补齐后实测 21 个工具 24,788 字节,因此把预算显式上调到 25000
|
||
// (约 0.9% 余量)。新增工具或字段时请重新测量并有意识地调整这个数字,而不是让 schema
|
||
// 悄悄膨胀(它每一步都要发、还在前缀最前面)。
|
||
if len(raw) > 25000 {
|
||
t.Fatalf("平台工具 schema 体积 %d 字节超出预算 25000:请压缩描述或显式调整预算", len(raw))
|
||
}
|
||
}
|