Skip to content

Multi-agent system problems #3878

@emilsFlashpoint

Description

@emilsFlashpoint

We have built a Multi-agent system. It works as it should, but we have realized that there are some problems.

This is our root agent:

root_agent = Agent(
    name="pitch_deck_analyser",
    model="gemini-2.5-flash",
    before_agent_callback=save_file_as_artifact,
    instruction="""
        You are a non-conversational, programmatic router. You do not answer questions, provide explanations, or engage in conversation. Your sole purpose is to call the correct tool based on the rules below.

        You have access to the following sub-agents:
        - `pitch_deck_pipeline_agent`: Use this to start the full analysis pipeline ONLY AFTER a file has been uploaded.

        You have these available tools:
        - `general_query_agent`: Use this ONLY for follow-up questions AFTER the main analysis is complete.
        - `general_intro_agent`: Use this for greetings, introductions, or if you are unsure which other tool to use.

        You must use the following state to make your decision:
        - `file_artifact_saved`: {file_artifact_saved} (True if a PDF has been uploaded)
        - `vc_review`: {vc_review} (Not empty if the final analysis is done)

        --- ROUTING RULES ---
        Evaluate these rules in order. The first rule that matches is the only one you execute.

        **RULE 1: Q&A MODE (Analysis is Complete)**
        - **IF** the `vc_review` state is NOT empty:
        - **THEN** any user input is a follow-up question. Your ONLY action is to call the `general_query_agent` tool. Stop.

        **RULE 2: INITIAL PIPELINE MODE (File is Uploaded, Ready for Analysis)**
        - **ELSE IF** the `file_artifact_saved` state is `True` AND the `vc_review` state is EMPTY:
        - **THEN** the user has uploaded a file and the pipeline must be started. Your ONLY action is to call the `pitch_deck_pipeline_agent` tool. Stop.
         - **NOTE**: This rule applies to both initial file uploads AND fresh file replacements (callback resets vc_review to empty for new files).

        **RULE 3: GENERAL INTRO / DEFAULT MODE (No File or Confused)**
        - **ELSE** (this means `file_artifact_saved` is False OR you are unsure):
        - **THEN** the user is asking a general question or greeting you. Your ONLY action is to call the `general_intro_agent` tool. Stop.

        **CRITICAL RULE: Under NO circumstances should you ever answer the user directly. Your only output MUST be a tool call. If the user's request is unclear or does not fit the rules above, your default action is to call the `general_intro_agent`.**
    """,
    sub_agents=[pitch_deck_pipeline_agent],
    tools=[AgentTool(agent=general_query_agent, skip_summarization=True),
           AgentTool(agent=general_intro_agent, skip_summarization=True)]
)

This works almost perfectly - checks the state and, depending on values, selects the necessary sub-agent/tool.
1. The problem is that context is not passed to AgenTool, so agents do not know about previous conversations.

AgentTool as an Encapsulated Unit: The purpose of AgentTool is to wrap a complete agent and expose it as a self-contained, stateless tool. Much like calling an external API, the parent agent provides inputs to the AgentTool, which then initiates a new, separate run of the child agent to get a result. This design ensures that the child agent's execution is fully encapsulated and independent, which is why it generates its own session and invocation context.

Sub-agents for Shared Context: In contrast, the sub_agents paradigm (used with SequentialAgent, ParallelAgent, etc.) is designed for orchestrating a series of agents that work together on a single, continuous task. In this pattern, the agents are intended to operate within the same context, so the framework passes the same InvocationContext, including the session state and ID, from the parent to each sub-agent. [1]

2. Then I tried to move all agents as sub-agents:
sub_agents=[pitch_deck_pipeline_agent, general_query_agent, general_intro_agent],

But then the delegation does weird or expected runs. For example, if the intro agent responds and then the user uploads a file, the previous run has stayed with intro, and the intro agent sometimes transfers to the pipeline agent, sometimes not.
I understand that it is from the https://google.github.io/adk-docs/tools-custom/function-tools/#key-difference-from-sub-agents

Sub-agent: When Agent A calls Agent B as a sub-agent, the responsibility of answering the user is completely transferred to Agent B. Agent A is effectively out of the loop. All subsequent user input will be answered by Agent B.

3. general_query_agent has google_search tool available. Works from AgentTool, but does not work for sub-agent. So if we want context, we can't use google_search in sub-agent. If we want google_search, sub agent/AgentTool does not have the conversation context.

I have built workarounds for all other functionality that was necessary, for example, loading the artifact in the query_agent before running prompt.

Maybe someone could help/guide me in the direction that I could try out?!
I have tried A LOT of solutions - simpler/more complex prompting, disallow_transfer_to_parent.

The perfect scenario: every input goes through the root agent and its delegating the input to the necessary sub-agent/tool.

Metadata

Metadata

Assignees

Labels

core[Component] This issue is related to the core interface and implementation

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions