在上一篇文章中,我们解析了 Dify Workflow 的“骨架”——DSL 定义与 Node 抽象。今天,我们将为这具骨架注入灵魂。
当用户点击“运行”按钮时,静止的 JSON 配置是如何变成流动的业务逻辑的?数据是如何在节点间精准传递的?当遇到分支(If/Else)或并行(Parallel)时,引擎又是如何调度的?
本篇将深入 core/workflow 的运行时(Runtime)层面,解密 Dify 的执行引擎。
核心代码路径:
api/core/workflow/runner及api/core/workflow/nodes
1. 指挥官:WorkflowRunner
整个执行过程的入口位于 WorkflowRunner 类。它的职责类似于操作系统的调度器:初始化上下文、找到入口点、并驱动整个图的流转。
1.1 启动流程
当 API 接收到运行请求时,WorkflowRunner 会被实例化并执行 run 方法:
-
初始化
VariablePool: 这是运行时的内存空间,用于存储所有节点的输入输出。 -
加载系统变量: 将
sys.query,sys.user_id等预置变量注入池中。 -
寻找起点: 扫描图结构,找到唯一的
StartNode。 -
触发遍历: 从 Start 节点开始,沿着边(Edge)向下游进发。
2. 数据流转的核心:Variable Pool (变量池)
在 Dify 的设计中,节点之间并不直接“传递”数据对象,而是通过一个共享的变量池进行间接通信。
2.1 读写机制
-
写入 (Write): 当节点 A (
llm_1) 执行完毕后,它会将执行结果(如{ "text": "Hello" })写入VariablePool,键值为llm_1。 -
读取 (Read): 当下游节点 B 需要使用 A 的结果时,它会在执行前向
VariablePool发起请求,获取llm_1.text的值。
2.2 映射处理 (Mapping)
在 DSL 中定义的变量引用(如 ["llm_1", "text"])在运行时会被解析器转化为具体的查找指令。
这种**“发布-订阅”**式的内存模型,极大地解耦了节点关系。节点只管运行,无需关心谁在消费它的数据。
3. 调度策略:广度优先与动态依赖
Dify 如何决定下一个执行哪个节点?虽然我们在计算机科学中学过“拓扑排序”,但在包含条件分支(If/Else)的 Workflow 中,静态的拓扑排序是不够的。
Dify 采用的是一种**动态图遍历(Dynamic Graph Traversal)**策略。
3.1 后继节点查找 (Successor Finding)
每当一个节点执行成功:
-
Runner 会查看该节点的
outgoing_edges(出边)。 -
识别出所有直接连接的下游节点(Successors)。
-
依赖检查 (Dependency Check): 这是关键一步。系统会检查下游节点的所有前置依赖是否都已在
VariablePool中就绪。
3.2 分支处理 (Branching)
对于 If/Else 节点或 QuestionClassifier 节点,调度逻辑变得特殊:
-
节点执行后,会返回一个特定的
edge_id或port_id(即命中的路径)。 -
Runner 只会将命中路径上的下游节点加入执行队列。
-
未命中路径上的节点及其后续节点将被自动跳过(Skipped),不会消耗计算资源。
4. 并发控制:Parallel Node (并行节点)
Dify 引入了 Parallel 节点(以及迭代节点 Iteration)来处理复杂的并发需求。这里涉及到 Python 的并发编程。
4.1 线程级并发
虽然 Python 有 GIL(全局解释器锁),但对于 I/O 密集型任务(如请求 OpenAI、搜索网络),多线程依然能带来显著的性能提升。
在 core/workflow/nodes/parallel 的实现中:
-
当 Runner 遇到并行分支时,会利用
concurrent.futures.ThreadPoolExecutor创建线程池。 -
不同的分支(Branch)被提交到线程池中同时运行。
-
同步栅栏 (Barrier): 必须等待所有并行分支都执行完毕(或报错),流程才会汇聚到下一个节点。
5. 状态机与回调系统 (Callback System)
用户在前端看到的一个个绿色的“勾”,背后是强大的回调机制在支撑。
WorkflowRunner 在执行的每个阶段都会触发事件:
-
on_workflow_start -
on_node_start: 节点变蓝(运行中)。 -
on_node_finish: 节点变绿(成功)或变红(失败)。 -
on_workflow_finish
这些事件通过 WorkflowCallback 被推送到:
-
数据库: 记录日志,用于审计和调试。
-
SSE (Server-Sent Events): 实时推送到前端,让用户看到“打字机”效果和进度条更新。
6. 总结
Dify 的 Workflow 运行时是一个精密的状态机:
-
Variable Pool 是它的共享内存。
-
Graph Traversal 是它的指令指针。
-
Runner 是它的 CPU。
它巧妙地结合了传统的图算法与现代的异步并发模式,既保证了逻辑执行的严谨性(通过依赖检查),又提供了在大模型场景下至关重要的并发性能。
下期预告:
Dify 源码解析 (六):Agent Runtime——ReAct 策略与工具调用的幕后机制。
Dify Workflow引擎运行时解析

266





