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
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 used to persist run checkpoints for later inspection/resumption.
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: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
- Validates graph structure
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] 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.
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 — internally it calls validate(auto_fix=False, raise_on_error=False) and only logs a warning. 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: Always compile before running
- Entry/Exit Points: Let auto-detection work unless specific control needed
- Backend Choice: Use Rustworkx for large graphs (>100 nodes)
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