核心设计原则

原则一:框架无关(Framework-Agnostic Core)

`rust-agent-flow`(core)不依赖任何 UI 框架。它只提供数据模型与算法:

core: FlowGraph + Schema + Geometry + Layout + Viewport
      (纯 Rust,零 GPUI 依赖)

这意味着:

- 图模型、边路径算法、布局引擎可被**任何** Rust 项目复用 - 未来可基于同一 core 实现不同渲染层(egui、WebAssembly + Canvas 等) - core 的单元测试无需窗口,纯数据验证

`rust-agent-flow-gpui` 才引入 GPUI,实现 `FlowEditorView`。应用代码只依赖 gpui 层,但可向下穿透到 core 直接使用算法。

原则二:策略模式(Strategy Pattern)

节点行为通过 `IFlowNode` trait 多态分发,而非 match 分发:

pub trait IFlowNode: Send + Sync {
    fn kind(&self) -> &str;
    fn get_view(&self, node: &Node, ctx: &mut NodeViewCtx) -> AnyElement;
    fn get_panel(&self, node: &Node, ctx: &mut NodeViewCtx) -> AnyElement;
    fn schema(&self) -> &NodeSchema;
    // ...可选方法
}

`NodeRegistry` 按 `kind` 字符串匹配实现:

registry.register(Arc::new(StartNode::new()));
registry.register(Arc::new(MyCustomNode::new()));
// 渲染时:registry.get(&node.kind) → Arc<dyn IFlowNode>

好处:

- 新增节点类型**不修改框架代码**,只注册新实现 - `kind` 是 `String` 而非枚举——支持任意自定义类型,无需修改 core - 内置 8 种节点与自定义节点走同一套分发路径

原则三:Schema 驱动(Schema-Driven)

节点的 `NodeSchema.fields` 描述 `node.data` 的字段结构,属性面板据此**自动生成**编辑界面:

NodeSchema::new("action", "Action")
    .with_field(FieldSpec::new("label", "Label", FieldType::Text))
    .with_field(FieldSpec::new("code", "Code", FieldType::CodeBlock))
    .with_field(FieldSpec::new("enabled", "Enabled", FieldType::Switch))

`FieldType` 映射到编辑控件:

| FieldType | 控件 | |-----------|------| | Text | 单行 Input | | TextArea | 多行 Input | | CodeEditor | 单行代码编辑器 | | CodeBlock | 多行代码编辑器(带行号) | | Number | 数字 Input | | Switch | 布尔开关 | | Dropdown | 下拉选择 | | List | 动态列表(可增删行) |

这消除了传统的 per-kind 面板分发——所有节点共用一个 `PanelView`,按 schema.fields 渲染。新增节点只需声明字段,面板自动跟上。

原则四:能力与 UI 分离

框架**只提供能力方法**,UI 控件由调用侧实现:

| 能力方法 | UI 由谁实现 | |----------|-------------| | `toggle_theme()` | 调用侧的工具栏按钮 | | `toggle_drag()` | 调用侧的工具栏按钮 | | `set_language()` | 调用侧的工具栏按钮 | | `set_graph()` | 调用侧的数据源选择器 |

框架内置工具栏只包含**框架级**操作(布局方向切换、缩放重置等)。拖拽开关、主题切换、语言切换、数据源选择——这些跟随目标应用产品的控件,由调用侧通过 `ToolbarProvider` 注入。

这是刻意的边界:框架不应假定你的应用需要什么样的工具栏布局。

原则五:单一数据源(Single Source of Truth)

图结构是唯一真相,所有派生数据通过缓存关联:

FlowGraph (真相)
  ├─ cached_body_groups     // 循环体分组
  ├─ cached_all_body_nodes  // 扁平化
  ├─ cached_hidden_nodes    // 收起的节点
  └─ version (版本计数)

`version` 在任何结构性变更时递增,用于失效缓存的几何数据。缓存在 `relayout` 末尾统一更新,避免每帧重复 O(V+E) 遍历。

原则六:声明式数据协议

`FlowDocument` 是流程图的**序列化协议**(JSON):

- 节点用**数组索引**引用(而非 slotmap 内部 key),保证序列化稳定 - 包含 `version`(协议版本)、`metadata`(名称/描述)、`nodes`、`edges` - `FlowGraph::from_document` / `to_document` 双向互转

这使流程定义可跨语言消费、可持久化到文件、可通过网络传输。

设计原则总览

| 原则 | 体现 | 收益 | |------|------|------| | 框架无关 | core 无 GPUI | 算法可复用 | | 策略模式 | IFlowNode + Registry | 开放扩展 | | Schema 驱动 | FieldSpec → 面板 | 消除分发 | | 能力与 UI 分离 | setter + ToolbarProvider | 不侵入产品 | | 单一数据源 | FlowGraph + 缓存 | 一致性 | | 声明式协议 | FlowDocument JSON | 可持久化 |

小结

这六条原则贯穿整个框架。理解它们,你就能预测框架在新增需求时的设计走向:能用 Schema 声明的不写代码分发,能用 trait 注入的不硬编码,能下沉到 core 的不放在 gpui。

下一节:[ReactFlow 的启发](reactflow-inspiration.md)