Skip to content

Commit d7954a2

Browse files
committed
[bugf][multi-agent-exec][same-named agents collapse into one dict entry]
run_agents_concurrently(return_agent_output_dict=True) keyed results by agent_name and overwrote on collision. agent_name defaults to the same string for every Agent, so a swarm whose agents never set one kept only the last result. MixtureOfAgents is the caller that feels it: step() returns that dict, _run adds one conversation message per entry, and the aggregator then synthesises a single opinion instead of the mixture. Nothing raises. Duplicate keys are suffixed rather than overwritten, so the dict holds one entry per agent as its docstring promises. get_final_agent_answer now pairs agents with entries by position, because a name lookup would resolve every duplicate back to the first agent's key.
1 parent 8b7b80c commit d7954a2

3 files changed

Lines changed: 66 additions & 6 deletions

File tree

swarms/structs/context_utils.py

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -131,11 +131,10 @@ def get_final_agent_answer(
131131
Returns:
132132
The same mapping with transcripts replaced by answers.
133133
"""
134+
# Paired by position, not by name: the mapping is built in agent order
135+
# and duplicate names are suffixed to keep one entry per agent, so a
136+
# name lookup would resolve every duplicate to the first agent's key.
134137
answers = dict(agent_outputs)
135-
for agent in agents:
136-
name = getattr(agent, "agent_name", None)
137-
if name in answers:
138-
answers[name] = agent_answer(
139-
agent, fallback=answers[name]
140-
)
138+
for agent, key in zip(agents, answers):
139+
answers[key] = agent_answer(agent, fallback=answers[key])
141140
return answers

swarms/structs/multi_agent_exec.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,16 @@ def run_agents_concurrently(
169169
or getattr(agent, "name", None)
170170
or str(agent)
171171
)
172+
# agent_name defaults to the same string for every
173+
# Agent, so a swarm whose agents did not set one had
174+
# every result land on one key and only the last
175+
# survive. Suffix instead of overwrite: callers ask for
176+
# this dict to get one entry per agent.
177+
if name in output_dict:
178+
suffix = 2
179+
while f"{name} ({suffix})" in output_dict:
180+
suffix += 1
181+
name = f"{name} ({suffix})"
172182
output_dict[name] = result
173183
return output_dict
174184
else:
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
"""Tests for swarms.structs.multi_agent_exec."""
2+
3+
from swarms.structs.multi_agent_exec import run_agents_concurrently
4+
5+
6+
class _Stub:
7+
"""Minimal stand-in for an Agent: a name and a fixed answer."""
8+
9+
def __init__(self, agent_name: str, answer: str):
10+
self.agent_name = agent_name
11+
self._answer = answer
12+
13+
def run(self, task=None, **kwargs):
14+
return self._answer
15+
16+
17+
def test_output_dict_keeps_one_entry_per_agent_when_names_collide():
18+
"""Agent.agent_name defaults to the same string for every agent.
19+
20+
A swarm whose agents never set one used to collapse into a single dict
21+
entry, so MixtureOfAgents recorded one worker per layer and aggregated a
22+
single opinion instead of the mixture it was asked for.
23+
"""
24+
agents = [
25+
_Stub("swarm-worker-01", f"answer-{i}") for i in range(3)
26+
]
27+
28+
outputs = run_agents_concurrently(
29+
agents=agents, task="t", return_agent_output_dict=True
30+
)
31+
32+
assert len(outputs) == 3, outputs
33+
assert sorted(outputs.values()) == [
34+
"answer-0",
35+
"answer-1",
36+
"answer-2",
37+
]
38+
39+
40+
def test_output_dict_leaves_distinct_names_untouched():
41+
agents = [_Stub(f"worker-{i}", f"answer-{i}") for i in range(3)]
42+
43+
outputs = run_agents_concurrently(
44+
agents=agents, task="t", return_agent_output_dict=True
45+
)
46+
47+
assert outputs == {
48+
"worker-0": "answer-0",
49+
"worker-1": "answer-1",
50+
"worker-2": "answer-2",
51+
}

0 commit comments

Comments
 (0)