Repository navigation
Description
Describe the bug
We have encountered a bug when nesting multiple instances of the following architecture:
SequentialAgent[ParallelAgent[A, B], LLMAgent],
where the LLMAgent acts as a reducer of the outputs of A and B.
When we only have one instance of that architecture, as above, it works exactly as expected. However, when nesting multiple of these architectures, the LLMAgent reducers don't see the outputs of Agents A and B in the parallel group before them.
This is because the _is_event_belongs_to_branch function from google.adk.flows.llm_flows.contents seems to be incorrectly filtering out events from the ParallelAgent before it.
In general, nesting parallel agents seem to have event visibility issues where the reducing agents don't always see the context of the agents in the Parallel Agent before them in sequence.
To Reproduce
Please share a minimal code and data to reproduce your problem.
Steps to reproduce the behavior:
We want the following architecture:
# Callback to demonstrate issue
def print_llmrequest_contents(callback_context: CallbackContext, llm_request: LlmRequest):
print("\n\n\n" + "*****"*10)
print(f"Model called in agent: {callback_context.agent_name}")
print(f"Agent branch: {getattr(callback_context, 'branch', 'NO BRANCH')}")
print(f"Total session events: {len(callback_context.session.events)}")
# Print all events with their branches
print("ALL SESSION EVENTS:")
for i, event in enumerate(callback_context.session.events):
print(f" {i}: {event.author} in branch '{event.branch}' said: \"{''.join([part.text for part in event.content.parts]) if event.content and event.content.parts else 'NO CONTENT'}\"")
print("FILTERED CONTENTS SENT TO MODEL:")
for content in llm_request.contents:
print(f"{content.role}: {''.join([part.text for part in content.parts])}")
print("\n\n\n" + "*****"*10)
# Group 1
A = Agent(name='Alice', description='An obedient agent.', instruction='Please say your name and your favorite sport.', model=model)
B = Agent(name='Bob', description='An obedient agent.', instruction='Please say your name and your favorite sport.', model=model)
C = Agent(name='Charlie', description='An obedient agent.', instruction='Please say your name and your favorite sport.', model=model)
# Group 2
D = Agent(name='David', description='An obedient agent.', instruction='Please say your name and your favorite sport.', model=model)
E = Agent(name='Eve', description='An obedient agent.', instruction='Please say your name and your favorite sport.', model=model)
F = Agent(name='Frank', description='An obedient agent.', instruction='Please say your name and your favorite sport.', model=model)
# Parallel Agents
P1 = ParallelAgent(
name='ABC',
description='Parallel group ABC',
sub_agents=[A, B, C],
)
P2 = ParallelAgent(
name='DEF',
description='Parallel group DEF',
sub_agents=[D, E, F],
)
# Reducers
# ABC -> R1
R1 = Agent(name="reducer1", description="Reducer for ABC", instruction="Summarize the responses from agents A, B, and C.", model=model, before_model_callback=print_llmrequest_contents)
S1 = SequentialAgent(name='Group1_Sequential', description="Sequential group for ABC", sub_agents=[P1, R1])
# DEF -> R2
R2 = Agent(name="reducer2", description="Reducer for DEF", instruction="Summarize the responses from agents D, E, and F.", model=model, before_model_callback=print_llmrequest_contents)
S2 = SequentialAgent(name='Group2_Sequential', description="Sequential group for DEF", sub_agents=[P2, R2])
# Running ABC and DEF in parallel
P3 = ParallelAgent(
name='Final_Parallel',
sub_agents=[S1, S2],
)
# Final reduction of previous reducers
R3 = Agent(name="final_reducer", description="Final Reducer", instruction="Summarize the outputs from both groups.", model=model, before_model_callback=print_llmrequest_contents)
S3 = SequentialAgent(name='Final_Sequential', description="Sequential group for final outputs", sub_agents=[P3, R3])So this combines two of those architectures to run in parallel, where each one contains three LLM Agents in Parallel followed by a single LLMAgent reducer in sequence; then both of those reducers go to the final reducer.
Architure looks like:
Sequential1 = [A, B, C] -> Reducer 1
Sequential2 = [D, E, F] -> Reducer 2
[Sequential1, Sequential2] -> Reducer 3
where [] denotes parallel execution.
The bug is that, when you run S3, Reducers R1 and R2 don't see the outputs of the parallel groups that came before them. Although they are present in session.events, they are filtered out and not present in llm_request.contents. However if you just run S1 or S2 (those would each be one instance of the reduce architecture), the reducers DO see the outputs of the parallel groups before them. So its only when nesting these architectures.
Expected behavior
We expect that the parallel sub-agents' outputs are visible to the reducing agent.
Screenshots
Desktop (please complete the following information):
- OS: [e.g. macOS, Linux, Windows] - MacOS
- Python version(python -V): 3.12
- ADK version(pip show google-adk): 1.16
Model Information:
- Are you using LiteLLM: Yes/No -- Yes
- Which model is being used(e.g. gemini-2.5-pro) -- Azure OpenAI 4o
Exploration into the cause and fix
The cause of this situation is happening in this function:
def _is_event_belongs_to_branch(
invocation_branch: Optional[str], event: Event
) -> bool:
"""Check if an event belongs to the current branch.
This is for event context segration between agents. E.g. agent A shouldn't
see output of agent B.
"""
if not invocation_branch or not event.branch:
return True
return invocation_branch.startswith(event.branch)The nested parallel agents have branches named like ParallelAgent.A and ParallelAgent.B; and if A and B are parallel agents themselves, you might see sub branches like ParallelAgent.A.sub1 and ParallelAgent.A.sub2.
When filtering events to decide what contents go to a particular LLM invocation, the above function checks if the invocation branch either equals the event branch; or starts with the event branch. But in the case of the architecture we had, the invocation of the reducer agents would have branch ParallelAgent.A, while the events from the parallel agents before it would have sub branches like ParallelAgent.A.sub1; and since ParallelAgent.A **does not start with ** ParallelAgent.A.sub1, indeed, this is only true in reverse, the reducer on branch ParallelAgent.A doesn't see the events of ParallelAgent.A.sub1.
One proposed solution is to patch this function to perform this check in the other direction as well. If the event's branch starts with the invocation's branch, then accept it too.
def _is_event_belongs_to_branch(invocation_branch, event) -> bool:
"""Check if an event belongs to the current branch.
This is for event context segration between agents. E.g. agent A shouldn't
see output of agent B.
"""
if not invocation_branch or not event.branch:
return True
# Original logic: can see parent branch events
if invocation_branch.startswith(event.branch):
return True
# New logic: can also see child branch events
if event.branch.startswith(invocation_branch):
return True
return FalseIn our testing, this fixes the architecture, allowing the reducers to see the parallel agents' outputs before them. But I'm not sure why this check in the reverse direction wasn't present before, and what this breaks, so I'm hoping to have the discussion about it.
Activity
llalitkumarrr commentedon Nov 11, 2025
Hello @dannovikov ,
Thank you for raising this issue, I tried reproducing your issue and observed the same issue. While running P3 and S3.
@Jacksunwei sharing output relevant code below for your reference:
Code:
Output:
asheeshgarg commentedon Nov 14, 2025
@llalitkumarrr is there a update on this. We are really looking for this issue.
ShaharKatz commentedon Nov 15, 2025
Us to, currently a blocker
llalitkumarrr commentedon Nov 17, 2025
Hello @asheeshgarg and @ShaharKatz ,
There is a PR raised by @dannovikov for this and we are also looking into this, we request your patience till we have an update on this for you.
Thank you for your understanding.
dannovikov commentedon Nov 21, 2025
Hi guys, we found another problem case along the same lines of this issue, which isn't solved by the above proposed solution.
if you have a sequence of parallel agents, each next parallel agent doesn't see the context from previous parallel agents.
Example:
SequentialAgent[
ParallelAgent1[A, B, C],
ParallelAgent2[D, E, F],
ParallelAgent3[G, H, I],
]
Even with the proposed fix, the Agents in Parallel2 cannot see the events of the agents in Parallel1. Likewise the agents in Parallel3 can't see any of the events before them.
The cause of this has also to do with the branch string and the prefix matching rule, and it is not fixed by the suggested bidirectional prefix check.
If you look at the branches of the events, you'll see that A, B and C have branch
parallel_agent1.A
parallel_agent1.B
parallel_agent1.C
meanwhile, the events from agents D, E F have branch
parallel_agent2.D
parallel_agent2.E
parallel_agent2.F
neither of these branches are prefixes of eachother, which is why downstream parallel subagents cant see the outputs from the group before them.
I tried to find a way to solve both of these cases using branch naming logic, but its unclear if its even possible. I couldn't find a way to reconcile parallel agent siblings not seeing each other, with sequential agent siblings being able to see each other.
asheeshgarg commentedon Nov 22, 2025
@Jacksunwei @llalitkumarrr this is important issue for some of our usecase. Appreciate any updates or discussion to move it to closer
feat(branch-context): Implement token-set provenance for parallel age…
13 remaining items
adk-bot commentedon Apr 5, 2026
This issue has been automatically marked as stale because it has not had recent activity for 7 days after a maintainer requested clarification. It will be closed if no further activity occurs within 7 days.
adk-bot commentedon Apr 12, 2026
This has been automatically closed because it has been marked as stale for over 7 days.
adk-bot commentedon Apr 13, 2026
This has been automatically closed because it has been marked as stale for over 7 days.
fix: let agents see finished output of ParallelAgents in other branches
fix: let agents see finished output of ParallelAgents in other branches