One agent, one system prompt, nineteen tools bolted on. It demos beautifully. Then a customer asks for a refund and a delivery date in the same message, the model reaches for the wrong tool, and you spend the afternoon adding another paragraph to a prompt that is already too long for the model to hold in its head. Every fix makes the next fix harder. The prompt is not the problem. The shape is.
What splitting the agent actually buys you
A single agent degrades in a predictable way as you add tools. The instruction grows to cover every branch, the model has more chances to pick the wrong tool on any given turn, and you cannot test one capability without dragging the rest along. Splitting the work fixes all three. Each specialist agent carries a short instruction and three or four tools instead of twenty, so it stays reliable. A coordinator sits on top and decides who handles what. You test the returns agent on returns questions and leave the billing agent out of it entirely.
The cost is real and worth naming up front. More agents means more model calls per request, more latency, and more moving parts to trace when something goes wrong. A two-agent hop can double your token spend on a turn that a single agent would have answered directly. So the split is not free, and it is not always right. It earns its keep when one agent has grown past roughly half a dozen tools or when distinct capabilities need different models, different owners, or different guardrails. Below that line, keep one agent and save yourself the orchestration.
ADK in one paragraph
The Agent Development Kit is Google’s open-source framework for building agents in code. It reached 1.0, its first stable release, and now ships in Python, Java, Go, and TypeScript with the same core concepts across all four, so an agent prototyped in Python ports to a Java backend without rewriting the logic. ADK runs on your laptop for development and deploys to any container or to Vertex AI Agent Engine for production. The building block is an agent object: you give it a name, a model, an instruction, and a list of tools or sub-agents. Everything in this part is a way of composing those objects. It is not tied to Gemini alone, but on Google Cloud the default pairing is ADK plus Gemini plus Agent Engine, and that is the path I will follow.
Coordinator and sub-agents, the LLM-driven split
The most common pattern is a coordinator: a central LlmAgent, an agent whose control flow is driven by a language model, that reads each request and routes it to the right specialist. In ADK you build it by assigning specialists to the coordinator’s sub_agents list. The framework wires the parent-child relationship and exposes a transfer_to_agent action the model can call to hand control to a named sub-agent. The coordinator does not answer the question itself. Its whole job is to pick the specialist and step aside.
This is LLM-driven delegation, and it shines when you cannot predict the path. A support request might be about billing, orders, or returns, and the coordinator reads the language to decide. The trade is that the model makes the routing call, so routing is only as good as the instruction and the sub-agent descriptions. Give each specialist a one-line description of what it handles, because the coordinator reads those descriptions to choose. Vague descriptions produce vague routing. This is the same delegation idea as Amazon Bedrock’s supervisor agents on the AWS side, covered in the AWS multi-agent part, if you run both clouds.
flowchart TD U[User request] --> C[Coordinator LlmAgent] C --> B[Billing sub-agent] C --> O[Orders sub-agent] C --> R[Returns sub-agent] R -->|A2A call| W[Partner warehouse agent] B --> C O --> C
In practice
Give the coordinator a small, cheap-to-run model and the specialists whatever they need. Routing is a short classification task, so a fast model handles it well, and you do not want to pay reasoning-model prices just to pick a branch. Put the expensive model behind the one specialist that actually does hard reasoning. I have watched teams run their most costly model as the router and wonder where the bill went.
When should the flow be deterministic?
Letting the model route is the right call when the path is genuinely open. When you already know the order of steps, do not make the model rediscover it every turn. ADK gives you three workflow agents that run sub-agents on a fixed structure, no model reasoning involved in the control flow. SequentialAgent runs its sub-agents one after another, passing state down the chain, which suits a pipeline like extract, then summarize, then translate. ParallelAgent runs its sub-agents at the same time and gathers the results, which suits independent lookups that do not depend on each other. LoopAgent repeats a sub-agent until a condition holds, which suits a draft-and-critique cycle that runs until a reviewer approves.
The difference matters for cost, latency, and testability. A deterministic workflow is cheaper because the structure is free; no model call decides what runs next. It is faster because you skip the routing hop. And it is far easier to test, because the same input always takes the same path. My rule is simple: if you can draw the flow as a fixed diagram, use a workflow agent. If the next step depends on reading the user’s intent, use an LlmAgent to decide. Most real systems mix both, an LLM coordinator on top with deterministic pipelines underneath.
| Agent type | Control flow | Reach for it when |
|---|---|---|
| LlmAgent | Model decides the next step | Path depends on reading intent |
| SequentialAgent | Fixed order, one after another | A known pipeline of steps |
| ParallelAgent | Concurrent, results merged | Independent lookups at once |
| LoopAgent | Repeat until a condition holds | Draft, critique, revise cycles |
| AgentTool | Parent calls agent as a function | Reusable skill, keep control |
The latency gap between sequential and parallel is not academic. Take three specialist lookups that do not depend on each other, each averaging about 4.2 seconds. Run them in a SequentialAgent and the wall-clock time is their sum. Run them in a ParallelAgent and it is roughly the slowest one plus a small merge. Same work, very different wait.
Agent as a tool
There is a third way to compose agents that sits between delegation and workflow. AgentTool wraps an agent so its parent can call it like a function, with structured input and output, and keep control instead of handing it off. The difference from a sub-agent is who holds the conversation. With transfer_to_agent, the coordinator hands the user to the specialist and steps back. With AgentTool, the parent stays in charge, calls the wrapped agent to get one specific result, and carries on. Reach for AgentTool when a capability is a reusable skill, say a currency converter agent or a summarizer, that the parent needs mid-thought rather than a destination to route the whole request to.
The practical tell is whether control should come back. If the specialist owns the rest of the exchange, make it a sub-agent. If the parent needs an answer and then continues its own reasoning, wrap it as a tool. Getting this backwards is a common early mistake: teams route to a sub-agent for what should have been a quick tool call, then wonder why the coordinator lost the thread.
Here is the coordinator pattern in ADK Python. Two specialists, one router, wired through sub_agents.
from google.adk.agents import LlmAgent
billing = LlmAgent(
name='billing',
model='gemini-2.5-flash',
description='Handles invoices, charges, and refunds.',
instruction='Answer billing questions only. Never guess an order status.',
)
orders = LlmAgent(
name='orders',
model='gemini-2.5-flash',
description='Tracks and updates order and delivery status.',
instruction='Answer order and delivery questions only.',
)
coordinator = LlmAgent(
name='coordinator',
model='gemini-2.5-flash',
instruction='Route each request to the right specialist. Do not answer yourself.',
sub_agents=[billing, orders],
)Expected behaviour: a billing question makes the coordinator emit a transfer_to_agent call to billing, which answers; an order question routes to orders. Failure mode: drop the "Do not answer yourself" line and the coordinator often answers directly from its own knowledge, the sub-agents never fire, and routing silently disappears. Model IDs shown are current 2.5-series strings.
How A2A connects agents across frameworks
Everything so far assumes the agents live in one codebase. Real organizations do not work that way. The returns team might run their agent on a different stack, or a supplier might expose an agent you do not control. Agent2Agent, or A2A, is the open protocol for that case: a thin, framework-agnostic way for one agent to delegate to another over the network, regardless of what each is built on. It is to agents roughly what HTTP is to web servers. Google contributed it, and it is now an open project under the Linux Foundation, stable at v1.0 with more than 150 organizations running it in production.
The mechanics are worth knowing at a glance. Each A2A agent publishes an Agent Card, a small signed document that says what it does, where it lives, and how to authenticate. A client agent reads the card, then sends tasks and receives results or streamed updates. Inside your own ADK app you use sub_agents and AgentTool; across an organizational or cloud boundary you use A2A. The line between them is ownership. If you own the code and it deploys together, keep it in-process. If it belongs to another team or vendor, reach it over A2A rather than trying to import it.
Where the agents run in production
ADK runs your agent locally with a dev server and a web UI, which is enough to build and debug. Production is a different problem: you need a runtime that scales, holds conversation state, and does not fall over when traffic arrives. Vertex AI Agent Engine is Google’s managed answer, a runtime that hosts an ADK agent behind a managed endpoint so you do not run the servers yourself. You hand it the agent object and a requirements list, it packages and deploys, and you get a scalable endpoint with tracing built in.
Two managed pieces make it usable for real conversations. Sessions store the back-and-forth of a single conversation so the agent remembers what was said a moment ago. Memory Bank is a managed store for longer-term memory across sessions, generating and retrieving what matters about a user over time. Both went generally available this year alongside a regional expansion, so you can keep data in an EU, Asia, or Americas region as needed. Build on your laptop, deploy to Agent Engine, and let Sessions and Memory Bank carry the state you would otherwise wire up by hand.
from vertexai import agent_engines
# coordinator is the LlmAgent defined earlier
remote_app = agent_engines.create(
agent_engine=coordinator,
requirements=['google-cloud-aiplatform[adk,agent_engines]'],
)
print(remote_app.resource_name)Expected: the call returns after a few minutes with a resource name you query for the deployed endpoint. Common failure: a dependency your agent imports is missing from the requirements list, so the build succeeds but the remote agent errors on first call. Pin every import your agent needs in requirements, not just the ADK extras.
What Agent Engine costs
Agent Engine bills the runtime by the resources it holds, not per request. The runtime is roughly $0.0864 per vCPU-hour and $0.0090 per GB-hour of memory, with a monthly free tier of about 50 vCPU-hours and 100 GB-hours. Model tokens are billed separately under the Gemini SKUs, and if you use the managed state, Sessions and Memory Bank add storage and read charges on top. The lever you control is how much compute you provision and whether the service stays always-on or scales down when idle.
Worked example
Run the coordinator plus two specialists as one always-on Agent Engine deployment sized at 2 vCPU and 4 GB, over a 730-hour month. Compute is 2 times 730 times $0.0864, about $126 for vCPU, plus 4 times 730 times $0.0090, about $26 for memory. That is roughly $152 a month gross; the free tier trims about $5, leaving near $147 for the runtime before any model tokens.
Double the size to 4 vCPU and 8 GB and the runtime roughly doubles to about $305. So the design call is concrete: right-size the container to real concurrency rather than provisioning headroom you never use, and let idle traffic scale down where the workload allows. The token bill, not the runtime, is usually the bigger number, which is exactly why a cheap router model matters.
| Component | Rough rate | Billed on |
|---|---|---|
| Runtime vCPU | ~$0.0864 per vCPU-hour | Provisioned compute time |
| Runtime memory | ~$0.0090 per GB-hour | Provisioned memory time |
| Sessions and memories | ~$0.25 per 1,000 stored | Stored events and memories |
| Memory Bank storage | ~$0.30 per GiB-month | Retained memory data |
| Model tokens | Per the Gemini SKU | Every model call, separately |
Failure modes I keep hitting
The first is over-splitting. Not every capability deserves its own agent. If two specialists always run together and share the same tools, they are one agent wearing two hats, and merging them cuts a model hop and a class of routing bugs. Start with the fewest agents that keep each instruction short, and split further only when a single agent gets unreliable. More agents is not more capable; it is more surface to break.
The second is routing you never measured. LLM-driven delegation is a classification problem, and classifiers are wrong sometimes. Build a small set of real requests with the specialist each should reach, and assert the coordinator routes them correctly. Run it in your pipeline so a prompt tweak that quietly breaks routing gets caught before users do. Grading the full answer, not just the routing, is the job of the evaluation service, which the next part covers.
The third is losing the thread across a transfer. When a coordinator hands off with transfer_to_agent, the specialist needs enough context to act, and state that lived only in the coordinator’s head does not travel by magic. Use Sessions to carry the conversation and be explicit about what each agent reads. The fourth is unbounded loops: a LoopAgent with a condition that never trips, or two agents that transfer back and forth, both burn tokens with nothing to show. Put a hard cap on iterations and transfers, and alert on it.
One coordinator, typed sub-agents, Agent Engine for the runtime
If you take one shape away, take this one. Put a cheap LlmAgent coordinator on top with disjoint, well-described sub-agents beneath it, and drop to SequentialAgent, ParallelAgent, or LoopAgent wherever the path is fixed rather than making the model rediscover it. Wrap reusable skills as AgentTool so the parent keeps control. Reach across a team or cloud boundary with A2A, never by importing code you do not own. Then deploy the whole graph to Agent Engine, size the container to real concurrency, and let Sessions carry the state.
The single most valuable thing you can add before this reaches users is a routing test: a dozen real requests, the specialist each should hit, and an assertion in your pipeline. It runs in seconds and catches the failure that costs you a support queue. Next in the series we grade the whole system with the Gen AI evaluation service, which measures whether the answer, not just the routing, holds up. Write the routing test today, before you add a fourth agent.
References
- Agent Development Kit, making it easy to build multi-agent applications, Google Developers Blog
- Developer’s guide to multi-agent patterns in ADK, Google Developers Blog
- Linux Foundation launches the Agent2Agent protocol project
- Vertex AI and Agent Engine pricing, Google Cloud


DrJha