Dify Workflow 硬核解读(万字长文)

1. 结论与阅读建议

如果要先用一句话概括 Dify Workflow 的本质,可以这样记:

Dify Workflow 不是简单的顺序执行器,而是一套队列驱动、事件驱动、支持外部命令控制的图执行引擎。

如果再进一步压缩成最核心的执行闭环,则是:

  1. 上层业务创建 WorkflowEntry
  2. WorkflowEntry 装配 GraphEngine
  3. GraphEngine 持有 GraphGraphRuntimeState
  4. 可执行节点进入 ready queue
  5. Workerready queue 取节点并执行
  6. 节点执行过程持续产出 node events
  7. 事件进入 event queue
  8. Dispatcher 分发事件给 EventHandler
  9. EventHandler 更新图状态、变量池和边状态
  10. 后继节点被重新放入 ready queue
  11. 重复直到成功、失败、暂停或被中止

这意味着:

  • Graph 负责"结构"
  • GraphRuntimeState 负责"共享运行态"
  • Node 负责"统一节点执行协议"
  • GraphEngine 负责"总调度"
  • Worker 负责"实际跑节点"
  • Dispatcher + EventHandler 负责"根据事件推进图"
  • CommandProcessor 负责"把外部控制命令接入运行时"

如果只是第一次读这块源码,建议先建立这个分工图,再下钻具体文件。

2. 当前模块在整个 Dify 中的位置

api/core/workflow/ 是 Dify 后端里最核心的执行引擎之一。

如果说应用层、服务层解决的是:

什么时候触发 workflow

那么 api/core/workflow/ 解决的是:

workflow 一旦开始执行,内部到底怎么流转、调度、暂停、恢复和收尾

这块模块承担的不是简单的数据编排,而是 Dify 的平台级执行能力,包括:

  • 图结构执行
  • 并行分支
  • 条件分支
  • 迭代与循环
  • 节点暂停
  • 外部中止
  • 恢复执行
  • 事件流输出
  • 变量池共享

因此这块代码的阅读方式,不能按"找一个主函数一路跟到底"的思路来做,而要先按分层理解。

3. 目录结构应该怎么理解

当前 api/core/workflow/ 下最值得先建立认知的目录如下:

  • workflow_entry.py
  • graph/
  • runtime/
  • nodes/
  • node_events/
  • graph_events/
  • graph_engine/
  • entities/
  • context/
  • repositories/

如果按职责再压缩一层,可以拆成 6 个阅读块。

3.1 入口装配层

  • workflow_entry.py

这一层回答的问题是:

  • 上层业务是如何把一个 workflow 运行起来的
  • 引擎启动前需要装配哪些依赖

3.2 结构定义层

  • graph/
  • entities/

这一层回答的问题是:

  • 工作流图在内存中如何表示
  • 节点、边、初始化参数分别如何建模

3.3 共享运行态层

  • runtime/

这一层回答的问题是:

  • 执行期间哪些状态是跨节点共享的
  • 变量池、ready queue、暂停态、输出、用量统计放在哪里

3.4 节点协议层

  • nodes/
  • node_events/

这一层回答的问题是:

  • 一个节点怎样被定义、注册、执行和发事件
  • 具体节点类型如何挂入统一协议

3.5 执行调度层

  • graph_engine/
  • graph_events/

这一层回答的问题是:

  • 调度器如何推进图执行
  • 事件如何消费
  • 边如何推进
  • 图状态如何收敛

3.6 外部控制与扩展层

  • graph_engine/command_channels/
  • graph_engine/layers/
  • repositories/

这一层回答的问题是:

  • workflow 运行中如何被外部 stop / pause / resume
  • 引擎怎样挂载调试、限制、观测等横切能力

4. 先看官方 README,先建立正确的抽象

api/core/workflow/README.md 已经给了很重要的抽象框架。

其中最关键的两个信息是:

第一,当前引擎是:

queue-based distributed workflow execution system

第二,它在结构上遵循严格分层:

  • graph_engine
  • graph_events
  • graph
  • nodes
  • node_events
  • entities

