Skip to content
Development
Agent

adk-python-workflows

Requires `google-adk >= 2.0.0`. This page documents the Python graph API; ADK Go has its own — see `references/adk-go-workflows.md`. Requires **Python >= 3.11**. The `Workflow` class itself does not support Live Streaming (`Runner.run_live`) — the graph engine needs strict

From plugin
google-agents-cli
5.9k28 skills28 agents
Install
$ npx -y skills add google/agents-cli --agent claude-code

How it fires

How this agent gets triggered: by you, by Claude, or both.

  • Fires itselfAuto-invocation. Claude auto-loads it when your prompt matches the work.Auto-invocation is when the right skill fires by itself at the right moment, driven by a FLOW.md router and a hook, instead of you invoking it by name. It is the difference between a skill being installed and a skill actually getting used.Read the full definition →
  • You can call itInvoke it directly when you want it.

Context preview

The summary Claude sees to decide when to auto-load this agent.

Requires `google-adk >= 2.0.0`. This page documents the Python graph API; ADK Go has its own — see `references/adk-go-workflows.md`. Requires **Python >= 3.11**. The `Workflow` class itself does not support Live Streaming (`Runner.run_live`) — the graph engine needs strict

Agent definition

adk-python-workflows.md

ADK Workflow API Cheatsheet

> Requires `google-adk >= 2.0.0`. This page documents the Python graph API; ADK Go has its own — see `references/adk-go-workflows.md`. > Requires **Python >= 3.11**. The `Workflow` class itself does not support Live Streaming (`Runner.run_live`) — the graph engine needs strict control over event emission. Use a plain `Agent` for live/bidi flows. ADK 2.0 itself still ships `Runner.run_live` and `LiveRequestQueue`.

**Official docs:** [Workflows overview](https://adk.dev/workflows/index.md) · [Graph routes](https://adk.dev/graphs/routes/index.md) · [Collaboration](https://adk.dev/workflows/collaboration/index.md) · [Data handling](https://adk.dev/graphs/data-handling/index.md) · [Dynamic workflows](https://adk.dev/graphs/dynamic/index.md) · [Human-in-the-loop](https://adk.dev/graphs/human-input/index.md)

1. Core Concepts

A `Workflow` is a graph-based agent: nodes do work, edges define flow, `START` is the entry point.

from google.adk.workflow import Workflow

def greet(node_input: str) -> str:
  return f"Hello, {node_input}!"

root_agent = Workflow(
    name="greeter",
    edges=[('START', greet)],
)

Three building blocks: **Nodes** (functions, LLM agents, tools), **Edges** (connections with optional route conditions), **START** (built-in entry receiving user input).

Workflow Constructor

root_agent = Workflow(
    name="my_workflow",
    edges=[...],                # Edge definitions (or use graph= instead)
    description="",             # Agent description
    input_schema=None,          # Pydantic model for input validation
    output_schema=None,         # Pydantic model for the workflow's output
    state_schema=None,          # Pydantic model for state validation
    rerun_on_resume=True,       # Rerun workflow on resume (default: True)
    max_concurrency=None,       # Limit parallel node execution (None = no limit)
    retry_config=None,          # Default RetryConfig applied to nodes
    timeout=None,               # Whole-workflow timeout in seconds
    wait_for_output=False,      # Wait for dynamically scheduled child output
)

---

2. Node Types

Any "NodeLike" is accepted in edges and auto-wrapped:

| Python Object | Wrapped As | Default `rerun_on_resume` | |--------------|-----------|------------------------| | Function/callable | `FunctionNode` | `False` | | `LlmAgent` | Internal `_LlmAgentWrapper` | `True` | | Other `BaseAgent` | Internal `AgentNode` | `False` | | `BaseTool` | Internal `_ToolNode` | `False` | | `BaseNode` subclass | Used as-is | Per subclass |

> **Auto-wrapping is the recommended approach.** Place functions, agents, and tools directly in edges — the framework wraps them automatically. You do not need to import or use internal wrapper classes directly.

---

3. Function Nodes

Most common node type. Parameter resolution:

| Parameter | Source | |-----------|--------| | `ctx` | Workflow `Context` object | | `node_input` | Output from predecessor node | | Any other name | `ctx.state[param_name]` |

from google.adk.agents.context import Context

def process(ctx: Context, node_input: Any, user_name: str) -> str:
  # node_input = predecessor output; user_name = ctx.state['user_name']
  # START outputs types.Content (not str) unless input_schema is set
  return f"{user_name}: {node_input}"

Return Types

  • **Value** -> wrapped in `Event(output=value)`, triggers downstream
  • **`None`** -> no event emitted, no downstream trigger
  • **`Event`** -> used directly (for routing or state updates)
  • **Generator** -> yield multiple events; only the last with `output` triggers downstream
from google.adk.events.event import Event

def classify(node_input: str):
  if "urgent" in node_input:
    return Event(output=node_input, route="urgent")
  return Event(output=node_input, route="normal", state={"processed": True})

Auto Type Conversion

FunctionNode auto-converts `dict` inputs to Pydantic models based on type hints. Works for `list[Model]` and `dict[str, Model]` too.

`node_input` Type by Predecessor

| Predecessor | `node_input` Type | |-------------|-------------------| | Function returning `str`/`dict` | `str`/`dict` | | Function returning `Event(output=X)` | type of `X` | | `LlmAgent` (no `output_schema`) | `types.Content` | | `LlmAgent` (with `output_schema`) | `dict` | | `JoinNode` | `dict[str, Any]` (keyed by predecessor names) | | `ParallelWorker` | `list` | | `START` (no `input_schema`) | `types.Content` | | `START` (with `input_schema`) | parsed schema type |

@node Decorator & Explicit FunctionNode

from google.adk.workflow import node, FunctionNode, RetryConfig

@node
def my_func(node_input: str) -> str:
  return node_input

@node(name="custom", rerun_on_resume=True)
async def my_async(node_input: str) -> str:
  return node_input

# Explicit FunctionNode for full control (func is keyword-only)
fn = FunctionNode(
    func=my_func,
    retry_config=RetryConfig(max_attempts=3),
    timeout=30.0,                          # Seconds before timeout
    parameter_binding='state',             # 'state' (default) or 'node_input'
    auth_config=None,                      # Requires rerun_on_resume=True
    state_schema=None,                     # Pydantic model for state validation
)

---

4. Edge Patterns

# Sequential chain
edges = [('START', a), (a, b), (b, c)]

# Conditional routing (node returns Event with route=)
edges = [
    ('START', classifier),
    (classifier, success_handler, "success"),
    (classifier, error_handler, "error"),
    (classifier, fallback_handler, '__DEFAULT__'),  # Fallback route
]

# Fan-out (parallel branches)
edges = [('START', (branch_a, branch_b, branch_c))]

# Fan-in with JoinNode
from google.adk.workflow import JoinNode
join = JoinNode(name="merge")
edges = [((branch_a, branch_b), join), (join, final)]
# JoinNode output: {"branch_a": output_a, "branch_b": output_b}

# Looping (must have a
Read more
Ships withgoogle-agents-cli

The CLI and skills that turn any coding assistant into an expert at creating, evaluating, and deploying AI agents on Google Cloud.

Get the whole plugin

Other agents on google-agents-cli.