diff --git a/DEPTH_ROADMAP.md b/DEPTH_ROADMAP.md index fcf3b4e..bf4b7cc 100644 --- a/DEPTH_ROADMAP.md +++ b/DEPTH_ROADMAP.md @@ -25,13 +25,15 @@ - **验收 ✅ live(deepseek)**:跑题→0.40 poor→**触发纠偏(0.40→0.55)**;截断→0.55 warn;好答案→1.00 ok。 校准前这三个全是 1.00→ok,恒温器从没触发。 -### [ ] T0.2 多智能体进 Studio(多智能体 v2) +### [x] T0.2 多智能体进 Studio(多智能体 v2)✅ 现状:coordinator 后端已通(见 MULTI_AGENT.md),但画布拖不出。 -- [ ] nodeCatalog 加 `coordinator` 节点 + 新 `agentList` 字段(子智能体卡片:名/用途/系统提示词/工具多选) -- [ ] StudioView 渲染+编辑 agentList,校验(≥1 专家、名唯一) -- [ ] 运行轨迹:专家派发渲染成可展开子节点(显示 brief + 精炼结论) -- [ ] preview/真机验证:UI 搭两专家协调任务跑通 -- 验收:用户能在 Studio 拖出协调者图并运行,轨迹看到并行派发 + 综合。 +- [x] nodeCatalog 加 `coordinator` 节点(「多智能体协调」,indigo)+ 新 `agentList` 字段类型 + Specialist 接口 +- [x] Inspector `AgentListField`:可增删的专家卡片(名/用途/系统提示词/工具逗号分隔) +- [x] dsl 校验:agentList 需 ≥1 个有名字的专家 + 名字不重复 +- [x] 运行轨迹「工具调用」面板纳入专家派发(kind=agent)→ 改名「工具/专家」,专家项 indigo 标识 +- [x] 后端验证:UI 同款 DSL 结构提交可跑(agents 数组经 exportDsl 透传 → parseSpecialists) +- 验收 ✅:Studio 左栏出现「多智能体协调」节点,检查器配专家卡片,导出 DSL 对齐后端; + 轨迹/工具面板能看到专家派发。(真机由用户在 wails 窗口确认) --- @@ -89,7 +91,7 @@ | Tier | 完成 / 总 | |---|---| -| T0 激活 | 1 / 2 | +| T0 激活 | 2 / 2 ✅ | | T1 收口 | 0 / 2 | | T2 深化 | 0 / 3 | | T3 硬化 ⏸ | 0 / 5 | diff --git a/sundynix-desktop/frontend/src/lib/dsl.ts b/sundynix-desktop/frontend/src/lib/dsl.ts index 6667044..99ff23f 100644 --- a/sundynix-desktop/frontend/src/lib/dsl.ts +++ b/sundynix-desktop/frontend/src/lib/dsl.ts @@ -52,6 +52,16 @@ export function validate(nodes: Node[], edges: Edge[]): Issue[] { .filter((f) => f.required) .forEach((f) => { const v = d.config?.[f.key]; + if (f.type === "agentList") { + const arr = Array.isArray(v) ? (v as Array<{ name?: string }>) : []; + const names = arr.map((s) => s?.name?.trim()).filter(Boolean) as string[]; + if (names.length === 0) { + issues.push({ level: "warn", msg: `节点「${d.label || k.label}」至少需要一个有名字的专家` }); + } else if (new Set(names).size !== names.length) { + issues.push({ level: "warn", msg: `节点「${d.label || k.label}」专家名有重复` }); + } + return; + } if (v === undefined || v === "" || String(v).startsWith("(未")) { issues.push({ level: "warn", msg: `节点「${d.label || k.label}」缺必填项:${f.label}` }); } diff --git a/sundynix-desktop/frontend/src/studio/Inspector.tsx b/sundynix-desktop/frontend/src/studio/Inspector.tsx index 2763279..327b9b4 100644 --- a/sundynix-desktop/frontend/src/studio/Inspector.tsx +++ b/sundynix-desktop/frontend/src/studio/Inspector.tsx @@ -1,8 +1,52 @@ import type { Node } from "@xyflow/react"; -import { NODE_KINDS } from "./nodeCatalog"; +import { NODE_KINDS, type Specialist } from "./nodeCatalog"; const inputCls = "mt-1 w-full rounded-md border border-line bg-ink-800 px-2 py-1 text-sm text-slate-200 focus:border-violet-500/60 focus:outline-none"; +const cardCls = + "w-full rounded border border-line bg-ink-800 px-2 py-1 text-[12px] text-slate-200 placeholder:text-slate-600 focus:border-indigo-500/60 focus:outline-none"; + +// AgentListField:多智能体协调节点的「子智能体(专家)」配置 —— 可增删的专家卡片。 +// 每张卡片对齐后端 parseSpecialists 的 {name, use, system, tools}。 +function AgentListField({ value, onChange }: { value: Specialist[]; onChange: (next: Specialist[]) => void }) { + const list = Array.isArray(value) ? value : []; + const update = (i: number, patch: Partial) => onChange(list.map((s, idx) => (idx === i ? { ...s, ...patch } : s))); + const add = () => onChange([...list, { name: "", use: "", system: "", tools: [] }]); + const remove = (i: number) => onChange(list.filter((_, idx) => idx !== i)); + return ( +
+ {list.map((s, i) => ( +
+
+ update(i, { name: e.target.value })} /> + +
+ update(i, { use: e.target.value })} /> +