DEV Community

Noble Ackerson
Noble Ackerson

Posted on

Your multi-agent system is probably slower than it needs to be

From Sequential to Dynamic: Evolving a Generative UI Multi-Agent Architecture

The Problem I Started With

Building dashboards sucks. You spend hours configuring charts, mapping data, and making sure everything works together. I thought: what if I could just ask for what I want in plain English and get back a working React dashboard?

So back to the drawing board, I built a system that does exactly that. Natural language in, interactive React components out. The magic happens through AI agents that specialize in different parts of dashboard creation.

But here's the thing - my first version was painfully slow. Users would ask for a dashboard and then... wait. And wait some more. Everything happened one step at a time, like being stuck behind someone counting exact change at the grocery store.

This post is essentially a part two of my Agentic Component Recognition and GenerativeUI applied research (see part one below) fixed that using Google's Agent Development Kit (ADK) and went from "please wait 30 seconds" to "here's your dashboard streaming in real-time."

Version 1: The Slow Sequential Approach

Our original system worked like an assembly line from the 1950s. One agent would finish, pass the work to the next agent, who would finish, pass it on, and so on.

# What I started with - everything in sequence
from google.adk.agents import SequentialAgent, LlmAgent

def create_dashboard_the_slow_way():
    chart_agent = LlmAgent(name="chart_maker", tools=[chart_tools])
    map_agent = LlmAgent(name="map_maker", tools=[map_tools])  
    layout_agent = LlmAgent(name="layout_maker", tools=[layout_tools])

    # This runs one agent at a time, in order
    # Chart agent finishes -> Map agent starts -> Layout agent starts
    return SequentialAgent(
        name="slow_dashboard_maker",
        sub_agents=[chart_agent, map_agent, layout_agent]
    )
Enter fullscreen mode Exit fullscreen mode

The math was brutal:

  • Chart agent: 5 seconds
  • Map agent: 5 seconds
  • Layout agent: 5 seconds
  • Total: 15+ seconds of staring at loading spinners

Users would request a dashboard and then go make coffee. That's not the experience I wanted.

The Fix: Three Phases of Getting Faster

Phase 1: Parallel Tools Within Agents

First fix was obvious once I saw it. Individual agents were doing multiple things sequentially too. Our chart agent would create a bar chart, wait for it to finish, then create a line chart, then create a pie chart.

Why not do all three at the same time?

# Let agents do multiple things at once
import asyncio

async def make_charts_parallel(chart_types):
    tasks = []
    for chart_type in chart_types:
        tasks.append(create_chart(chart_type))

    # All charts get created simultaneously
    results = await asyncio.gather(*tasks)
    return results
Enter fullscreen mode Exit fullscreen mode

This was like going from dial-up to broadband for individual agents. Our chart agent went from 10 seconds to 2 seconds when it needed multiple charts.

Phase 2: Parallel Agents

Next problem: independent agents were still waiting in line. Chart generation and map generation have nothing to do with each other, so why does the map agent wait for the chart agent to finish?

ADK has a ParallelAgent that handles this perfectly:

# Multiple agents working simultaneously
from google.adk.agents import ParallelAgent

def create_dashboard_faster(query):
    # Figure out what we actually need
    agents_needed = []

    if "chart" in query.lower():
        agents_needed.append(chart_agent)
    if "map" in query.lower():
        agents_needed.append(map_agent)
    if "accessible" in query.lower():
        agents_needed.append(accessibility_agent)

    # Run only what we need, all at the same time
    parallel_maker = ParallelAgent(
        name="fast_dashboard_maker",
        sub_agents=agents_needed
    )

    return parallel_maker
Enter fullscreen mode Exit fullscreen mode

Now instead of 15 seconds sequential, we're looking at 5-7 seconds with results streaming in as each agent finishes.

Phase 3: Dynamic Agent Creation

The real breakthrough was realizing I didn't need to keep all these agents sitting around doing nothing. Why have a map agent loaded in memory if someone just wants a simple chart?

I built an agent factory that creates exactly what each query needs:

# Create agents on demand
class AgentFactory:
    def __init__(self):
        # Templates for different types of agents
        self.templates = {
            "chart_basic": ChartTemplate(model="gemini-flash", complexity="basic"),
            "chart_advanced": ChartTemplate(model="gemini-pro", complexity="advanced"),
            "map_basic": MapTemplate(model="gemini-flash", complexity="basic"),
            # etc...
        }

    async def make_dashboard(self, query, requirements):
        # What do I actually need for this query?
        needed_capabilities = self.analyze_query(query)

        # Create only those agents
        agents = []
        for capability in needed_capabilities:
            template = self.templates[capability]
            agent = template.create_agent()
            agents.append(agent)

        # Run them in parallel
        if len(agents) > 1:
            orchestrator = ParallelAgent(sub_agents=agents)
        else:
            orchestrator = agents[0]

        try:
            return await orchestrator.run(query)
        finally:
            # Clean up when done
            self.cleanup_agents(agents)
Enter fullscreen mode Exit fullscreen mode

This is where things got interesting. I could:

  • Spin up exactly the right agents for each query
  • Use lightweight agents for simple requests
  • Use heavyweight agents for complex analysis
  • Clean up everything when done

Real-World Examples

Simple query: "Show me Q4 sales trends"

  • Factory creates: 1 basic chart agent
  • Time: ~3 seconds
  • Memory: Minimal

Complex query: "Show regional sales with accessible high-contrast visualization and executive summary"

  • Factory creates: Chart agent + Map agent + Accessibility agent + Summary agent
  • Time: ~6 seconds (all running in parallel)
  • Memory: Only what's needed, cleaned up after

Follow-up query: "Make that chart bigger"

  • Factory creates: 1 lightweight chart modifier agent
  • Time: ~1 second
  • Memory: Almost nothing

ADK Made This Possible

None of this would have worked without ADK's built-in features:

Session Management: Agents automatically share data through ADK's session state. Chart agent puts data in session, map agent reads it out. No complicated message passing.

Streaming: Results show up in the UI as soon as each agent finishes, not when everything is done.

Callbacks: We can monitor performance, catch errors, and log everything without cluttering our agent code.

# ADK handles the hard parts
from google.adk.core.callbacks import before_agent, after_agent

@before_agent
def log_start(agent_name, context):
    print(f"Starting {agent_name}")

@after_agent  
def log_finish(agent_name, result, context):
    print(f"{agent_name} finished in {context.duration}ms")
Enter fullscreen mode Exit fullscreen mode

The Results

Before: "Please wait 20-30 seconds for your dashboard"
After: "Here's your chart... here's your map... here's your final dashboard" (streaming in real-time)

Numbers:

  • 70% faster for complex dashboards
  • 60% less memory usage (only create what you need)
  • 90% fewer timeout errors (parallel execution is more resilient)

But the real win was user experience. Instead of staring at loading spinners, users watch their dashboard come together piece by piece.

What I Learned

Start simple: I began with SequentialAgent, figured out what worked, then made it faster.

Use ADK's built-ins: Don't reinvent session management, streaming, or callbacks. ADK already solved these problems.

Parallel isn't always better: For dependencies (like layout composition), you still need sequential steps.

Clean up your mess: Dynamic agent creation means dynamic cleanup. Don't leave agents sitting around eating memory.

Monitor everything: You can't optimize what you don't measure. ADK's callback system makes this easy.

Future Plans

What's next? I'm working on:

Smart agent sizing: Use small models for simple queries, big models for complex analysis.

Learning from history: Track which agent combinations work best for different types of queries.

Cost optimization: Balance speed vs. cost by choosing the right models for each task.

Cross-query caching: If two users ask for similar dashboards, reuse what we can.

If you're interested in contributing to this create an issue or for the repo (link below)

Try It Yourself

The complete code is available in our GenUI repo. You can run the ADK web interface locally:

# Install ADK
pip install google-adk

# Run the web UI
adk web genui_agent/ --port 8080

# Visit http://localhost:8080
Enter fullscreen mode Exit fullscreen mode

The ADK documentation has everything you need to build your own multi-agent systems. Start with their quickstart guides and work your way up to parallel orchestration.

Bottom Line

Building responsive AI systems isn't about having the biggest models or the most complex architecture. It's about using the right tools efficiently and not making users wait when they don't have to.

ADK gave us the building blocks. I just had to stop thinking sequentially and start thinking in parallel.


Code examples and implementation details are available at github.com/stigsfoot/google-ai-sprint. Questions? Find me on LinkedIn @noblea or check out the ADK documentation.

Resources:

Top comments (0)