这个分层顺序很重要,因为它直接反映了代码的依赖方向。

也就是说,在 Dify Workflow 里,作者并不希望你把"结构定义""节点实现""调度逻辑""事件系统"混在一起写。

阅读源码时,如果先建立这个分层意识,后面很多目录命名会变得很清楚。

5. 第一个入口:WorkflowEntry

最值得先读的文件是:

  • api/core/workflow/workflow_entry.py

5.1 它的角色不是执行器,而是装配器

WorkflowEntry 本身并不负责跑图。

它解决的是更上游的问题:

  • 为一次 workflow 执行构造正确的运行环境
  • 注入 command channel
  • 创建 GraphEngine
  • 挂上必要的 layers
  • 对外暴露统一 run() 接口

因此它更接近:

  • 引擎入口
  • 执行上下文装配器
  • 运行时依赖组织者

5.2 这层为什么重要

很多人读 workflow 引擎,会一上来就扎进 graph_engine.py

但如果不先理解 WorkflowEntry,就会漏掉很多"引擎外部是怎么把依赖喂进去的"这一层上下文。

5.3 它最值得看的内容

从阅读角度,WorkflowEntry 有三类价值:

  1. 看引擎初始化依赖
  2. 看 layer 如何挂载
  3. 看单节点运行模式

其中 single_step_run() 尤其值得看,因为它暴露了一个"最小执行闭环":

  • 构造 GraphInitParams
  • 创建 GraphRuntimeState
  • 通过工厂拿到节点实例
  • 处理变量映射
  • 将输入灌入变量池
  • 最后执行 node.run()

如果你想快速理解"节点单独运行时最少依赖什么",这个入口比直接读大引擎更高效。

6. Graph:工作流图的内存表示

关键目录与文件:

  • api/core/workflow/graph/
  • api/core/workflow/graph/graph.py

6.1 Graph 的角色

Graph 不是调度器,也不是运行态容器。

它是:

  • 工作流图的内存表示
  • 节点与边的组织者
  • root node 和边关系的索引持有者

6.2 它关心的是"结构",不是"执行过程"

Graph 主要持有的内容包括:

  • nodes
  • edges
  • in_edges
  • out_edges
  • root_node

这说明 Graph 的任务重心是:

  • 节点配置解析
  • 边关系建立
  • root node 识别
  • 节点与边的索引组织

而不是:

  • 节点什么时候可运行
  • 哪个节点已经完成
  • 当前 workflow 是否暂停

这些都不属于 Graph 的职责范围。

6.3 这一层最重要的理解

阅读 Graph 时要特别避免一个误区:

不要把"工作流图"和"工作流执行过程"混为一谈。

在 Dify 里,两者是明确拆开的:

  • Graph 是静态或半静态结构
  • GraphRuntimeState 是动态运行态

这也是后面理解整个引擎的关键前提。

7. GraphRuntimeState:共享运行态容器

关键目录与文件:

  • api/core/workflow/runtime/
  • api/core/workflow/runtime/graph_runtime_state.py

7.1 它是什么

如果用一句话概括:

GraphInitParams 是启动输入,GraphRuntimeState 是执行期间持续变化的共享状态。`

7.2 它承载什么

从源码结构和已有阅读结果看,这里集中承载了 workflow 运行期间的核心共享态,例如:

  • variable_pool
  • ready_queue
  • graph_execution
  • response_coordinator
  • outputs
  • llm_usage
  • node_run_steps
  • paused_nodes
  • deferred_nodes
  • 图级和节点级状态快照
  • stop_event

7.3 为什么它是核心

如果没有 GraphRuntimeState,引擎里很多角色就只能彼此强耦合:

  • Worker 执行节点需要访问变量
  • EventHandler 推进边时需要更新图状态
  • 外部命令处理需要看到暂停和停止状态
  • Layer 观察运行过程也需要只读视图

GraphRuntimeState 的存在让这些角色围绕同一份运行态协作,而不是彼此直接调用和共享零散字段。

7.4 对阅读者最重要的判断

如果你想知道:

  • 某个状态到底存在哪里
  • 某个节点为什么能读到前置节点输出
  • 暂停信息从哪儿取

第一反应应该先查 GraphRuntimeState,而不是先查 GraphEngine

8. Node 基类:统一节点协议

关键目录与文件:

  • api/core/workflow/nodes/base/
  • api/core/workflow/nodes/base/node.py

8.1 为什么先看基类,而不是先看具体节点

nodes/ 下面具体节点很多:

  • llm
  • agent
  • code
  • http_request
  • iteration
  • loop
  • tool
  • if_else
  • start
  • end

如果一开始就选某个节点实现往里扎,很容易只理解局部业务逻辑,却看不到 Dify 如何统一抽象"节点执行协议"。

8.2 Node 基类解决了哪些共性问题

从阅读角度,最值得关注的共性问题至少有三类。

第一,节点类型注册。

通过 __init_subclass__ 一类机制,节点子类会被自动纳入注册体系,并关联自己的 NodeData 类型。

第二,节点配置的强类型化。

节点不会长期直接操作原始 config["data"],而是会被 hydrate 成明确的 BaseNodeData 子类。

第三,统一执行包装。

外界不是直接调用 _run(),而是调用 run()

run() 负责统一做这些事情:

  • 生成执行 id
  • 发出开始事件
  • 调用真正的 _run()
  • 将 node event 映射为 graph 侧可消费事件
  • 捕获异常并统一发失败事件

8.3 这一层的意义

这说明 Dify 的节点体系不是"每个节点自己随意跑"。

相反,它强调:

  • 节点有统一生命周期
  • 节点有统一事件协议
  • 节点有统一的输入输出与异常包装方式

这对后面的调度器和事件系统至关重要。

9. GraphEngine:执行引擎总调度器

关键目录与文件:

  • api/core/workflow/graph_engine/
  • api/core/workflow/graph_engine/graph_engine.py

9.1 它的角色

GraphEngine 是整个 workflow 运行期的总 orchestrator。

如果只看职责,不看细节,它解决的是:

  • 怎么把结构、运行态、事件、Worker、命令处理、Layer 组装起来
  • 怎么把一次 workflow 执行稳定推进到终态

9.2 它装配了哪些子系统

结合当前目录与源码结构,GraphEngine 附近最关键的子系统包括:

  • ready_queue
  • event_management
  • graph_state_manager
  • response_coordinator
  • error_handler
  • graph_traversal
  • command_processing
  • worker_management
  • orchestration
  • layers

更具体地说,阅读时最值得建立的对象图是:

  • GraphStateManager
  • EventManager
  • EventHandler
  • Dispatcher
  • WorkerPool
  • ExecutionCoordinator
  • EdgeProcessor
  • CommandProcessor
  • ResponseCoordinator

9.3 它为什么不是单纯的"主循环"

很多工作流引擎会有一个清晰的 while loop 主循环,里面顺序做:

  1. 取节点
  2. 跑节点
  3. 判断后继
  4. 继续

但 Dify 这里更像一个协作式系统:

  • Worker 执行节点
  • Dispatcher 消费事件
  • EventHandler 推进状态
  • EdgeProcessor 处理边
  • CommandProcessor 处理中途命令

所以 GraphEngine 更像"装配和协调中心",而不是"所有逻辑都写在一个大循环里"。

10. Worker:真正执行节点的运行者

关键文件:

  • api/core/workflow/graph_engine/worker.py
  • api/core/workflow/graph_engine/worker_management/worker_pool.py

10.1 Worker 的职责很纯粹

Worker 的核心职责可以压缩成四步:

  1. ready_queue 取出 node id
  2. Graph 中定位节点
  3. 执行 node.run()
  4. 将执行产生的事件写入 event queue

10.2 为什么这个角色必须独立存在

如果调度器一边决定执行顺序,一边自己执行节点,耦合会非常重。

拆出 Worker 之后,整个系统就变成:

  • 调度负责推进
  • Worker 负责执行
  • 事件系统负责传递结果

这让并发、暂停、失败收敛、Layer 观测都更容易实现。

11. Dispatcher 与 EventHandler:图推进的真正核心

关键文件:

  • api/core/workflow/graph_engine/orchestration/dispatcher.py
  • api/core/workflow/graph_engine/event_management/event_handlers.py

11.1 为什么说"真正决定图怎么前进"的不是 Worker

Worker 只负责把节点跑完。

但一个节点跑完之后:

  • 是成功还是失败
  • 是否进入 fail-branch
  • 是否要暂停
  • 是否可以激活后继边
  • 是否应该把下一个节点放进 ready queue

这些都不是 Worker 自己决定的。

真正决定图怎么推进的是:

  • Dispatcher
  • EventHandler

11.2 Dispatcher 做什么

Dispatcher 负责:

  • event queue 取事件
  • 将事件交给 EventHandler
  • 在运行过程中检查命令和终止条件
  • 判断图是否 complete / paused / aborted

11.3 EventHandler 做什么

EventHandler 负责根据不同事件类型推进运行态。

从阅读角度,最值得先抓的四类事件是:

  1. NodeRunStartedEvent
  2. NodeRunSucceededEvent
  3. NodeRunFailedEvent
  4. NodeRunPauseRequestedEvent

这四类事件分别对应:

  • 节点进入运行
  • 节点成功完成
  • 节点异常失败
  • 节点主动触发暂停

真正的图推进逻辑,大部分都藏在"这些事件被如何消费"里,而不是藏在节点本身。

11.4 这一层揭示的设计思想

Dify Workflow 不是通过"节点直接调用节点"来推进图。

它采用的是:

节点执行 -> 产出事件 -> 事件被消费 -> 状态推进 -> 后继节点入队

这使得:

  • 节点实现可以保持相对纯粹
  • 调度策略可以集中管理
  • Layer 和观测系统更容易挂接

12. GraphStateManager 与 EdgeProcessor:状态推进和路径选择

关键文件:

  • api/core/workflow/graph_engine/graph_state_manager.py
  • api/core/workflow/graph_engine/graph_traversal/edge_processor.py
  • api/core/workflow/graph_engine/graph_traversal/skip_propagator.py

12.1 GraphStateManager 的角色

它统一管理图执行期间的关键状态,例如:

  • node state
  • edge state
  • 当前执行中的节点
  • ready queue 入队相关状态

它解决的是:

  • 哪些节点已开始
  • 哪些节点已结束
  • 哪些节点可进入 ready 状态
  • 图当前收敛到什么程度

12.2 EdgeProcessor 的角色

节点成功后,后续该怎么走,不是节点自身决定,而是由 EdgeProcessor 基于图结构和边规则来决定。

也就是说,路径推进的主语不是:

  • 节点自己调用下游节点

而是:

  • 调度侧根据边规则推进图

12.3 这一层为什么重要

如果不把"节点执行"和"边推进"拆开,那么:

  • 分支逻辑会散落到各个节点内部
  • fail-branch 和 skip 规则难以统一
  • 调试和可视化都会变乱

当前这种拆法说明 Dify 对 workflow 的理解是"图执行系统",而不是"节点函数链"。

13. 外部控制是如何进入引擎的

关键文件:

  • api/core/workflow/graph_engine/manager.py
  • api/core/workflow/graph_engine/command_processing/command_processor.py
  • api/core/workflow/graph_engine/command_processing/command_handlers.py
  • api/core/workflow/graph_engine/entities/commands.py
  • api/core/workflow/graph_engine/command_channels/in_memory_channel.py
  • api/core/workflow/graph_engine/command_channels/redis_channel.py

13.1 这块解决什么问题

workflow 在运行中经常需要被外部系统控制,例如:

  • stop
  • pause
  • resume
  • update variable

这些命令显然不能通过"直接拿到某个 Python 对象然后调方法"来完成,因为引擎可能正在异步运行,甚至可能跨进程。

13.2 当前的基本思路

api/core/workflow/README.md 和当前目录结构来看,这套机制本质上是:

外部系统 -> command channel -> CommandProcessor -> RuntimeState / GraphExecution

其中 command channel 可以是:

  • InMemoryChannel
  • RedisChannel

这意味着 Dify 在设计上已经把"外部控制入口"和"引擎内部处理逻辑"分离开了。

13.3 这一层的重要性

这不是附属功能,而是 workflow 平台化能力的关键部分。

如果没有 command channel 和 command processor:

  • workflow 就难以被外部可靠中止
  • pause / resume 会很难设计
  • 分布式控制能力会明显受限

14. Layer:引擎级中间件机制

关键文件:

  • api/core/workflow/graph_engine/layers/base.py
  • api/core/workflow/graph_engine/layers/debug_logging.py
  • api/core/workflow/graph_engine/layers/execution_limits.py

14.1 Layer 解决的是什么问题

Workflow 引擎运行过程中,经常会有一些横切需求:

  • 调试日志
  • 执行限制
  • 可观测性
  • 运行监控

如果把这些逻辑硬写进 GraphEngine 主体,会迅速让引擎膨胀。

因此当前设计提供了 Layer 机制。

14.2 Layer 能看见什么

从 README 和已有阅读结果来看,Layer 可以观察:

  • graph start
  • graph end
  • event 流
  • node run start / end
  • 只读 runtime state

这本质上就是引擎级中间件。

14.3 这层的价值

它说明 Dify Workflow 不是只追求"能跑起来",而是在设计上为:

  • 调试
  • 观测
  • 约束
  • 扩展

预留了清晰挂点。

15. import 分层规则说明了什么

api/core/workflow/README.md 里还有一部分很容易被忽略,但非常重要:

  • graph_engine -> graph_events -> graph -> nodes -> node_events -> entities

以及 graph engine 内部的更细分层:

  • orchestration -> command_processing -> event_management -> graph_traversal -> domain

这说明两件事。

第一,作者非常在意分层边界。

第二,这不是"看起来像分层",而是明确尝试用 import 规则约束的分层。

从阅读角度,这能帮助你快速判断:

  • 某个模块如果开始反向依赖下层,通常意味着设计边界被破坏
  • 某个对象该不该直接 import 另一个对象,可以先看它们所在层级

16. 我对当前 Workflow 核心设计的总体理解

16.1 它本质上是一个队列驱动的图执行器

它不是:

  • 递归 DFS 执行器
  • 简单的拓扑排序顺推器
  • 单线程的串行节点解释器

它的执行核心是:

  • ready queue
  • Worker
  • event queue
  • Dispatcher

这几者协作推进。

16.2 它本质上是事件驱动,而不是节点直接互调

节点执行结束后,并不会直接调用下游节点。

它采用的是:

  • 节点产出事件
  • 调度器消费事件
  • 事件处理器更新状态
  • 边处理器计算后继
  • 后继节点重新入队

这使执行流更可观测,也更利于扩展。

16.3 它把"结构""运行态""调度""控制"拆得比较清楚

大致可以记成四块:

  • Graph: 结构
  • GraphRuntimeState: 运行态
  • GraphEngine: 调度装配
  • CommandChannel / Layer: 控制和扩展

这是一种平台型工作流引擎常见但很重要的拆法。

16.4 它从设计上就不是只支持 happy path

从目录和对象划分就能看出,这套引擎原生考虑了:

  • fail
  • partial success
  • fail-branch
  • skip
  • pause
  • abort
  • resume
  • external command

所以阅读时不要只用"成功路径"去理解它。

17. 推荐的源码精读顺序

如果后续真要深读,我建议按下面顺序推进。

17.1 第一轮:先建立整体结构感

  1. api/core/workflow/README.md
  2. api/core/workflow/workflow_entry.py
  3. api/core/workflow/graph/graph.py
  4. api/core/workflow/runtime/graph_runtime_state.py
  5. api/core/workflow/nodes/base/node.py

这一轮的目标不是看完所有细节,而是建立:

  • workflow 是怎么装配起来的
  • Graph 和 RuntimeState 如何分工
  • 节点执行协议长什么样

17.2 第二轮:再读执行引擎

  1. api/core/workflow/graph_engine/graph_engine.py
  2. api/core/workflow/graph_engine/worker.py
  3. api/core/workflow/graph_engine/worker_management/worker_pool.py
  4. api/core/workflow/graph_engine/orchestration/dispatcher.py
  5. api/core/workflow/graph_engine/event_management/event_handlers.py
  6. api/core/workflow/graph_engine/graph_state_manager.py
  7. api/core/workflow/graph_engine/graph_traversal/edge_processor.py

这一轮重点看:

  • ready queue 如何推进
  • event queue 如何被消费
  • 节点完成后如何推进后继

17.3 第三轮:再读外部控制和扩展机制

  1. api/core/workflow/graph_engine/manager.py
  2. api/core/workflow/graph_engine/command_processing/command_processor.py
  3. api/core/workflow/graph_engine/command_channels/redis_channel.py
  4. api/core/workflow/graph_engine/layers/base.py
  5. api/core/workflow/graph_engine/layers/debug_logging.py
  6. api/core/workflow/graph_engine/layers/execution_limits.py

这一轮重点看:

  • stop / pause / resume 命令是如何接入的
  • Layer 如何做观测与约束

17.4 第四轮:最后回到具体节点实现

等前面三轮建立结构感以后,再去读:

  • nodes/llm/
  • nodes/agent/
  • nodes/http_request/
  • nodes/iteration/
  • nodes/loop/
  • nodes/tool/

这时你读到的就不只是"节点业务逻辑",而是"节点如何被纳入整个引擎协议"。

18. 后续最值得继续补的专题

如果这篇导读后续继续扩展,最值得单独写的几篇是:

  • Dify Workflow 节点系统梳理
  • Dify Workflow 执行时序图与事件流笔记
  • Dify Workflow 暂停、恢复与命令通道机制笔记
  • Dify Workflow 变量池与上下文传播笔记

19. 总结

理解 Dify Workflow 的关键,不是记住某个类里写了什么,而是先建立一个判断:

它是一套分层明确、队列驱动、事件驱动、支持外部命令控制的图执行系统。

因此,最合理的阅读方式不是直接陷进某个节点实现,而是先按下面这条主线建立认知:

  1. WorkflowEntry 看装配入口
  2. Graph 看静态结构
  3. GraphRuntimeState 看共享运行态
  4. Node 基类看统一执行协议
  5. GraphEngine 看总调度
  6. Worker + Dispatcher + EventHandler 看执行推进
  7. CommandProcessor + Channel + Layer 看控制与扩展

只要这个主线建立起来,后面再看具体节点、暂停恢复、边规则、变量传播,都会容易很多。

相关推荐
秋名山码民2 小时前
2026 年生成式优化引擎新范式:陕西灵怡秦智科技灵怡云系统深度解析 —— 从流量争夺到语义共鸣的营销变革
人工智能·科技
艾莉丝努力练剑2 小时前
【QT】QT快捷键整理
linux·运维·服务器·开发语言·图像处理·人工智能·qt
源码之家2 小时前
计算机毕业设计:Python智慧交通大数据监控系统 Flask框架 可视化 百度地图 汽车 车况 数据分析 大模型 机器学习(建议收藏)✅
大数据·python·算法·机器学习·信息可视化·flask·课程设计
2601_955363152 小时前
B端拓客号码核验:行业困局拆解与技术升级的理性思考氪迹科技法人号码核验系统、阶梯式价格
大数据·人工智能
帐篷Li2 小时前
阿里林俊旸离职后首发长文:AI从“推理思维“迈向“智能体思维“的五大挑战
人工智能
一次旅行2 小时前
飞书接入龙虾后失联解决方法
前端·人工智能·chrome·飞书
2601_955363152 小时前
技术赋能B端拓客:号码核验行业的破局之路与价值深耕,氪迹科技法人,股东号码核验系统
大数据·人工智能
Th13360702 小时前
技术赋能B端拓客:号码核验行业的困局突破与发展新路径,氪迹科技股东号码筛选系统,阶梯式价格
大数据·人工智能