DEV Community

Ömer Berat Sezer
Ömer Berat Sezer

Posted on

Multi-agents: Parallel Agents using Google ADK 🤖 Gemini, Fast API, Streamlit - Report Generation

In the past 4-5 months, TWO powerful AI agent development frameworks have been released:

  • Google Agent Development Kit (ADK)
  • AWS Strands Agents

You can view the other posts in the Google ADK series above. In our current post, we’ll dive into the Google Agent Development Kit (ADK) and show how to create workflow parallel agents using ADK, Gemini 2.5, FastAPI, and a Streamlit interface.

Table of Contents

What is Google Agent Development Kit?

  • Agent Development Kit (ADK) is an open-source framework to develop AI agents to run anywhere:
    • VSCode, Terminal,
    • Docker Container
    • Google CloudRun
    • Kubernetes

What is Multi-Agent, Parallel Agents?

The ParallelAgent is a workflow agent designed to run its sub-agents simultaneously, significantly accelerating workflows where tasks are independent.

When the run_async() method of the ParallelAgent is invoked:

  • Simultaneous Execution: It triggers the run_async() method for all agents in the sub_agents list at the same time.
  • Isolated Processes: Each sub-agent runs in its own thread of execution without automatically sharing conversation history or state with the others.
  • Gathering Results: After execution, the ParallelAgent handles collecting the results from each sub-agent. These results are typically accessible through a list or event-based mechanism, but their order might not be predictable.

It's important to recognize that sub-agents within a ParallelAgent operate independently. If coordination or data sharing is required between them, it must be handled manually. Here are some possible strategies:

  • Shared InvocationContext: You can provide all sub-agents with the same InvocationContext, using it as a shared data container. However, to prevent race conditions, concurrent access must be managed carefully—typically with synchronization mechanisms like locks.
  • External State Handling: Leverage external tools such as databases or message queues to manage shared state and facilitate inter-agent communication.
  • Post-Execution Coordination: Let each sub-agent complete its task independently, then process and reconcile their outputs afterward to coordinate data.

parallelagents

Details: https://google.github.io/adk-docs/agents/workflow-agents/parallel-agents/#independent-execution-and-state-management

Agent App

Sample project on GitHub:

Installing Dependencies & Reaching Gemini Model

# .env
GOOGLE_GENAI_USE_VERTEXAI=FALSE
GOOGLE_API_KEY=PASTE_YOUR_ACTUAL_API_KEY_HERE
Enter fullscreen mode Exit fullscreen mode
  • Please install requirements:
fastapi
uvicorn
google-adk
google-generativeai
Enter fullscreen mode Exit fullscreen mode

Frontend - Streamlit UI

# app.py
import streamlit as st
import requests

st.set_page_config(page_title="Agent Chat", layout="centered")

if "messages" not in st.session_state:
    st.session_state.messages = []

st.title("Multi-Agent Paralel Researcher")

for msg in st.session_state.messages:
    with st.chat_message(msg["role"]):
        st.markdown(msg["content"])

user_query = st.chat_input("Ask to search for real-time data or anything...")

# send and display user + assistant messages
if user_query:
    st.chat_message("user").markdown(user_query)
    st.session_state.messages.append({"role": "user", "content": user_query})
    try:
        response = requests.post(
            "http://localhost:8000/ask",
            json={"query": user_query}
        )
        response.raise_for_status()
        # respond is not str, it is list, because of multiple agent responds.
        all_replies = response.json().get("responses", ["No response."])

        for reply in all_replies:
            st.chat_message("assistant").markdown(reply)
            st.session_state.messages.append({"role": "assistant", "content": reply})

    except Exception as e:
        error_msg = f"Error: {str(e)}"
        st.chat_message("assistant").markdown(error_msg)
        st.session_state.messages.append({"role": "assistant", "content": error_msg})
Enter fullscreen mode Exit fullscreen mode

Backend - Agent

# agent.py
from fastapi import FastAPI
from pydantic import BaseModel
from dotenv import load_dotenv
from google.genai import types
from google.adk.agents import Agent
from google.adk.agents.llm_agent import LlmAgent
from google.adk.agents.sequential_agent import SequentialAgent
from google.adk.agents.parallel_agent import ParallelAgent
from google.adk.runners import Runner
from google.adk.sessions import InMemorySessionService
from google.adk.memory import InMemoryMemoryService
from google.adk.tools import google_search
import uvicorn

# Load environment variables
load_dotenv()

# Define model and app constants
MODEL = "gemini-2.5-flash"
APP_NAME = "search_memory_app"
USER_ID = "user123"
SESSION_ID = "session123"

# --- Agent Definitions ---
# the Pydantic model for the output of the TopicSetterAgent
class TopicOutput(BaseModel):
    subtopic_1: str
    subtopic_2: str
    subtopic_3: str

topic_setter = LlmAgent(
    name="TopicSetterAgent",
    model=MODEL,
    instruction="""
    You are a research planner. Your task is to break the user's input query into three distinct and relevant subtopics.
    Respond ONLY in this format:

    {
    "subtopic_1": "...",
    "subtopic_2": "...",
    "subtopic_3": "..."
    }
    """,
    output_schema=TopicOutput,
    output_key="topic_output"
)

