Mastering agentic workflows with ADK for Java: Sequential agents
In the first part of this series, we explored the “divide and conquer” strategy using sub-agents to create a flexible, modular team of AI specialists. This is perfect for situations where the user is in the driver’s seat, directing the flow of conversation. But what about when the process itself needs to be in charge?
Some tasks are inherently linear. You have to do Step A before Step B, and Step B before Step C. Think about a CI/CD pipeline: you build, then you test, then you deploy. You can’t do it out of order… or if you do, be prepared for havoc!
For these situations, the ADK for Java provides another powerful tool in our orchestration toolbox: the SequentialAgent.
When order matters: the sequential workflow
A sequential workflow is all about creating a predictable, step-by-step process. Unlike the flexible sub-agent model where an orchestrator LLM decides which sub-agent to invoke and when, a SequentialAgent executes a predefined list of agents in a fixed order.
This is useful for:
- Automating multi-step processes: Guiding a user through a fixed procedure, like a trip planner or a complex data analysis pipeline.
- Ensuring consistency: Guaranteeing that tasks are always performed in the correct sequence for reliable and predictable outcomes.
- Building on previous results: Creating workflows where the output of one step is the essential input for the next.
A practical example: a trip-planner
Let’s dive into our SequentialFlow.java example (code further down). This code defines a trip-planner agent that guides a user through the process of planning a vacation. You can't suggest restaurants before you have an itinerary, and you can't build an itinerary before you've researched the destination. This is a perfect use case for a SequentialAgent.
Our trip-planner consists of three specialist agents that will be executed in a specific order:
destination-researcher: The first step. It takes a user's request (e.g., "a romantic weekend in Paris") and finds suitable attractions.itinerary-creator: The second step. It takes the list of attractions from the previous step and organizes it into a logical 2-day plan.restaurant-suggester: The final step. It takes the completed itinerary and enriches it with relevant lunch and dinner recommendations.
I’ll show you the full source code here first, but then in subsequent sections, we’ll explore each steps in more detail.
var destinationResearcher = LlmAgent.builder()
.name("destination-researcher")
.description("""
Finds points of interest for a given destination
and travel style.
""")
.instruction("""
Your role is to find points of interest for a given
city and travel style, according to the duration of
the trip, and potentially budget.
Use the Google Search Tool to find relevant information.
Present the results as a simple bullet point list
of key attractions.
""")
.model("gemini-2.0-flash")
.tools(new GoogleSearchTool())
.outputKey("destination-research")
.build();
var itineraryCreator = LlmAgent.builder()
.name("itinerary-creator")
.description("""
Creates an itinerary from a list of attractions.
""")
.instruction("""
Your role is to create a logical itinerary
from the provided list of attractions:
{destination-research}
Group the attractions by proximity or theme for each day.
Present the itinerary clearly, with each day's plan laid out.
""")
.model("gemini-2.0-flash")
.outputKey("itinerary")
.build();
var restaurantSuggester = LlmAgent.builder()
.name("restaurant-suggester")
.description("""
Suggests restaurants for each day of the itinerary.
""")
.instruction("""
Your role is to suggest one lunch and one dinner restaurant
for each day of the itinerary:
{itinerary}
Use the Google Search Tool to find restaurants
that are near the day's activities
and match the overall travel style.
Add the restaurant suggestions to the itinerary.
""")
.model("gemini-2.0-flash")
.tools(new GoogleSearchTool())
.build();
return SequentialAgent.builder()
.name("trip-planner")
.description("""
Helps you plan a trip by finding attractions,
creating an itinerary, and suggesting restaurants.
""")
.subAgents(
destinationResearcher,
itineraryCreator,
restaurantSuggester
)
.build();The magic ✨ of state passing: outputKey
“But wait,” you ask, “if these are separate agents, how does the itinerary-creator know what the destination-researcher found?"
This is the core mechanism of the SequentialAgent: passing state between agents. The ADK makes this incredibly simple and elegant using an outputKey.
Here’s how it works:
- The producer agent: The agent that generates the data uses the
.outputKey("some-key")method. This tells theSequentialAgentto store the final result of this agent's turn in a context variable namedsome-key.
var destinationResearcher = LlmAgent.builder()
.name("destination-researcher")
// ... instructions ...
.outputKey("destination-research") // <-- Produces the data
.build();- The consumer agent: The next agent in the sequence can then reference this stored data directly in its instructions using curly braces:
{some-key}.
var itineraryCreator = LlmAgent.builder()
.name("itinerary-creator")
.instruction("""
Your role is to create a logical itinerary
from the provided list of attractions:
{destination-research} // <-- Consumes the data
Group the attractions by proximity or theme for each day.
""")
.outputKey("itinerary") // Can produce data for the next agent!
.build();The ADK handles the injection automatically. The {destination-research} placeholder will be replaced by the full output of the destinationResearcher agent before the prompt is sent to the itineraryCreator agent.
Building the SequentialAgent
Putting it all together is straightforward. We use the SequentialAgent.builder() and provide our agents to the .subAgents() method. The key difference here is that the order is strictly enforced.
return SequentialAgent.builder()
.name("trip-planner")
.description("""
Helps you plan a trip by finding attractions,
creating an itinerary, and suggesting restaurants.
""")
.subAgents(
destinationResearcher, // will always run first
itineraryCreator, // will always run second
restaurantSuggester // will always run third
)
.build();When this agent runs, it will execute destinationResearcher, take its output, feed it into itineraryCreator, take its output, feed it into restaurantSuggester, and finally return the completed, enriched itinerary to the user. The last agent could also reference the output of the first agent if needed, for full context awareness!
When to choose SequentialAgent
- Choose a
SequentialAgentwhen you have a fixed, linear process where Step B logically depends on the output of Step A. It provides predictability and structure. - Choose an
LlmAgentwith sub-agents (as in part 1) when you need flexibility and want the orchestrator LLM to dynamically decide the best tool or next step based on the conversational context.
By understanding both patterns, you can build incredibly robust and sophisticated AI systems tailored to the specific problem you’re trying to solve.
Stay tuned for the next part of the series, where we’ll explore how to run agents in parallel to handle multiple tasks at once!
Originally published at https://glaforge.dev on July 24, 2025.

