datarekha

ADK workflow agents — sequential, parallel, loop

ADK gives you SequentialAgent, ParallelAgent, and LoopAgent for multi-agent orchestration. Here's a research → write → review pipeline.

7 min read Advanced Agentic AI Lesson 47 of 71

What you'll learn

  • The three workflow agents — Sequential, Parallel, Loop
  • When workflow agents beat a single LlmAgent with tools
  • State sharing between sub-agents via session state
  • A research → write → review sequential pipeline

Before you start

A team building a competitive-intel agent shoved everything into a single LlmAgent: scrape three sources, dedupe, summarize, score confidence, format. The prompt grew to a multi-page spec; outputs started drifting because the model couldn’t keep all the personas straight. Splitting it into a SequentialAgent of focused LlmAgents — one per stage — fixed the regressions overnight and made each stage debuggable on its own. That’s the win ADK workflow agents are built for.

When one LlmAgent with tools isn’t enough, ADK gives you workflow agents — three built-in orchestration classes, each of which wraps a list of sub-agents and runs them in a fixed pattern. Unlike MAF’s WorkflowBuilder (which requires explicitly wiring every edge in a graph), ADK’s approach is declarative: you pick the right class for the shape you need, pass in the sub-agents, and the orchestration is implied. This keeps simple pipelines simple while still composing into arbitrary shapes.

The three you’ll use:

ClassRuns sub-agents…
SequentialAgentOne after another, output of N feeds input of N+1
ParallelAgentAll at once, concurrent
LoopAgentRepeatedly, until a sub-agent signals stop

You compose them — a SequentialAgent can contain a ParallelAgent which contains LlmAgents — to build arbitrary shapes.

SequentialAgentlinear pipelineresearcherwriterrevieweroutput_key feeds next stageParallelAgentfan-out, fan-ininputfinancelegalmarketsynthesizerindependent sub-agents onlyLoopAgentuntil escape signaldraftercriticSTOP / escalatecap with max_iterationsThree declarative shapes — compose them to build arbitrary pipelinessub_agents communicate via session state, not return values

ADK’s three workflow agent classes. Each runs its sub_agents in a fixed shape; state passes between them via output_key / template substitution.

A research → write → review pipeline

The canonical 3-agent shape: a researcher gathers facts, a writer drafts based on the research, a reviewer critiques. Sequential is the right primitive.

# requires: pip install google-adk
# Shape only — google-adk needs a real Python process and Gemini access; read it as reference code.

from google.adk.agents import LlmAgent, SequentialAgent

# Each sub-agent has its own instructions. Specialize, don't generalize.
researcher = LlmAgent(
    name="researcher",
    model="gemini-2.5-flash",
    instruction=(
        "You receive a user topic. Produce 3-5 bullet points of relevant "
        "facts. Be specific; cite reasoning, not vibes. "
        "Save your output to the session state under key 'research'."
    ),
    output_key="research",  # writes the final response into session state
)

writer = LlmAgent(
    name="writer",
    model="gemini-2.5-flash",
    instruction=(
        "You receive bullet-point research in session state under "
        "{research}. Write a 2-paragraph executive summary based on it. "
        "Do not invent facts beyond what's in research."
    ),
    output_key="draft",
)

reviewer = LlmAgent(
    name="reviewer",
    model="gemini-2.5-flash",
    instruction=(
        "Review the draft in session state under {draft}. Check it against "
        "the research in {research}. Respond with exactly: "
        "APPROVED — <one-line reason>  OR  REVISE — <one-line reason>."
    ),
    output_key="review",
)

# The pipeline. SequentialAgent runs sub_agents in order.
pipeline = SequentialAgent(
    name="research_write_review",
    sub_agents=[researcher, writer, reviewer],
)

# Run via a Runner (see adk-intro):
#   from google.adk.runners import Runner
#   from google.adk.sessions import InMemorySessionService
#
#   runner = Runner(agent=pipeline, app_name="lessons",
#                   session_service=InMemorySessionService())
#   session = await runner.session_service.create_session(
#       app_name="lessons", user_id="u_1"
#   )
#   events = runner.run_async(
#       user_id="u_1", session_id=session.id,
#       new_message="Why did Q1 churn drop at our company?",
#   )

print("Shape only — needs Gemini API to actually run.")

The new thing here: output_key. When a sub-agent finishes, its final response is written into session state under that key. Downstream sub-agents reference it in their instructions with {research} or {draft} — ADK substitutes the value from state. This is how information flows between stages in a workflow.

ParallelAgent — fan out and merge

When subtasks are independent, run them concurrently:

from google.adk.agents import ParallelAgent

finance_researcher = LlmAgent(name="finance", model="gemini-2.5-flash",
                              instruction="...", output_key="finance_notes")
legal_researcher   = LlmAgent(name="legal",   model="gemini-2.5-flash",
                              instruction="...", output_key="legal_notes")
market_researcher  = LlmAgent(name="market",  model="gemini-2.5-flash",
                              instruction="...", output_key="market_notes")

# All three run concurrently; each writes to its own state key.
researchers = ParallelAgent(
    name="researchers",
    sub_agents=[finance_researcher, legal_researcher, market_researcher],
)

synthesizer = LlmAgent(
    name="synthesizer",
    model="gemini-2.5-flash",
    instruction=(
        "Combine these into a unified brief: "
        "Finance: {finance_notes}\\nLegal: {legal_notes}\\nMarket: {market_notes}"
    ),
)

# Wire the parallel block into a sequence:
brief_pipeline = SequentialAgent(
    name="brief",
    sub_agents=[researchers, synthesizer],
)

The pattern: parallel for the independent work, then sequential for the merge step. The synthesizer reads all three state keys and produces the combined output.