researcher_agent_1 = LlmAgent(
    name="SubResearcherOne",
    model=MODEL,
    instruction="""
    You are a research assistant. Research the subtopic: "{topic_output.subtopic_1}".
    Summarize your findings in 1-2 sentences.
    """,
    tools=[google_search],
    output_key="result_1"
)

researcher_agent_2 = LlmAgent(
    name="SubResearcherTwo",
    model=MODEL,
    instruction="""
    You are a research assistant. Research the subtopic: "{topic_output.subtopic_2}".
    Summarize your findings in 1-2 sentences.
    """,
    tools=[google_search],
    output_key="result_2"
)

researcher_agent_3 = LlmAgent(
    name="SubResearcherThree",
    model=MODEL,
    instruction="""
    You are a research assistant. Research the subtopic: "{topic_output.subtopic_3}".
    Summarize your findings in 1-2 sentences.
    """,
    tools=[google_search],
    output_key="result_3"
)

# final synthesis
merger_agent = LlmAgent(
    name="ResearchSynthesizer",
    model=MODEL,
    instruction="""
    You are a synthesis assistant. Merge the three research summaries below into a single cohesive research report.

    Subtopic 1 ({topic_output.subtopic_1}): {result_1}
    Subtopic 2 ({topic_output.subtopic_2}): {result_2}
    Subtopic 3 ({topic_output.subtopic_3}): {result_3}

    Write in this format:

    ## Research Summary on the Given Topic

    ### {topic_output.subtopic_1}
    [result_1]

    ### {topic_output.subtopic_2}
    [result_2]

    ### {topic_output.subtopic_3}
    [result_3]

    ### Conclusion
    Write 2-3 sentences summarizing the topic overall.
    """
)

parallel_research_agent = ParallelAgent(
    name="ParallelWebResearchAgent",
    sub_agents=[researcher_agent_1, researcher_agent_2, researcher_agent_3],
    description="Runs multiple research agents in parallel to gather information."
)

research_pipeline = SequentialAgent(
    name="GeneralResearchPipeline",
    description="Extracts a topic, researches it in parallel, then synthesizes a report.",
    sub_agents=[topic_setter, parallel_research_agent, merger_agent],
)

root_agent = research_pipeline

session_service = InMemorySessionService()
memory_service = InMemoryMemoryService()

app = FastAPI()

class QueryRequest(BaseModel):
    query: str

@app.on_event("startup")
async def startup_event():
    """
    Initializes the session and runner on application startup.
    """
    await session_service.create_session(
        app_name=APP_NAME,
        user_id=USER_ID,
        session_id=SESSION_ID
    )

    global runner
    runner = Runner(
        agent=root_agent,
        app_name=APP_NAME,
        session_service=session_service,
        memory_service=memory_service
    )

@app.post("/ask")
async def ask_agent(req: QueryRequest):
    """
    Endpoint to send a query to the agent pipeline.
    """
    content = types.Content(role="user", parts=[types.Part(text=req.query)])
    # `runner.run()` is a generator, not a coroutine, so it should not be awaited.
    events = runner.run(user_id=USER_ID, session_id=SESSION_ID, new_message=content)

    responses = []
    for event in events:
        if event.is_final_response() and event.content and event.content.parts:
            responses.append(event.content.parts[0].text)

    # Await both the get_session and add_session_to_memory calls.
    session = await session_service.get_session(app_name=APP_NAME, user_id=USER_ID, session_id=SESSION_ID)
    await memory_service.add_session_to_memory(session)

    return {"responses": responses or ["No response received."]}

if __name__ == "__main__":
    uvicorn.run(app, host="0.0.0.0", port=8000)
Enter fullscreen mode Exit fullscreen mode

Run & Demo

Run backend (agent.py):

$  uvicorn agent:app --host 0.0.0.0 --port 8000
Invalid config for agent TopicSetterAgent: output_schema cannot co-exist with agent transfer configurations. Setting disallow_transfer_to_parent=True, disallow_transfer_to_peers=True
INFO:     Started server process [17790]
INFO:     Waiting for application startup.
INFO:     Application startup complete.
INFO:     Uvicorn running on http://0.0.0.0:8000 (Press CTRL+C to quit)
INFO:     127.0.0.1:45096 - "POST /ask HTTP/1.1" 200 OK
Enter fullscreen mode Exit fullscreen mode

Run frontend (app.py):

$ python3 -m streamlit run app.py

  You can now view your Streamlit app in your browser.

  Local URL: http://localhost:8501
  Network URL: http://172.28.246.163:8501
Enter fullscreen mode Exit fullscreen mode

Test Prompt:

I want to research about the "LLM Agents"
Enter fullscreen mode Exit fullscreen mode

Topic_setter agent gives following response:

{"subtopic_1": "Definition and architecture of LLM Agents", 
"subtopic_2": "Applications and use cases of LLM Agents", 
"subtopic_3": "Challenges and future directions for LLM Agents"}
Enter fullscreen mode Exit fullscreen mode

