My thoughts
One agent, an assembly line, or a crowd: what agent patterns do to the user experience
I built the same trip planner three ways. The architecture choice turned out to be a design choice, and the user can feel every one of them.
For a long time I treated "how many agents" as an engineering detail. Someone else decided whether the assistant was one model with tools or a team of smaller agents, and I designed the chat window around whatever came back. That stopped working the first time I watched a user stare at a spinner for twenty seconds, then get a plan that had quietly skipped half of what they asked for.
So I started prototyping the agent structure myself. My test bed was a deliberately boring task: plan me a day in San Francisco. Find somewhere to eat, work out how to get around, maybe add a museum or a concert. It is the same example Google Cloud Tech uses in their AI agent design patterns episode, which walks through three foundational patterns with the Agent Development Kit (ADK). I borrowed their framing and their trip example, then pushed each pattern until I could see where it broke for the person using it.
Here is what I learned about each one, written for people who design these products rather than only build them.
Pattern 1The single agent: one brain, a toolbox, and a lot of trust
The simplest version is one agent. You give it instructions, a model, and some tools, in my case Google Search, and let the model's reasoning decide what to do and in what order. In ADK that is one LlmAgent:
from google.adk.agents import LlmAgent
from google.adk.tools import google_search
trip_planner = LlmAgent(
name="trip_planner",
model="gemini-2.5-flash",
instruction="Plan a one-day trip. Find food, transport and activities.",
tools=[google_search],
)
It is wonderful to prototype with. I had a working planner in an afternoon, and for short, open-ended asks ("what's a good brunch spot near the Ferry Building?") it felt fast and natural. The model adapts to whatever the user throws at it, which is exactly what you want from a conversational surface.
The trouble started when the request had several parts. Because the model is nondeterministic, the same prompt produced different step orders on different runs. Sometimes it searched for food first, sometimes transport, and once in a while it decided one search was enough and wrote a confident plan that never mentioned how to get anywhere. The video puts it plainly: the single agent lacks control for complex workflows, because multi-step logic can't be guaranteed.
What that means for the interface
From a design point of view, a single agent is a black box with a variable runtime. I couldn't show meaningful progress, because I didn't know what the steps would be. The honest UI was a generic "Thinking…" state, and in testing that is where trust leaked away. People will wait for a process they can see. They get anxious waiting for one they can't.
Pattern 2The sequential agent: an assembly line you can draw on a whiteboard
The fix for "it skipped transport" is to stop asking the model to remember the order. In the sequential pattern you split the job into sub-agents and run them in a fixed sequence, so the output of one becomes the input of the next, like stations on an assembly line. For the trip planner that meant a food agent first, then a transportation agent that plans routes to wherever the food agent picked.
The interesting part is how they hand work to each other. ADK gives every run a session state, a shared scratchpad. The first agent writes its result under a key with output_key, and the next agent pulls it into its own instructions with curly braces:
food_agent = LlmAgent(
name="food_finder", model="gemini-2.5-flash", tools=[google_search],
instruction="Find two well-reviewed places to eat in {city}.",
output_key="food", # writes to session state
)
transport_agent = LlmAgent(
name="transport_planner", model="gemini-2.5-flash", tools=[google_search],
instruction="Plan how to travel between these places: {food}", # reads it back
output_key="transport",
)
planner = SequentialAgent(name="day_planner",
sub_agents=[food_agent, transport_agent])
The difference in reliability was immediate. Every run now produced restaurants and a way to reach them, because the transport agent literally couldn't run without the food agent's output. The video describes this pattern as giving you a high degree of control and reliability, and that matched what I saw.
What that means for the interface
This is where I got excited as a designer, because a fixed order is something you can show. Each sub-agent maps to a step in a progress indicator. "Finding places to eat" ticks over to "Planning how to get around." The waiting time didn't get shorter, but in sessions it felt shorter, and people started reading the intermediate results while the next step ran. Session state also made it easy to surface partial output: the moment food was written, I could render the restaurant cards.
The cost is rigidity. When a tester said "actually, I already have a car," the assembly line didn't care. It ran the same two stations in the same order and planned transit routes anyway. The video calls this out as the pattern's weakness: it is inflexible when the situation is dynamic. Designing for it meant adding explicit up-front choices (a transport preference toggle) instead of trusting the conversation to steer.
Pattern 3The parallel agent: a crowd working at once, and a synthesizer at the end
Once I added activities, the sequential planner got slow. Museums, concerts, and restaurants were three searches that had nothing to do with each other, yet each one waited for the previous to finish. The parallel pattern runs independent tasks like these concurrently. A final aggregator agent then reads all of their results and synthesizes one plan.
In ADK the shape is a ParallelAgent for the fan-out, wrapped together with the aggregator in a SequentialAgent so the synthesis waits for everyone:
museums = LlmAgent(name="museums", ..., output_key="museums")
concerts = LlmAgent(name="concerts", ..., output_key="concerts")
restaurants = LlmAgent(name="restaurants", ..., output_key="restaurants")
research = ParallelAgent(name="research", sub_agents=[museums, concerts, restaurants])
aggregator = LlmAgent(name="aggregator", model="gemini-2.5-flash",
instruction="Build one day plan from {museums}, {concerts} and {restaurants}.")
planner = SequentialAgent(name="day_planner", sub_agents=[research, aggregator])
The planner felt noticeably snappier, which is the headline benefit the video names: lower latency from running tasks concurrently. Two costs showed up that I hadn't planned for, and the video flags both. First, three agents running at once means three sets of model and tool calls at once. Your bill doesn't get smaller because the wait did. Second, the aggregator became the most important prompt in the system. When the concerts agent found an evening show and the museums agent found a place that closes at 5pm, it was the aggregator's job to notice and build a sensible day. Early versions just stapled the three lists together.
What that means for the interface
Parallel work wants a parallel UI. A linear stepper lies about what's happening, so I switched to three result cards that fill in independently as each agent finishes, followed by a "Putting your day together" state while the aggregator runs. It felt alive in a way the other versions didn't. It also exposed a new failure mode: if one agent failed, the user saw two full cards and one empty one. I had to design that partial state explicitly, with a retry on the one card, instead of failing the whole plan.
Side by sideThe same request, three loading experiences
The most useful artifact from this whole exercise was a sketch of what the user sees while each pattern is working. The architecture decides which of these screens you are allowed to design honestly.
ChoosingHow I pick a pattern now
I no longer start with "which pattern is best." I start with three questions about the task and the person on the other side of the screen:
- Does the order of steps matter, and is it always the same? If yes, a sequential pipeline will be more reliable than hoping the model remembers.
- Are some steps independent of each other? If they are, and waiting is hurting the experience, run them in parallel and invest in the aggregator.
- What happens to the user if a step is skipped? If the answer is "they'd notice and shrug," a single agent is fine and much cheaper to build.
| Pattern | Gives you | Costs you | Honest loading UI | I reach for it when |
|---|---|---|---|---|
| Single agent | Simplicity, flexibility | Control on multi-step work | Generic "thinking" | Short, open-ended asks |
| Sequential | Control, predictability | Adapting to surprises | Ordered stepper with partial results | Repeatable flows with a fixed order |
| Parallel | Speed on independent work | Compute cost, synthesis complexity | Cards that fill in, then a combine step | Several lookups that don't depend on each other |
In practice these patterns compose. My final trip planner was a sequential pipeline whose first step was a parallel research stage, which is exactly the nesting Figure 3 shows. The structure became a design tool: once I could name the stages, I could name them to the user too.