GraphWorkflow orchestrates agents using a Directed Acyclic Graph (DAG) structure, enabling complex workflows with dependencies, parallel branches, and convergence points. Ideal for sophisticated pipelines with intricate task relationships.
When to Use
- Complex dependencies: Tasks with intricate dependency graphs
- Parallel branches: Multiple independent paths that converge
- Pipeline optimization: Maximize parallelism while respecting dependencies
- Software builds: Compile, test, and deploy pipelines
- Data processing: ETL workflows with multiple stages
Key Features
- DAG-based execution with topological sorting
- Automatic parallelization of independent nodes
- Support for NetworkX and Rustworkx backends
- Fan-out and fan-in patterns
- Entry and exit point management
- Graph visualization (with Graphviz)
- Auto-compilation for performance
- Graph validation and cycle detection
- Nested workflows: a
GraphWorkflowcan run as a single node - Per-layer checkpoints for resuming interrupted runs
- Token usage summed across every node and subgraph
Basic Example
Creating from Spec
Simplified workflow creation:Advanced Edge Patterns
Fan-Out Pattern
One node distributes to multiple nodes:Fan-In Pattern
Multiple nodes converge to one:Parallel Chain Pattern
Full mesh connection:Tuple-Based Patterns
Simplified edge definitions:Key Parameters
str
Name for the workflow
str
Description of the workflow’s purpose
Dict[str, Node]
Dictionary of nodes (agents)
List[Edge]
List of edges (dependencies)
List[str]
Node IDs with no predecessors (auto-detected if not set)
List[str]
Node IDs with no successors (auto-detected if not set)
int
default:"1"
Maximum execution loops
bool
default:"True"
Automatically compile on initialization
str
default:"networkx"
Graph backend (“networkx” or “rustworkx”)
bool
default:"False"
Enable verbose logging
str
Default task used by
run() when no task argument is passed.str
Directory for per-layer checkpoints. Rerunning the same task skips every layer that already has one. See Checkpoints.
Callable[[str, Any], None]
Instance-level callback fired as
(node_id, output) immediately after each node finishes. A callback passed directly to run() takes precedence over this one.int
Caps how many nodes execute concurrently within a layer. Defaults to
max(1, int(get_cpu_cores() * 0.95)) when not set.Methods
add_node()
Add an agent to the graph. The node ID is the agent’sagent_name, and a duplicate ID raises ValueError:
add_node() also accepts a whole GraphWorkflow, which becomes a subgraph node. Pass the agent or workflow itself, not a Node object.
add_nodes()
Add multiple agents concurrently:add_edge()
Add a dependency between nodes:compile()
Pre-compute expensive operations:- Auto-sets entry/exit points
- Computes topological layers
- Caches for performance until the graph changes
- Validates graph structure, logging problems without raising
run() compiles automatically when needed, so calling compile() yourself is optional.
run()
Execute the workflow:run() also accepts on_node_complete (fired as (node_id, output) right after each node finishes, before its layer completes) and streaming_callback (fired as (node_id, token) for every token an agent generates). A callback passed to run() overrides the instance-level on_node_complete set in the constructor.
max_loops == 1 (the default), run() returns a Dict[str, Any] with every node’s output, keyed by node ID. With max_loops > 1, it returns per-loop results keyed as {node_id}_loop_{loop_number} plus the final loop’s results under the plain node_id keys.
A node that raises does not stop the run. Its output becomes an "[ERROR] Agent <name> failed: ..." string and downstream nodes still execute.
Nested workflows
Add aGraphWorkflow as a node to reuse a whole sub-pipeline as one step. Its node ID is the inner workflow’s name. The inner workflow receives the outer node’s prompt as its task, and its results are joined into a single output for downstream nodes.
Token usage
workflow.usage sums provider token usage over every agent in the graph, subgraphs included. An agent that backs several nodes is counted once. Totals are lifetime totals, so they grow across runs. See Token usage.
Checkpoints
Setcheckpoint_dir to write each layer’s outputs to disk as it finishes. If a run crashes, run the same task again and completed layers are restored instead of re-executed:
Use Cases
Software Build Pipeline
Data Science Pipeline
Content Creation Workflow
Entry and Exit Points
Auto-Detection
Manual Setting
Backend Selection
NetworkX (Default)
- Pure Python
- Rich ecosystem
- Extensive algorithms
- Easy debugging
Rustworkx (Performance)
- Rust-based performance
- Faster graph operations
- Lower memory usage
- Better for large graphs
pip install rustworkx
Topological Execution
The workflow executes in topological layers:Graph Validation
Cycle Detection
compile() never raises on a cycle by itself. It runs the same structural checks as validate() and only logs a warning; the cyclic nodes are placed in a final layer. To turn cycle detection into an exception, call validate(raise_on_error=True) explicitly: a detected cycle counts as a “serious warning” that marks the workflow invalid (unless auto_fix=True), so raise_on_error=True will raise ValueError for it.
Dependency Validation
validate() checks for (each reported as a warning or error in the returned dict):
- Referenced nodes existing / valid agent instances on every node (error)
- Isolated nodes (no incoming or outgoing edges)
- Cyclic dependencies (detected via
simple_cycles(), returned underresult["cycles"]) - Unreachable nodes (not reachable from any entry point)
- Dead-end nodes (cannot reach any end point)
- Missing entry points / end points
Performance Optimization
Compilation Caching
Concurrent Node Addition
Best Practices
Graph Design: Keep graphs acyclic - use multiple workflows for iterative processes
- Clear Dependencies: Only add edges for true dependencies
- Maximize Parallelism: Let independent nodes run concurrently
- Compilation: Call
compile()early to surface validation warnings;run()compiles on its own otherwise - Entry/Exit Points: Let auto-detection work unless specific control needed
- Backend Choice: Use Rustworkx for large graphs (>100 nodes)
Graph compilation is cached. Adding nodes or edges, or setting entry or end points, invalidates the cache, and the next
run() recompiles automatically.Error Handling
Visualization
With Graphviz installed:visualize() renders nodes and edges with Graphviz (auto-detecting fan-out/fan-in patterns for clearer styling) and returns the path to the generated file. It raises ImportError if graphviz is not installed. For a dependency-free text view, use workflow.visualize_simple(), which returns an ASCII representation of the graph.
Related Architectures
- Agent Rearrange - Simpler flow patterns
- Sequential Workflow - Linear execution
- Concurrent Workflow - Pure parallel
- Hierarchical Swarm - Director coordination