Dify 源码解析 (四):Workflow 引擎(上)—— 基于图的 DSL 设计与解析

如果说 RAG 是 Dify 的“海马体”(负责记忆),那么 Workflow 引擎无疑是它的“前额叶皮层”——负责逻辑推理、决策制定和任务编排。

Dify 的 Workflow 是一个可视化的、基于图(Graph-based)的编排系统。本篇作为 Workflow 系列的上篇,我们将深入 core/workflow 模块,剖析 Dify 是如何通过 DSL(领域特定语言)定义业务逻辑,并将前端拖拽的“图”转化为后端可执行的代码对象的。

核心代码路径: api/core/workflow


1. 什么是 Workflow DSL?

在 Dify 中,用户在画布上拖拽生成的每一个 Workflow,最终都会被序列化为一个 JSON 对象。这个 JSON 就是 Dify 的 DSL。它描述了节点(Nodes)的存在以及它们之间的数据流向(Edges)。

1.1 数据结构概览

一个典型的 Workflow DSL 结构包含以下核心要素:

JSON

{
  "graph": {
    "nodes": [
      {
        "id": "llm_1",
        "data": {
          "title": "LLM 节点",
          "type": "llm",
          "model": { "provider": "openai", "name": "gpt-4" },
          "prompt_template": "..."
        },
        "position": { "x": 100, "y": 200 },
        ...
      }
    ],
    "edges": [
      {
        "source": "start_node",
        "target": "llm_1",
        ...
      }
    ]
  }
}
  • Nodes (节点): 定义了任务单元。包含 id(唯一标识)、data(业务配置,如提示词、模型参数)和 type(节点类型)。

  • Edges (边): 定义了控制流。即“谁执行完之后轮到谁”。

  • View Data:position 等前端展示数据,后端在解析执行时通常会忽略,但在存储时必须保留以还原画布。


2. 核心抽象:Node (节点) 的设计

在后端代码 core/workflow/nodes 中,所有的节点都遵循严格的继承体系。

2.1 基类设计 (BaseNode)

所有具体节点(如 LLMNode, CodeNode, KnowledgeRetrievalNode)都继承自 BaseNode。这个基类定义了所有节点必须遵守的“契约”:

  1. Config Schema (配置校验): 每个节点必须定义自己的配置结构验证规则(通常使用 Pydantic 或 Marshmallow),确保前端传入的 data 字段合法。

  2. Input/Output (输入输出): 显式定义该节点接受什么变量,输出什么变量。

  3. Run (执行入口): 抽象方法 _run,子类必须实现具体的业务逻辑。

2.2 节点的多态实现

Dify 利用工厂模式根据 DSL 中的 type 字段实例化不同的节点类:

  • LLM Node: 封装了对 model_runtime 的调用,处理 Prompt 组装和模型推理。

  • Code Node: 一个安全的沙箱环境(Sandbox),允许用户运行 Python/Node.js 代码片段来转换数据。

  • If/Else Node: 逻辑分支节点,它不产生新数据,而是决定控制流的走向。


3. 解析器:从 JSON 到对象 (The Parser)

当请求到达后端时,原始的 JSON 字符串是一堆死数据,无法直接运行。WorkflowParser (core/workflow/parser.py) 承担了“编译器”的角色。

3.1 解析流程

WorkflowParser 的工作主要包含三个步骤:

  1. 结构反序列化: 将 JSON 转换为 Python 字典和对象。

  2. 拓扑验证 (Validation):

    • 连通性检查: 检查是否存在孤立节点。

    • 环路检测: Workflow 通常是一个 DAG(有向无环图)。解析器需要检测是否存在死循环(虽然某些高级场景允许循环,但基础检查必不可少)。

    • 起点/终点检查: 确保有 Start 节点和 End 节点。

  3. 变量引用解析 (Variable Selector Validation):

    这是最复杂的一步。节点 B 想要使用节点 A 的输出,必须验证:

    • 节点 A 是否是节点 B 的上游(Predecessor)。

    • 节点 A 输出的变量名是否存在。

3.2 变量选择器 (Value Selector)

在 Dify 的 DSL 中,变量引用通常表示为一个数组,例如 ["llm_1", "text"]。

解析器会构建一个**变量池(Variable Pool)**的概念。在解析阶段,解析器会模拟遍历整个图,收集每个节点可能产生的 Output Schema,从而验证下游节点的引用是否合法。


4. 关键设计思考:逻辑与执行分离

Dify 的架构设计体现了一个重要的工程原则:定义与执行分离

  • Definition Layer (core/workflow/graph_engine): 负责构建图的结构,处理节点间的依赖关系。它只关心“图长什么样”。

  • Execution Layer (core/workflow/runner): 负责真正的运行。它并不直接操作 JSON,而是操作由 Parser 生成的 WorkflowGraph 对象。

这种分离带来的好处是:

  1. 安全性: 在运行前就能拦截大部分配置错误。

  2. 可测试性: 可以单独测试 Parser 的校验逻辑,而无需真的去调用 OpenAI API。

  3. 扩展性: 新增一种节点类型,只需增加对应的 Node 类和 Schema 定义,无需修改图的核心解析逻辑。


5. 总结

core/workflow 的静态部分展示了 Dify 如何将复杂的业务逻辑抽象为结构化的数据:

  1. DSL 是前后端通信的契约。

  2. Node 是业务逻辑的封装单元。

  3. Parser 是保证图结构合法、逻辑通顺的守门员。

理解了这些,你就看懂了 Dify Workflow 的“骨架”。

下期预告:

下一篇我们将进入 Dify Workflow 引擎(下)——拓扑排序与节点执行调度


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

天天进步2015

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值