aiml
SearchMy learning
Menu

Lesson 13 of 17Module 3

Choose an ADK workflow pattern

Compare ADK workflow patterns by dependencies, control and failure policy, then inspect the real lesson graphs without calling a model.

Prerequisites

The workflow lessons in Module 2 of this course.

By the end, you can

  • Name the three ADK 2.0 workflow types and say who decides the next step in each.
  • Choose a workflow pattern from its dependencies, decision owner and failure policy.
  • Inspect a workflow's graph before it runs, and recognize the deprecated template agents.
Runtime
Python 3.12+ (inspection only; no API key needed)
Dependencies
google-adk, google-genai

Lab code, tests and fixtures (ZIP, 2.9 MB)Quiz check your understanding

Progress is saved in this browser.

When you need this. Your documentation assistant now has several jobs: draft an announcement, check its claims, prepare upgrade steps and ask for approval. Before adding agents, decide which steps depend on each other and who chooses what happens next.

This lesson compares seven workflow patterns using the course’s docuctl documentation tools. You will choose a control structure from the dependencies and failure policy. An optional inspection script lets you compare the actual course graphs and agent trees without calling a model.

ADK 2.0 workflow types · who decides the next step
Graph-basednodeedgeroutematchAgent nodeEdges are fixed and validatedbefore the run.Dynamic@nodePython coderun_nodechildchildCode decides how many stepsrun, as each run goes.CollaborativerootAgentdelegateAgentAgentA model decides which agentto call next.Graph-basednodeedgeroutematchAgent nodeEdges are fixedbefore the run.Dynamic@nodePythonrun_nodechildchildCode decides the steps.CollaborativecoordinatordelegateagentagentA model decides the next agent.
The three workflow types differ in who decides the next step. In a graph, the edges you wrote decide, so a tool can draw and validate the graph before it runs. In a dynamic workflow, your Python code decides at run time. In a collaborative workflow, a coordinator model decides by calling the delegation tools ADK generates for its sub-agents.

The three workflow types

A workflow type says where control lives. A pattern describes how work is arranged. Several patterns can use the same workflow type.

  • Graph. A Workflow declares nodes and edges. Code fixes the available paths; a router selects a named branch. A model can supply a router’s input, so a fixed graph can still take different paths.
  • Dynamic. A @node function starts children with ctx.run_node. Python controls loops, fan-out and error handling when the structure depends on the input.
  • Collaborative. A coordinator agent delegates through sub_agents or AgentTool. The model chooses which specialist to call and what to ask it.

A deprecated template agent. In the pinned SDK, constructing SequentialAgent, LoopAgent or ParallelAgent emits a deprecation warning in favor of Workflow. Use the workflow implementations linked below when adapting an older example. The offline suite exercises those warnings.

The seven patterns

Coordinator and dispatcher

requestrouteagentagent

A dispatcher selects a specialist. Use a function router when the decision follows a rule, such as sending a broken-link report to triage. Use a coordinator Agent when interpreting the request is the task; its model proposes a delegation tool call, and ADK invokes the selected specialist. Compare conditional edges with collaborative delegation. A coordinator Agent can choose the wrong specialist; a rule needs an explicit fallback for unexpected input.

Sequential pipeline

parsegrouprenderoutput → next node's input

Each step needs the previous step’s result. For example, parse a git log, generate grouped release notes, then validate the result. Build a graph workflow implements that sequence. A failed node stops later work; it does not undo earlier effects.

Parallel fan-out and gather

inputagentagentcheckjoin

Independent branches read the same input and produce separate outputs. A join collects them. Use this for highlights, upgrade steps and a link report that can be prepared independently. Parallel fan-out and gather compares a graph that requires every branch with a dynamic parent that keeps results when an optional check fails.

Hierarchical task decomposition

plan_guidewrite_sectionresearch_docscallreturncallreturnAgentTool calls down; text returns up.

A planner delegates a section, and the section writer delegates research. Each call returns its result to its caller. Use this when subtasks need their own instructions and context. Hierarchical task decomposition builds the tree with AgentTool. Depth and repeated delegation both increase work, so the tree needs a depth limit and a shared call budget.

