feat(orchestration): Phase2 —— map 真并行 fan-out + branch 真/假边标签精确选路
Phase1 让引擎按图执行后,本轮补上两块:
1) map 并行 fan-out(dispatcher)
- map 节点:planItems 把主题拆成 3–6 子项 → 复用 report 的 writeSections
有界并发(4)逐项撰写 → 结构化 sections 存黑板 + 拼进 answer + 流式呈现。
- 检索节点记下 owner 作用域库名(b.kb),供 map 各项并行检索复用。
- render 节点优先用 map 产出的多章 sections 渲染,否则整段成稿当单章。
2) branch 真/假边标签(前端 + DSL + dispatcher)
- TypedNode:分支节点渲染两个出口手柄 真(绿)/假(红),连线各带 sourceHandle。
- exportDsl / TaskDsl:边导出携带 sourceHandle。
- dispatcher dsl.Edge 增 SourceHandle;branchNode 优先按 true/false 标签精确
选路,无标签的旧图退回"出边顺序"约定,向后兼容。
实测(gateway+dispatcher+DeepSeek 真跑):
- map:input→map→render,DeepSeek 拆出 5 章并行撰写(347–512字),trace 见
section:0..4 并发 + 有界并发(section3 等槽);render 因 mcp-go 不在优雅降级 ✓
- branch 标签:把 true 边故意列第二位,条件真仍走 true 标签的分支A、假走分支B,
证明按标签而非边序选路 ✓
- 桌面端:分支节点正确渲染 真/假 两手柄,无 console 报错 ✓
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -5,7 +5,8 @@ import { NODE_KINDS } from "../studio/nodeCatalog";
|
||||
export interface TaskDsl {
|
||||
version: "1";
|
||||
nodes: Array<{ id: string; kind: string; label?: string; config: unknown }>;
|
||||
edges: Array<{ source: string; target: string }>;
|
||||
// sourceHandle 标记从 branch 节点引出的边走真/假分支("true"/"false")。
|
||||
edges: Array<{ source: string; target: string; sourceHandle?: string }>;
|
||||
}
|
||||
|
||||
// exportDsl 把画布的节点/连线导出为类型化 JSON DSL。
|
||||
@@ -16,7 +17,11 @@ export function exportDsl(nodes: Node[], edges: Edge[]): TaskDsl {
|
||||
const d = n.data as { kind: string; label?: string; config?: unknown };
|
||||
return { id: n.id, kind: d.kind, label: d.label, config: d.config ?? {} };
|
||||
}),
|
||||
edges: edges.map((e) => ({ source: e.source, target: e.target })),
|
||||
edges: edges.map((e) => ({
|
||||
source: e.source,
|
||||
target: e.target,
|
||||
...(e.sourceHandle ? { sourceHandle: e.sourceHandle } : {}),
|
||||
})),
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -19,7 +19,7 @@ export function TypedNode({ data, selected }: NodeProps) {
|
||||
|
||||
return (
|
||||
<div
|
||||
className={`min-w-[170px] rounded-lg border border-l-[3px] bg-ink-800 shadow-card ${k.accent} ${
|
||||
className={`relative min-w-[170px] rounded-lg border border-l-[3px] bg-ink-800 shadow-card ${k.accent} ${
|
||||
selected ? "ring-2 ring-violet-500/70" : "border-line"
|
||||
}`}
|
||||
>
|
||||
@@ -32,7 +32,17 @@ export function TypedNode({ data, selected }: NodeProps) {
|
||||
<div className="text-xs font-medium text-slate-200">{d.label || k.desc}</div>
|
||||
{d.summary && <div className="mt-0.5 truncate text-[10px] text-slate-500">{d.summary}</div>}
|
||||
</div>
|
||||
<Handle type="source" position={Position.Right} className="!h-2 !w-2 !border-0 !bg-slate-500" />
|
||||
{d.kind === "branch" ? (
|
||||
// 分支节点:两个出口手柄 —— 真(绿)/假(红),连线时各自带 sourceHandle,引擎据此精确选路。
|
||||
<>
|
||||
<Handle id="true" type="source" position={Position.Right} style={{ top: "38%" }} className="!h-2.5 !w-2.5 !border-0 !bg-emerald-500" />
|
||||
<span className="pointer-events-none absolute right-3 top-[32%] text-[9px] font-medium text-emerald-400">真</span>
|
||||
<Handle id="false" type="source" position={Position.Right} style={{ top: "72%" }} className="!h-2.5 !w-2.5 !border-0 !bg-rose-500" />
|
||||
<span className="pointer-events-none absolute right-3 top-[66%] text-[9px] font-medium text-rose-400">假</span>
|
||||
</>
|
||||
) : (
|
||||
<Handle type="source" position={Position.Right} className="!h-2 !w-2 !border-0 !bg-slate-500" />
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user