A Workflow Is an Executable Decision Graph
Dify's workflow is the clearest place to see how the platform turns model capability into an application. The specific question is how a visual arrangement of steps becomes a running, inspectable process with inputs, transitions, outputs, and failure states. Understanding that path lets the reader read a Dify workflow as executable control flow rather than as a decorative canvas around a prompt.
From a canvas to a running graph
A persisted workflow is represented as a graph: nodes describe units of work and edges describe how execution can move between them. The graph may contain a start point, model calls, transformations, conditions, retrieval, tools, loops, or an answer. That description is still only data. Before it can run, Dify has to turn the stored configuration into objects with the right runtime behavior and give those objects a common execution context.
The first part of that context is the variable pool. It carries the values a run can use: user inputs, system values such as the application and workflow identity, environment variables, uploaded files, and outputs produced by earlier nodes. A later node does not need to reach into an unrelated global object to find those values. It refers to the relevant selectors, and the workflow layer loads or maps them into the pool. This gives the graph a shared vocabulary for passing information while keeping values associated with the run that produced them.
That association matters in the support-assistant example. The employee's question is a run input. The account identity and workflow execution identity are system context. A retrieved policy passage is an output of a knowledge node. A ticket identifier returned by an external tool is another output, with a different meaning from the text the model generated. If all of these were flattened into one prompt string, the application would have difficulty deciding which values may be reused, persisted, or shown to a user. The variable pool does not solve every data-governance problem, but it gives the runtime a place to keep those distinctions visible.
Dify resolves the graph's entry point from the persisted configuration rather than assuming that the first item in a file is the start. Start-capable node types include ordinary starts and certain trigger or data-source entries. Once the root is known, the graph can be initialized with the current run context, workflow identity, variable pool, and call depth. The call-depth value becomes important when one workflow is exposed as a tool to another: composition must have a boundary even when the visible workflow looks simple.
The node factory is the bridge between generic graph machinery and Dify behavior. It loads the external graph-node implementations together with Dify's workflow-local nodes, resolves a node class by type and version, validates the node's data against that concrete class, and constructs it with the runtimes it needs. An LLM node receives a prepared model interface and supporting file or prompt adapters. A tool node receives a Dify tool runtime. An HTTP node receives a configured client and file manager. Human input and Agent nodes receive their own product-aware services.
This construction phase is where the control plane meets the capability plane. The graph says that an LLM step exists, but it does not hard-code a particular vendor into the graph primitive. The factory resolves the model reference and binds the tenant-aware model access used by that run. Similarly, a tool node can remain a node in the graph while the actual tool provider, credentials, runtime parameters, and file handling are resolved outside the graph's visual shape. The workflow remains an application design; capability selection remains a runtime concern.
Dify then creates a GraphEngine with the graph, runtime state, command channel, and execution configuration. The engine can use a bounded worker pool, scale workers according to configured thresholds, and enforce maximum steps and execution time. It can receive commands through an in-memory channel for isolated execution or through channels connected to the running application's control surface. The exact scheduling implementation belongs largely to the external Graphon dependency, but Dify's integration makes the important boundary clear: the application supplies context, policies, and layers around an engine that executes the graph.
That boundary is also why a visual workflow is more than a sequence of API calls. A sequence of API calls can describe the happy path in application code. A persisted graph can be inspected, versioned, debugged, and executed through a common event protocol. It can express branches and repeated structures without forcing every consumer to understand the internals of each node. The price is that graph validity, variable mappings, node versions, and runtime dependencies become part of the application contract. Visual composition reduces some coding work; it does not remove systems engineering.
One execution, several projections
Once the graph starts, the engine emits events as the run and its nodes change state. Dify's application runner receives those events and projects them into queue events suitable for an application or client. At the graph level, the projection can report that a workflow started, succeeded, partially succeeded, failed, was aborted, or paused. At the node level, it can report start, retry, success, failure, exception, streamed text, reasoning chunks, retrieval resources, Agent logs, and loop or iteration progress.
The distinction between an engine event and an application event is useful. The engine knows about graph execution. A person waiting for a support answer needs text chunks and perhaps a final answer. An operator needs node timing, failure information, and the retrieval resources that influenced the response. A client reconnecting after a network interruption needs a coherent event stream, not the private objects used by the scheduler. The queue manager is the translation layer that lets one run serve those different audiences without making each audience couple directly to GraphEngine internals.
Dify also applies a response-stream filter over the engine events. This preserves the response ordering and streaming behavior expected by Dify runners while the underlying Graphon layer can emit lower-level variable stream chunks. A streamed answer is consequently a deliberate compatibility projection. It is not safe to infer that the order in which internal workers finish is automatically the order in which a user should see every piece of output.
The persistence layer observes the same event stream. When a workflow starts, it creates a workflow execution record containing the workflow identity, version, graph data, and prepared inputs. When a node starts, it creates a node execution record with its position, type, predecessor, and status. When the node finishes, it stores inputs, process data, projected outputs, metadata, timing, and any error. Retry events add attempt history rather than silently erasing the previous attempt. At the workflow boundary, token use, step count, exception count, and completion time can be recorded.
This is an execution ledger rather than a transcript. A transcript answers what was said. The ledger can answer which node was running, what state it returned, whether it was retried, and how the whole graph ended. The difference becomes concrete when the support assistant appears to have done nothing. The durable record may show that the retrieval node found no eligible dataset, that the model provider rejected its credentials, that the ticket tool failed after a branch, or that the client simply disconnected while the workflow continued. Those cases look similar in a chat window but require different remedies.
The diagram shows two control directions. Work flows forward from the caller into the graph and then out through the event queue. Control can travel back through the API: a client may reconnect to the stored event path, or a caller may request that execution stop. The stop endpoint sets a stop flag and sends a GraphEngine command. That is cooperative cancellation: the runtime must observe the command and stop at an appropriate boundary. It is not a transaction that rewinds a remote ticket, email, or database write already made by a tool.
Pause is different from stop. A pause records that the workflow cannot continue yet, often because a human-input boundary or another waiting condition needs resolution. The persistence layer saves the current outputs and timing information without treating the run as finished. A reconnecting client can receive the paused state and, where the endpoint permits, a state snapshot. After the required input is available, the workflow can resume with its prior runtime context. The mechanism is designed for supported pause/resume flows; it should not be confused with a guarantee that an arbitrary process crash can reconstruct every in-flight external operation.
Terminal states also carry different meanings. Success says the graph reached its intended completion. Partial success says some work completed while exceptions remained. Failure records that the workflow did not complete normally, while abort or stop identifies an intentional termination path. A node can fail without the whole graph immediately having the same business meaning: a branch may handle an error, a retry may be attempted, or a workflow may finish partially. Treating every non-answer as one generic error discards the distinctions the event and persistence models were built to preserve.
What explicit control buys—and what it costs
An explicit graph makes the stable part of an AI application visible. It can require retrieval before generation, place a condition before a side effect, isolate a tool behind a named node, and make the final answer depend on a known set of outputs. It also makes inspection possible before execution: a team can reason about which branches exist, what a node expects, and where an approval boundary belongs. These properties are valuable when the application is a business process rather than an open-ended conversation.
The graph does not make uncertain operations certain. A model node can still produce an incorrect classification. Retrieval can return weak or irrelevant context. A tool can time out after performing a remote action. A provider can change behavior behind a stable interface. Explicit control narrows the space of possible behavior; it does not remove uncertainty inside each capability or across the external systems that capability touches.
There is also a maintenance cost. A graph must keep its node versions, variable selectors, execution limits, and external dependencies coherent. A branch that is visually obvious may still hide a provider-specific schema or a tool's credential requirement. A workflow that calls another workflow must respect call depth. A long-running graph needs a queue, a ledger, and a client capable of handling streamed or reconnected events. These are not flaws in the model; they are the costs of making an AI application more inspectable than a single prompt call.
The right design question is therefore not whether every task should be converted into the largest possible graph. It is whether the task has enough stable decisions, meaningful side effects, or operational risk to justify explicit control. A short, disposable generation may need little orchestration. A policy assistant that retrieves private information and can create tickets needs much more. Dify's workflow engine is useful precisely where the surrounding decisions deserve a durable shape.
A workflow is consequently both a program and a record of intent. Nodes and edges describe what the application permits; the variable pool carries the data of one run; the factory binds the capabilities available to that run; events describe what happened; and persistence preserves the result. The next question follows naturally. If the graph leaves model choice outside its visual control, how does Dify resolve that choice, protect tenant-specific configuration, and make different kinds of models available to the same execution path?
References
- Workflow entry and GraphEngine integration
- Dify workflow node factory
- Workflow application runner
- Workflow graph topology helper
- Workflow event-to-queue conversion
- Workflow app generation and response conversion
- Workflow persistence layer
- Workflow event reconnect endpoint
- OpenAPI app-run endpoint
- Node.js workflow client
- Node.js SSE parser
- Node.js SDK usage