Generate and review

writerdraftreviewerapproverejectpublishsend back

A writer produces a draft; a reviewer compares it with source facts; code accepts or rejects the result. Use it when a draft needs a dedicated check before the caller uses it. Generate and review returns a release announcement or reasons for rejection. There is no automatic revision loop, and a model reviewer can miss an unsupported claim.

Iterative refinement

Agentdraftcheckproblemspass

A failed check returns feedback to a reviser, then the check runs again. Use it when the check can identify a repairable problem. Iterative refinement checks a changelog line’s format and required names. The loop must also stop when its revision limit is reached; more attempts cannot fix contradictory requirements.

Human-in-the-loop

Agentpersonapproverejectapplyrecord

A run pauses for a person’s decision and resumes with the reply. Use it when an action requires a human decision. Pause a workflow for human approval uses RequestInput before a schema migration. The workflow stays paused until a reply arrives, and the reply must be validated before the action continues.

Compare the trade-offs

Requirement Start with Decision to make
Known order and required steps Sequential graph Which failures stop later nodes?
A rule selects the next step Graph router What catches an unmatched route?
Independent outputs Fan-out and join Must every branch succeed?
A draft needs a dedicated check Generate and review What source facts does the reviewer receive?
A check can guide repairs Refinement loop What bounds revisions?
The model must discover subtasks Collaborative tree What bounds depth and total calls?
A person must decide Human input What reply is valid, and how is it resumed?

Choose from the dependency structure first. A fixed sequence does not need a planner. Independent branches do not need to delegate to one another. Keep decisions in code when the rule is already known.

This optional inspection needs Python 3.12+ and the pinned ADK dependencies. Download code contains the inspector, its expected map and tests. The ZIP includes the lesson modules and their documentation under patterns/. Extract it and open a terminal in adk-workflow-patterns/:

Inspect the actual lesson structuresTerminal · bash
python3 -m venv .venv
source .venv/bin/activate
python -m pip install -r requirements-lock.txt
python -W ignore pattern_map.py

pattern_map.py constructs each lesson’s graph or tree, prints its shape and writes pattern-map.json. It sends no user turn and needs no API key. Keep the bundled patterns/ directory beside the inspector.

pattern_map.pySource · pythonDownload
"""Print the structure of every workflow pattern this course builds, without calling a model.

The bundled lesson modules construct the actual course structures without model calls.
"""
import importlib.util
import json
from pathlib import Path

from google.adk import Agent, Workflow
from google.adk.tools import AgentTool

EXAMPLES = Path(__file__).resolve().parent / "patterns"

# Pattern name on adk.dev/workflows/patterns -> (lesson folder, module, builder, workflow type)
PATTERNS = {
    "Sequential pipeline": ("adk-graph-workflow", "release_notes", "build_workflow", "graph"),
    "Coordinator and dispatcher": ("adk-graph-routes", "build_router", "build_workflow", "graph"),
    "Human-in-the-loop": ("adk-human-approval", "migration_gate", "build_workflow", "graph"),
    "Generate and review": ("adk-generate-review", "review_gate", "build_workflow", "graph"),
    "Iterative refinement": ("adk-iterative-refinement", "refine_entry", "build_workflow", "graph"),
    "Parallel fan-out and gather": ("adk-parallel-gather", "release_prep", "build_graph", "graph"),
    "Parallel fan-out, partial results": ("adk-parallel-gather", "release_prep", "build_partial",
                                          "dynamic"),
    "Coordinator with sub-agents": ("adk-sub-agents", "review_team", "build_team", "collaborative"),
    "Hierarchical task decomposition": ("adk-task-decomposition", "upgrade_guide", "build_tree",
                                        "collaborative"),
}


def load(folder: str, module: str):
    """Import a lesson's module under a unique name; every lesson folder is self-contained."""
    spec = importlib.util.spec_from_file_location(f"{folder}.{module}",
                                                  EXAMPLES / folder / f"{module}.py")
    loaded = importlib.util.module_from_spec(spec)
    spec.loader.exec_module(loaded)
    return loaded


