For AI agents: the complete documentation index is available at https://a3s-lab.github.io/Flow/v1.0.0/llms.txt, the full documentation bundle is available at https://a3s-lab.github.io/Flow/v1.0.0/llms-full.txt, and this page is available as Markdown at https://a3s-lab.github.io/Flow/v1.0.0/concepts/workflow-dag.md.
  • 简体中文
  • v1.0.0
  • 工作流图

    WorkflowDslWorkflowDag 负责可移植工作流文档的结构边界。它们解析节点与边、保留不认识的扩展字段、校验图结构,并生成确定性计划。具体节点怎样执行仍由宿主绑定。

    Flow 不根据 data.type 自动获得模型、数据库或工具权限。宿主先验证并授权节点,再把节点语义接到持久步骤上。

    文档与裸图

    完整文档包含版本、应用信息和 workflow.graph。只需要处理画布图时,也可以直接解析 WorkflowDag

    use a3s_flow::{WorkflowDag, WorkflowDsl};
    
    let document = WorkflowDsl::from_yaml(&yaml_source)?;
    let graph = document.graph();
    
    let bare_graph = WorkflowDag::from_json(&json_source)?;

    单份 UTF-8 文档上限为 10 MiB。当前经过测试的 DSL 版本是 0.7.0。旧小版本可以带警告导入,新版本或不同主版本需要宿主明确决定是否接受。

    最小图结构

    {
      "nodes": [
        {
          "id": "start",
          "position": { "x": 0, "y": 0 },
          "data": { "type": "start" }
        },
        {
          "id": "validate-order",
          "position": { "x": 280, "y": 0 },
          "data": {
            "type": "tool",
            "operation": "orders.validate"
          }
        },
        {
          "id": "end",
          "position": { "x": 560, "y": 0 },
          "data": { "type": "end" }
        }
      ],
      "edges": [
        { "id": "start-validate", "source": "start", "target": "validate-order" },
        { "id": "validate-end", "source": "validate-order", "target": "end" }
      ],
      "viewport": { "x": 0, "y": 0, "zoom": 1 }
    }

    节点必须有非空 id 和字符串 data.type。边必须有唯一 ID,并且引用已经存在的起点与终点。

    Playground 中的连线名称

    Playground 默认把源节点输出端口的名称显示在连线中间。例如批处理节点的 done 端口会显示为“全部完成”。这段文字用于帮助阅读图结构,不是引擎用来路由的端口 ID。

    选中连线后,点击名称旁的铅笔按钮,或双击名称即可编辑。也可以先选中连线,再按 Enter 或 F2。输入完成后按 Enter 保存,按 Escape 放弃,失去焦点也会保存。把输入框清空会恢复端口默认名称。自定义名称会保存在本地草稿和导出的工作流文件中。

    编辑名称不会改变 sourceHandletargetHandle、执行计划或运行语义。条件节点的成立与其他分支名称仍应在节点配置中的 matched_labelotherwise_label 字段修改,因为这两个字段属于条件节点的业务文案。普通连线名称只是画布展示字段,发布前仍应按端口 ID 校验整张图。

    生成执行计划

    let plan = graph.execution_plan()?;
    println!("top_level={:?}", plan.top_level());
    
    for (scope, order) in plan.scopes() {
        println!("scope={scope:?} order={order:?}");
    }

    同一作用域中有多个可执行节点时,计划按稳定 ID 排序消除输入数组顺序的影响。计划只表达结构拓扑,不执行节点,也不决定失败、重试或授权策略。

    空画布可以被解析并再次保存,便于编辑器保留草稿。execution_plan() 会拒绝空画布,因为没有可执行节点。

    结构校验

    编译计划前会拒绝以下情况。

    • 重复或空的节点 ID、边 ID
    • 缺少字符串 data.type
    • 边引用不存在的节点
    • 节点连接自己
    • 任一作用域内存在环
    • 边跨越顶层与容器作用域
    • parentId 指向不存在或不支持的容器
    • 迭代与循环容器缺少对应的起始子节点
    • 超过 10,000 个节点或 100,000 条边

    这些检查保证拓扑可确定,但不会判断节点参数是否满足某个业务执行器。宿主需要对每个已授权节点类型继续做模式校验。

    迭代与循环作用域

    iterationloop 节点可以成为容器。容器内节点通过 parentId 归属作用域,内部边只能连接同一容器里的节点。

    {
      "id": "for-each-item",
      "data": {
        "type": "iteration",
        "start_node_id": "iteration-start"
      }
    }

    对应的起始节点必须满足三个条件。

    1. parentId 等于容器 ID。
    2. 迭代使用 iteration-start,循环使用 loop-start
    3. 容器内至少还有一个可执行子节点。

    plan.scope("for-each-item") 返回容器内部的确定性顺序。容器自身留在顶层计划中。

    语义摘要

    execution_digest() 为可执行语义生成稳定 SHA-256 身份。

    let digest = graph.execution_digest()?;
    println!("execution_digest={digest}");

    节点位置、选中状态、视口和输入数组顺序不会改变摘要。节点 data、边的语义数据、连接端点和作用域会改变摘要。宿主可以用它判断两次发布是否只调整了画布布局。

    摘要只证明输入语义相同,不证明宿主绑定的节点实现相同。节点执行器版本仍要进入宿主发布身份或 runtime_build_id

    无损往返与扩展字段

    解析器会保留未解释的顶层字段、应用字段、工作流字段、节点展示字段和边展示字段。to_yaml()to_json() 可把这些字段写回。

    保留字段不代表接受其语义。安全边界仍然要采用允许列表。

    • 先解析并限制文档大小。
    • 再运行结构计划校验。
    • 对允许的 data.type 做宿主模式校验。
    • 解析凭据引用,禁止把明文密钥放入图文档。
    • 发布时记录文档摘要和节点执行器版本。

    与持久运行的衔接

    图编译器输出节点顺序,Flow 引擎持久化运行决定。宿主通常会把一个节点映射为稳定步骤 ID,把分支选择或容器游标写进步骤输出或续段输入。

    如果图在运行期间发生语义变化,应创建新的工作流定义版本或补丁路径。不要用新图解释已经开始的旧历史。

    完整导入示例位于 examples/workflow_dsl_import.rs