docs/guides/workflow/graph/index.md
In ADK 2.0, workflows are represented as directed graphs where execution flows from node to node along defined edges. This guide explains the core concepts of nodes, edges, and graphs, how to define them, and the validation rules enforced by the framework.
A workflow graph defines the execution plan for your multi-step agent interactions. It specifies:
The graph structure is compiled and validated when you instantiate the Workflow class.
NodeLike)A node represents a single unit of execution in the workflow. In ADK, you can use several types of objects as nodes (collectively referred to as NodeLike):
@node. They are automatically wrapped in a FunctionNode.LlmAgent instances (typically in single_turn mode). They are automatically wrapped in an internal _LlmAgentWrapper.BaseTool instances. They are wrapped in a ToolNode.Workflow is itself a BaseNode and can be nested as a child node in another workflow.START: A special sentinel node that marks the entry point of the workflow. Every graph must have exactly one edge starting from START.Edge)An edge defines a transition from a source node (from_node) to a destination node (to_node).
By default, edges are unconditional. When the source node completes, execution immediately transitions to the destination node.
An edge can be associated with one or more routes (a string, integer, or boolean). The edge is only followed if the source node explicitly emits a matching route.
To emit a route, the source node must yield an Event(route="my_route") (or return/yield an object that maps to that route).
You can define a fallback edge using DEFAULT_ROUTE (imported as from google.adk.workflow import DEFAULT_ROUTE or using "__DEFAULT__"). This edge is followed if the source node emits a route, but no specific conditional edge matches it.
You define the graph structure by passing a list of edges to the Workflow constructor. ADK supports two syntax styles:
Chain tuples provide a concise way to define sequential, parallel, and conditional transitions using Python tuples.
Sequential Chain:
edges=[
(START, step_a, step_b, step_c),
]
This defines: START -> step_a -> step_b -> step_c.
Parallel Fan-Out: Use a tuple of nodes to split execution into parallel branches.
edges=[
(START, step_a, (step_b, step_c)),
]
This defines: START -> step_a, and then step_a -> step_b AND step_a -> step_c in parallel.
Conditional Routing: Use a dictionary (Routing Map) to define conditional branches.
from google.adk.workflow import DEFAULT_ROUTE
edges=[
(START, step_a, {
"success": step_b,
"failure": step_c,
DEFAULT_ROUTE: fallback_step,
}),
]
If step_a yields Event(route="success"), it goes to step_b. If it yields "failure", it goes to step_c. Any other route goes to fallback_step.
For complex graphs or when you prefer explicit declarations, you can use Edge objects:
from google.adk.workflow import Edge, START
edges=[
Edge(from_node=START, to_node=step_a),
Edge(from_node=step_a, to_node=step_b, route="success"),
Edge(from_node=step_a, to_node=step_c, route="failure"),
]
When a Workflow is initialized, it builds an internal Graph representation and runs validate_graph() to catch structural errors early. The following rules are strictly enforced:
All distinct node objects in the graph must have unique names.
process_data, validation will fail.The graph must contain the START node, and START must not have any incoming edges.
START or with an edge pointing back to START will fail validation.All nodes in the graph must be reachable from the START node.
You cannot define duplicate edges between the same two nodes.
Edge(from_node=A, to_node=B) and Edge(from_node=A, to_node=B) in the same list will fail.DEFAULT_ROUTE edge.DEFAULT_ROUTE cannot be combined with other routes in a list (e.g., route=["success", DEFAULT_ROUTE] is invalid).The graph must not contain cycles consisting entirely of unconditional edges (edges with no route).
A -> B -> A where B -> A is conditional on a route).A -> B -> A with no routes) are rejected to prevent infinite execution loops.If a node defines an output_schema and its successor defines an input_schema, they must match exactly.
LlmAgent instances configured with mode='chat' are only allowed to follow the START node.
mode='single_turn'.