def edges(workflow: Workflow) -> list[str]:
    """The graph ADK validated at construction: one line per edge, with its route."""
    lines = []
    for edge in workflow.graph.edges:
        route = f" [{edge.route}]" if edge.route is not None else ""
        lines.append(f"{edge.from_node.name} -> {edge.to_node.name}{route}")
    return lines


def tree(agent: Agent, depth: int = 1, via: str = "") -> list[str]:
    """An agent hierarchy: sub-agents with their mode, and agents wrapped in AgentTool.
    ADK 2.12 also gives a coordinator one AgentTool subclass per single_turn sub-agent;
    those are the sub-agents, so they are listed once."""
    lines = [f"{'  ' * (depth - 1)}{agent.name} (depth {depth}{via})"]
    for sub in agent.sub_agents:
        lines += tree(sub, depth + 1, f", sub-agent, mode={sub.mode}")
    subs = {sub.name for sub in agent.sub_agents}
    for tool in agent.tools:
        if isinstance(tool, AgentTool) and tool.agent.name not in subs:
            lines += tree(tool.agent, depth + 1, ", AgentTool")
    return lines


def describe(name: str) -> dict:
    folder, module, builder, kind = PATTERNS[name]
    built = getattr(load(folder, module), builder)("gemini-3.8-flash")
    shape = edges(built) if isinstance(built, Workflow) else tree(built)
    return {"pattern": name, "type": kind, "lesson_code": f"{folder}/{module}.py", "shape": shape}


if __name__ == "__main__":
    described = [describe(name) for name in PATTERNS]
    for entry in described:
        print(f"{entry['pattern']} ({entry['type']}, {entry['lesson_code']})")
        print("\n".join(f"  {line}" for line in entry["shape"]) + "\n")
    Path(__file__).with_name("pattern-map.json").write_text(json.dumps(described, indent=2) + "\n")

Compare the generate-and-review entry with this saved structure:

Recorded example. The archived “Choose an ADK workflow pattern” demonstration retains the earlier shipctl scenario; the current lab uses docuctl and does not claim a new model result.

pattern-map.json: the review graphRecorded output · jsonDownload
{
  "pattern": "Generate and review",
  "type": "graph",
  "lesson_code": "adk-generate-review/review_gate.py",
  "shape": [
    "__START__ -> read_changelog",
    "read_changelog -> write_announcement",
    "write_announcement -> review_announcement",
    "review_announcement -> gate",
    "gate -> publish [approve]",
    "gate -> send_back [reject]"
  ]
}

The refinement graph has a back-edge from revise_entry to check_entry. The parallel graph has several edges into gather. The hierarchical entry is an agent tree rather than a list of graph edges. These shapes tell you how control can flow; they do not tell you whether generated text is useful.

A node that no edge reaches. Constructing an invalid Workflow raises a Pydantic ValidationError that identifies unreachable nodes.

Test the map

Check the structures without a modelTerminal · bash
python -W ignore -m unittest -v test_pattern_map.py

The tests reconstruct the real lessons and compare them with the saved map. They also exercise invalid graph construction and the deprecated template warnings. They check topology, not generated answers.

Choose before you implement

Suppose the release page needs highlights and upgrade steps, but the link report is optional. Choose a pattern and write down its failure policy before reading on. A dynamic fan-out parent fits: both writing branches are required, while a failed link check becomes an explicit warning. In this course’s plain join example, an uncaught link-check error fails the whole run. A graph could instead catch that error inside the optional branch and return a warning for the join.

Continue with Generate and review to build a writer, a reviewer and a code gate.

Highlights and upgrade steps are required, but a link check is optional. Which design makes that policy explicit?

Choose one answer, then check your reasoning.

Read the explanation

Choose the control structure from dependencies and failure policy before adding a model decision.

References

Written by Arjun Prabhulal.

Module 3

Workflow patterns