LoopAgent — refinement until satisfied

LoopAgent re-runs its sub-agents until one of them signals stop. The canonical use: a generator/critic loop that refines until the critic says it’s good enough.

from google.adk.agents import LoopAgent

drafter = LlmAgent(
    name="drafter",
    model="gemini-2.5-flash",
    instruction=(
        "Draft an answer to {user_question}. If a {critique} exists in "
        "state, revise the previous draft to address it."
    ),
    output_key="draft",
)

critic = LlmAgent(
    name="critic",
    model="gemini-2.5-flash",
    instruction=(
        "Critique {draft}. If acceptable, respond exactly: STOP. "
        "Otherwise return one specific improvement."
    ),
    output_key="critique",
)

# LoopAgent stops when a sub-agent signals escalation (the exact API —
# likely setting escalate=True on an EventActions object — check current
# ADK docs) or when max_iterations is hit. Always cap.
refinement_loop = LoopAgent(
    name="refine",
    sub_agents=[drafter, critic],
    max_iterations=4,
)

The critic can signal stop either by emitting a specific token the drafter inspects, or via ADK’s escalation mechanism — refer to current docs for the exact API.

State is the contract between sub-agents

In ADK workflows, session state is the data bus. Sub-agents read state via {key} substitution in their instructions and write to state via output_key. That’s the only way they communicate.

This is a deliberate constraint. It means:

  • Each sub-agent has a focused prompt. It doesn’t need to see upstream agents’ full chains-of-thought; it sees the artifacts they produced.
  • The state object is your reasoning trace. If the workflow fails, inspecting state shows you exactly what each stage produced.
  • You can persist state across runs. Replace InMemorySessionService with a backed one and your workflow becomes resumable.

The shapes compose to fit it: a ParallelAgent runs the three independent checks at once, a SequentialAgent feeds their merged findings into a verdict step, and wrapping the whole thing in a LoopAgent lets it revise and re-check until the verdict is “approved” (capped, of course, by max_iterations). One sentence, three nested shapes — that compositionality is the point.

When to choose ADK workflows over a single LlmAgent

The test is the same as MAF: don’t build a workflow until a single agent demonstrably fails. Workflows add latency (you make N model calls instead of one), cost (N × tokens), and debugging surface (N prompts to tune). They earn their keep when:

  • Roles genuinely differ (a writer needs different temperature/style than a critic).
  • You want auditable stages (each output_key is a checkpoint).
  • A loop is the right shape (refinement against a quality bar).

When you can describe the task in one sentence with one tool list, use one LlmAgent and stop there.

In one breath

  • When one LlmAgent strains under too many personas, split it into workflow agents — declarative orchestration classes, each wrapping a list of sub_agents.
  • Three shapes: SequentialAgent (pipeline, N feeds N+1), ParallelAgent (independent work, run concurrently), LoopAgent (refine until a stop signal) — and they compose into arbitrary pipelines.
  • Sub-agents communicate only through session state: write with output_key, read with {key} substitution in instructions — so each prompt stays focused and the state object is your audit trail.
  • ParallelAgent is for genuinely independent sub-agents; if one needs another’s output, it’s sequential. Always cap LoopAgent with max_iterations.
  • Don’t build a workflow until a single agent demonstrably fails — they cost N model calls, N× tokens, and N prompts to tune. They earn it when roles truly differ, stages must be auditable, or a refinement loop is the right shape.

Quick check

Quick check

0/3
Q1How do ADK sub-agents pass information to each other in a workflow?
Q2When is ParallelAgent the right choice?
Q3Why cap LoopAgent with max_iterations?

Next

You’ve seen how to build ADK agents and workflows. The last lesson in the ADK series covers deployment — the three paths from local code to production: Agent Engine, Cloud Run, GKE.

Sign in to track your progress

Completed lessons, your XP, level, and streak save to your account — it's free and takes a few seconds.

Practice this in an interview

All questions
What is an AI agent, and how does it differ from a single LLM call?

An agent is an LLM placed in a loop where it reasons, chooses and calls tools or actions, observes the results, and repeats until a goal is met, rather than producing one response and stopping. The key differences are autonomy, tool use, memory and state, and multi-step control flow driven by the model's own decisions.

When should you use a multi-agent system versus a single agent, and what is the supervisor versus swarm pattern?

Use multiple agents when a task decomposes into distinct specialties or parallel subtasks that exceed one agent's context or reliability; avoid it when a single agent suffices, since multi-agent systems add coordination overhead, latency, cost, and error propagation. A supervisor architecture has an orchestrator routing work to specialized sub-agents, while a swarm lets peer agents hand off control to one another without a central coordinator.

Why use a pipeline orchestrator like Airflow or Kubeflow instead of cron scripts for ML workflows?

ML workflows are multi-step DAGs with dependencies, and an orchestrator gives you dependency management, retries, backfills, caching, observability, and lineage that chained cron jobs cannot. Airflow is a general-purpose task orchestrator defining DAGs in Python, while Kubeflow Pipelines is ML-native, passing typed artifacts between containerized steps on Kubernetes with conditional logic like deploy only if accuracy exceeds a threshold. Choosing depends on whether you need generic scheduling or ML-specific, container-based pipelines.

Explain the ReAct agent pattern and how it compares to Plan-and-Execute and Reflexion.

ReAct interleaves reasoning traces with actions step by step, deciding the next tool call based on the latest observation. Plan-and-Execute first drafts a full multi-step plan and then executes it, which is more efficient and predictable for complex tasks but less adaptive, while Reflexion adds a self-reflection step where the agent critiques past failures and retries with that feedback.

Related lessons

Explore further

Skip to content