Why architecture matters here

Architecture matters here because the failure modes of autonomous agents — nondeterminism, runaway loops, skipped steps — are exactly the failure modes that workflow agents eliminate by construction. If your task genuinely has a fixed shape (gather, then analyze, then write; or fan out to three researchers, then merge), encoding that shape in a workflow agent makes the shape a guarantee rather than something you hope the LLM chooses. The reliability difference is not marginal: a coded pipeline runs the same way every time, which is what production systems need.

The problem workflow agents solve is that pure LLM orchestration does not compose predictably. If you ask a single agent to 'research this topic thoroughly and write a report,' the model decides how many searches to run, whether to analyze before or after gathering, and when it is done — and it may decide differently each run. For a demo that is fine; for a system that must behave consistently, bill predictably, and be testable, that variability is a liability. Workflow agents pull the structural decisions out of the model and into code, leaving the model to do what it is good at inside each step.

Consider a concrete pipeline to feel the value. A document-processing task needs to: extract entities, then in parallel run sentiment and topic classification on the extracted text, then synthesize a summary. Expressed as a SequentialAgent wrapping [extractor, ParallelAgent[sentiment, topic], synthesizer], the control flow is explicit and guaranteed — extraction always precedes classification, the two classifiers always run concurrently, synthesis always runs last. Each node is still an LLM agent free to reason, but the pipeline's skeleton is code, so it is deterministic and unit-testable.

The LoopAgent addresses a different but equally common need: iterative refinement with a controlled exit. A 'generate a draft, critique it, revise it, repeat until good enough' pattern is naturally a loop, but a naive LLM loop has no reliable stop — it may declare victory too early or never. LoopAgent runs its body repeatedly and terminates only when a sub-agent signals escalation (or a max-iteration bound is hit), giving you the iterative behavior with a hard guarantee that it will not spin forever. The exit condition is explicit and bounded, not left to the model's judgment alone.

The payoff of this architecture is that complex behavior is built from small, composable, individually testable pieces. Because a workflow agent is itself an agent, a SequentialAgent can contain a ParallelAgent that contains a LoopAgent that contains LLM agents — arbitrary nesting of deterministic structure around intelligent nodes. You can test each sub-agent in isolation, test each workflow agent's control flow with stub sub-agents, and trust that composing them preserves both the determinism of the structure and the flexibility of the leaves. That composability is what lets ADK scale from a single agent to an elaborate multi-stage system without the whole thing becoming unpredictable.

Advertisement

The architecture: every piece explained

Top row: the three orchestration primitives and their leaves. SequentialAgent holds an ordered list of sub-agents and runs them strictly in order, waiting for each to finish before starting the next; state written by an earlier sub-agent is visible to later ones. ParallelAgent holds a list of sub-agents and runs them concurrently, waiting for all to complete before it returns; each branch reads the shared state as it was at fan-out and writes its own output. LoopAgent holds a body (one or more sub-agents) and runs it repeatedly, checking after each iteration whether to continue. The sub-agents at the leaves are ordinary ADK agents — usually LLM agents with tools, but possibly nested workflow agents.

Middle row: the data and control channels. Session state is the shared key/value store threaded through the whole invocation; it is how sub-agents communicate without referencing each other. output_key is the declaration on a sub-agent that says 'write my result to this state key,' which a downstream sub-agent then reads — this is the wiring that turns a list of agents into a pipeline that passes data along. Escalation is the control signal a sub-agent raises to tell an enclosing LoopAgent to stop iterating; it is how a critic agent inside a refinement loop says 'this draft is good enough, we are done.'

Bottom row: execution and persistence. Everything the workflow does emits into one ordered event stream — each sub-agent's actions, tool calls, and state changes appear as events in causal order, giving a single replayable log of the whole run. The runner is what actually drives execution: it invokes the top-level workflow agent, pumps the event stream, and persists session state and events so a long-running or resumable workflow can survive across turns. The runner is the boundary between the workflow's logic and the outside world.

The architectural crux is that control flow lives in the workflow agents and data flow lives in session state, and the two are cleanly separated. A SequentialAgent does not know what its sub-agents compute; it only knows to run them in order. The sub-agents do not know they are in a sequence; they only read and write state keys. This decoupling is what makes the primitives composable — any sub-agent can be swapped, nested, or reordered without the others changing, because they coordinate only through shared state, not through direct calls.

The ops strip is the health surface. Step latency shows how long each stage of a sequential or parallel workflow takes, locating the slow node. Loop iterations counts how many times a LoopAgent ran before escalating — a number that should be bounded and is the leading indicator of a refinement loop that is not converging. Branch fan-out shows how wide a ParallelAgent spread, which drives concurrent resource use. State size per session tracks how much context is accumulating, since an unbounded-growth loop can bloat session state until it becomes the bottleneck.

ADK workflow agents — deterministic orchestration primitivesSequential, Parallel, and Loop agents compose sub-agents with predictable control flowSequentialAgentrun sub-agents in orderParallelAgentrun sub-agents concurrentlyLoopAgentrepeat until conditionSub-agentsLLM or nested workflowSession stateshared key/value contextoutput_keywrite result to stateEscalationsignal to stop a loopEvent streamone ordered event logRunnerdrives execution + persistsOps — step latency, loop iterations, branch fan-out, state size per sessionwritecomposeinvokeemitstorestopdriveobserveobserve
ADK workflow agents are deterministic orchestrators: SequentialAgent runs sub-agents in order, ParallelAgent runs them concurrently, and LoopAgent repeats until an escalation signal — all sharing session state through output_key and emitting one ordered event stream that the runner drives and persists.
Advertisement

End-to-end flow

Trace a research-and-refine workflow through the runner. The top-level agent is a SequentialAgent with three children: a ParallelAgent of two researchers, a synthesizer, and a LoopAgent that refines the synthesis. The runner receives the user task, opens a session with empty state, and invokes the SequentialAgent, which begins its first child.

The ParallelAgent runs. It fans out to a web-research sub-agent and a knowledge-base sub-agent concurrently. Each reads the user task from session state, does its work (tool calls, LLM reasoning), and writes its findings to its declared output_key — say web_findings and kb_findings. The ParallelAgent waits for both to finish, then returns; session state now holds both findings keys. Every action from both branches has been emitted to the shared event stream in order.

The SequentialAgent moves to its second child, the synthesizer. It reads web_findings and kb_findings from state, produces a combined draft, and writes it to draft via its output_key. Because the sequential agent guarantees ordering, the synthesizer is certain both findings keys are populated before it runs — the pipeline structure makes the data dependency safe without any explicit synchronization.

The SequentialAgent moves to its third child, the LoopAgent. Its body is [reviser, critic]. Iteration one: the reviser reads draft, improves it, writes it back; the critic reads the revised draft and judges it. If the critic is not satisfied, the loop continues to iteration two with the improved draft. When the critic decides the draft meets the bar, it raises an escalation signal; the LoopAgent sees the signal after that iteration and stops. Had the critic never been satisfied, the LoopAgent's max-iteration bound would have stopped it anyway, guaranteeing termination. The final draft sits in session state, the SequentialAgent returns, and the runner delivers the result.

Notice how session state carried the whole conversation between stages without any sub-agent holding a reference to another. The researchers wrote web_findings and kb_findings; the synthesizer read those exact keys and wrote draft; the reviser and critic read and rewrote draft across iterations. At no point did the synthesizer call the researchers or the critic call the reviser directly — they communicated entirely through the shared key/value context that the runner threads through the invocation. This indirection is what makes the pieces swappable: you could replace the web researcher with a different data source, add a third parallel branch, or insert another sequential stage, and as long as the keys line up, no other sub-agent needs to change. The workflow agents guarantee the control flow while session state carries the data, and the two concerns never entangle.

Step back and count what the composition guaranteed across that run. The two researchers definitely ran concurrently and both definitely finished before synthesis, because ParallelAgent enforced the barrier. Synthesis definitely ran after research and before refinement, because SequentialAgent enforced the order. Refinement definitely iterated but definitely terminated, because LoopAgent enforced the escalation-or-bound exit. Data flowed cleanly from stage to stage through session state via output_key, with no sub-agent needing a reference to any other. And the whole run produced one ordered event stream that the runner persisted, so it is replayable and resumable. Every one of those guarantees came from the deterministic control-flow primitives wrapping the nondeterministic LLM leaves — exactly the separation the architecture is designed to provide.