Graph Workflows in ADK: Static Graphs, Routers, and Dynamic Orchestration

Graph engineering in the Agent Development Kit (ADK) is the design work of splitting a task into nodes, connecting them with edges, and deciding where code, models, or people control the next step. Annie Wang and Shangjie Chen use a refund workflow to show how ADK’s Workflow turns that design into an executable process. The example covers parallel lookups, routing, human review, batch processing, and dynamic orchestration, and it explains when a static graph is enough and when Python should schedule further work.

The first pattern is fan-out and fan-in. The order, payment, and refund-history lookups all need the order ID but none depends on another lookup’s result, so they can run in parallel. In ADK, a nested tuple starts them together and a JoinNode waits for their results, returning a dictionary keyed by node name. The next node reads node_input[‘fetch_order’], node_input[‘fetch_payment’], and node_input[‘fetch_history’] without a model call. If one lookup needed another’s output, the edge would be sequential; dependencies determine the edges, even when the prompt lists steps in order. One implementation detail: a parameter named node_input receives the previous node’s output, while other parameter names bind to ctx.state by default.

Routing is the next piece. A deterministic router follows explicit rules; a nondeterministic router can choose different branches for the same input, and an agent router is the model-based version. In the support example, an agent classifies the customer’s intent and returns a structured category, then a small function emits Event(route=…) to send the request to the chosen workflow. Inside the refund workflow, route_refund combines the lookup records and returns AUTO_APPROVE, DENY, or MANUAL_REVIEW based on policy thresholds. That is deterministic and testable without a model. Both routers choose among defined paths, so an agent router can sit inside a static graph. Each route gets a notice agent that writes the customer reply from the case data.

The MANUAL_REVIEW branch pauses for a person. The review node yields RequestInput, which records the pending request and pauses the run. With rerun_on_resume=True, the node runs again on resume and reads the reviewer’s answer from ctx.resume_inputs. The response schema provides an approve value to route on and a note to carry forward. ADK has two variants: the single-node pattern in request_input_rerun, and a two-node pattern in request_input where the reviewer’s answer arrives as the next node’s node_input. A Workflow is also a node, so the whole refund process can become one step in a larger customer-service workflow.

For batches, parallel_worker=True runs one node once per item in an input list and collects results in the original order. The same policy function can be reused for each case, and no JoinNode is needed. The flag also works on agents. The post notes that concurrency is more useful when each item waits on an API or model call, but the way inputs and results move stays the same. The batch size can change without changing the graph.

Dynamic orchestration lets results shape the next step. ADK’s ctx.run_node runs another node and awaits its result. Expressing the refund flow this way uses asyncio.gather for the lookups, Python to combine results and apply the policy, and use_as_output=True to make the handler’s result the parent node’s output without emitting it twice. The outer graph only needs an entry point. When the parent resumes after a human-review pause, completed ctx.run_node calls return their recorded outputs from session history, so lookups do not repeat as direct function calls would; keeping side effects inside child nodes lets completed calls replay their results. use_sub_branch=True keeps concurrent children’s events on separate branches. This version also gives a place to add investigation logic: inspect a result, choose a follow-up node, and run independent checks together. A model might suggest what to investigate while code limits the work, for example three follow-ups per finding and two levels of investigation before human review.

Loops can still fit in a static graph with a conditional back-edge and a router that limits revisions; static describes the possible connections, not the exact path. A dynamic node can also sit inside a static workflow. The decision rule is whether you can draw the possible workflow before the input arrives. Use an edge list when it makes connections clear; use dynamic orchestration when results create further work or Python expresses the control more naturally. A small, open-ended task may need only one agent and its tools. In the refund workflow, the graph coordinates the lookups, code applies the policy, a person handles exceptions, and a model writes the reply. The post points to the workflow guide, workflow samples, and a hands-on codelab that applies these patterns to a marathon race-day coach.

Graph Workflows in ADK: Everything You Need to Know

View Original