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
  • Nested workflows: a GraphWorkflow can 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’s agent_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:
Compilation:
  • 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.
Return shape: with 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 a GraphWorkflow 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

Set checkpoint_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:
Checkpoints are keyed on the exact task string, so a different task starts from scratch.

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. 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 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: Call compile() early to surface validation warnings; run() compiles on its own otherwise
  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. 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.