Research Agent1 gives response:

LLM agents face challenges including hallucinations, limited memory and 
context windows, and struggles with real-time decision-making and complex 
calculations. Future directions involve developing hybrid systems 
combining LLMs with deterministic tools for precision tasks, improving 
continuous learning and scalability, and enhancing multi-agent 
collaboration and ethical considerations.
Enter fullscreen mode Exit fullscreen mode

Research Agent2:

LLM agents are advanced AI systems that use large language models (LLMs) 
to reason through problems, create plans, and execute tasks with the help 
of various tools, going beyond simple text generation to handle complex, 
multi-step assignments autonomously. Their architecture typically includes 
a core LLM (the "brain"), memory modules for retaining context, tools for 
external interactions (like APIs or databases), and planning components to 
break down complex tasks and determine actions.
Enter fullscreen mode Exit fullscreen mode

Research Agent3:

LLM agents are utilized across various industries and applications due to 
their ability to understand language, perform complex tasks, and interact 
with external tools and data sources. Key applications include enhancing 
customer service through personalized interactions and 24/7 support, and 
streamlining internal operations such as HR and IT support. They also play 
a significant role in automating tasks like market analysis, financial 
reporting, supply chain management, and compliance checks by gathering and 
evaluating vast amounts of data. Furthermore, LLM agents are valuable in 
software development for generating code and assisting with debugging, and 
in research and development for hypothesis testing and data analysis.
Enter fullscreen mode Exit fullscreen mode

Merger Agent:

Definition and architecture of LLM Agents
LLM agents are advanced AI systems that use large language models (LLMs) 
to reason through problems, create plans, and execute tasks with the help 
of various tools, going beyond simple text generation to handle complex, 
multi-step assignments autonomously. Their architecture typically includes 
a core LLM (the "brain"), memory modules for retaining context, tools for 
external interactions (like APIs or databases), and planning components to 
break down complex tasks and determine actions.

Applications and use cases of LLM Agents
LLM agents are utilized across various industries and applications due to 
their ability to understand language, perform complex tasks, and interact 
with external tools and data sources. Key applications include enhancing 
customer service through personalized interactions and 24/7 support, and 
streamlining internal operations such as HR and IT support. They also play 
a significant role in automating tasks like market analysis, financial 
reporting, supply chain management, and compliance checks by gathering and 
evaluating vast amounts of data. Furthermore, LLM agents are valuable in 
software development for generating code and assisting with debugging, and 
in research and development for hypothesis testing and data analysis.

Challenges and future directions for LLM Agents
LLM agents face challenges including hallucinations, limited memory and 
context windows, and struggles with real-time decision-making and complex 
calculations. Future directions involve developing hybrid systems 
combining LLMs with deterministic tools for precision tasks, improving 
continuous learning and scalability, and enhancing multi-agent 
collaboration and ethical considerations.

Conclusion
LLM agents represent a significant advancement in AI, enabling autonomous 
execution of complex, multi-step tasks by integrating LLMs with planning, 
memory, and external tools. While offering vast potential across diverse 
applications from customer service to software development, they currently 
grapple with issues like hallucinations and computational limitations. 
Future developments aim to overcome these challenges through hybrid 
architectures, enhanced learning capabilities, and robust ethical frameworks.
Enter fullscreen mode Exit fullscreen mode

Demo GIF: GIF on GitHub

Result

Conclusion

In this post, we mentioned:

  • how to access Google Gemini 2.5,
  • how to implement multi-agent parallel agents

If you found the tutorial interesting, I’d love to hear your thoughts in the blog post comments. Feel free to share your reactions or leave a comment. I truly value your input and engagement 😉

For other posts 👉 https://dev.to/omerberatsezer 🧐

Follow for Tips, Tutorials, Hands-On Labs:

References

Your comments 🤔

  • Which tools are you using to develop AI Agents (e.g. Google ADK, AWS Strands, Google ADK, CrewAI, Langchain, etc.)?
  • What are you thinking about Google ADK?
  • Did you implement multi-agents using any framework (ADK, Strands, CrewAI)?

=> Welcome to any comments below related AI, agents for brainstorming 🤯

Top comments (2)

Collapse
 
omerberatsezer profile image
Ömer Berat Sezer • Edited

This time, I tested how to implement multi-agents using Google ADK. I’m also testing several AI multi-agent frameworks, CrewAI, AWS Strands, and Google ADK, across use cases such as multi-agent collaboration, MCP tool integration, support for multiple language models, and workflow orchestration. Google ADK and AWS Strands stand out for their ease of deployment. Unlike CrewAI, they don’t include built-in task implementations, but both provide comparable feature sets and integrate seamlessly with open-source tools like LiteLLM, MCP components (e.g., StdioServerParameters), and short- and long-term session memory management.

Collapse
 
natasha_sturrock_07dac06b profile image
Eminence Technology

Makes sense — the dependency array is just telling React when to re-run the effect, and in this case post changing is the real trigger. Including the state setters isn’t wrong, but it’s not necessary since they don’t change between renders.