Skip to main content
The 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:
Compilation:
  • 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.
Return shape: with 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)

Benefits:
  • Pure Python
  • Rich ecosystem
  • Extensive algorithms
  • Easy debugging

Rustworkx (Performance)

Benefits:
  • Rust-based performance
  • Faster graph operations
  • Lower memory usage
  • Better for large graphs
Requires: pip install rustworkx

Topological Execution

The workflow executes in topological layers:
Parallel execution within layers using ThreadPoolExecutor.

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 under result["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
  1. Clear Dependencies: Only add edges for true dependencies
  2. Maximize Parallelism: Let independent nodes run concurrently
  3. Compilation: Always compile before running
  4. Entry/Exit Points: Let auto-detection work unless specific control needed
  5. Backend Choice: Use Rustworkx for large graphs (>100 nodes)
Graph compilation is cached - manually recompile if graph structure changes after initial compilation

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.