Agents under different parents may share a name: no warning, and find_agent() silently resolves to the first
- Lingua principale
- Python
- Stelle
- 21.5k
- Fork
- 4k
- Merge medio
- 1g 22h
- PR unite (30g)
- 31
Descrizione
## 🔴 Required Information
**Describe the Bug:**
`validate_sub_agents_unique_names` checks one `sub_agents` list at a time, so two agents that share a name under **different parents** are accepted with no warning. Every by-name resolution then silently picks the first match in a depth-first walk, and the other agent cannot be reached by name at all.
`find_agent` ([`base_agent.py:512`](https://github.com/google/adk-python/blob/main/src/google/adk/agents/base_agent.py#L512)) returns the first descendant whose name matches. Two callers make that visible:
- [`llm_agent.py:1001`](https://github.com/google/adk-python/blob/main/src/google/adk/agents/llm_agent.py#L1001) — `transfer_to_agent` resolves the target with `root_agent.find_agent(agent_name)`. The second agent can never be transferred to.
- [`_agent_router.py:133`](https://github.com/google/adk-python/blob/main/src/google/adk/agents/_agent_router.py#L133) — a resumed invocation picks the agent to continue with `root_agent.find_agent(event.author)`. When two agents share a name, the one that resumes is whichever the walk reaches first, which is not necessarily the one that authored the event. I have verified `find_agent`'s behaviour by execution; this second consequence follows from reading the call, and I have not reproduced a full resume.
The sibling case is already treated as a problem — `validate_sub_agents_unique_names` logs *"Found duplicate sub-agent names ... All sub-agents must have unique names."* The check just does not extend past one level.
The node layer already solves the same ambiguity: the docstring of `restore_branch_from_history` says nodes are *"matched by their static path (run ids stripped) so that two nodes sharing a name (e.g. the same sub-agent mounted under two parents) are disambiguated"*. That disambiguation is not available to `find_agent`.
**Steps to Reproduce:**
1. `pip install google-adk==2.9.0`
2. Save the script under *Minimal Reproduction Code* as `repro.py`
3. `python repro.py`
**Expected Behavior:**
Either the uniqueness check covers the whole tree rather than one list of siblings, or by-name resolution is path-aware the way node matching already is.
**Observed Behavior:**
```
A. two agents named "alpha", under DIFFERENT parents
warning at construction? NO
find_agent("alpha") -> the one under branch1
the one under branch2 is reachable by name? False <- unreachable
B. NEGATIVE CONTROL — two agents named "alpha" as SIBLINGS
warning at construction? YES
C. NEGATIVE CONTROL — unique names
find_agent("beta") -> under branch2 (correct)
```
B is the control that matters: the existing validator does fire, so the case is already recognised — it is the scope of the check that differs, not the intent.
**Environment Details:**
- ADK Library Version: `google-adk 2.9.0` (same code on `main`)
- Desktop OS: macOS 26.6.2 (arm64)
- Python Version: 3.12.13
**Model Information:**
- Are you using LiteLLM: No
- Which model is being used: N/A — no model is involved
---
## 🟡 Optional Information
**Additional Context — scope, stated restrictively:**
- Verified by execution: construction raises no warning for the cousin case, and `find_agent` returns the first match so the second agent is unreachable by name.
- **Not** verified by execution: the effect on a real resumed invocation, or on a live `transfer_to_agent` round trip with a model. Those follow from the two call sites above, which I read rather than ran.
- Whether this is worth fixing depends on how likely you consider a tree with repeated names. I am not claiming it is common — only that nothing reports it, and that the sibling check shows the intent is for names to be unique.
- I have deliberately not proposed a patch: widening the check to the whole tree would reject trees that work today, which is a compatibility decision that is yours.
**Minimal Reproduction Code:**
```python
"""Two agents can share a name when they sit under different parents.
Nothing warns, and every by-name resolution silently picks the first."""
import logging, io
from google.adk.agents.base_agent import BaseAgent
log = io.StringIO()
logging.getLogger('google_adk').addHandler(logging.StreamHandler(log))
logging.getLogger('google_adk').setLevel(logging.WARNING)
def build(label, tree):
log.truncate(0); log.seek(0)
root = tree()
warned = 'duplicate' in log.getvalue().lower()
print(f'{label}\n warning at construction? {"YES" if warned else "NO"}')
return root
root_a = build('A. two agents named "alpha", under DIFFERENT parents',
lambda: BaseAgent(name='root', sub_agents=[
BaseAgent(name='branch1', sub_agents=[BaseAgent(name='alpha')]),
BaseAgent(name='branch2', sub_agents=[BaseAgent(name='alpha')]),
]))
first = root_a.find_agent('alpha')
under_b2 = root_a.find_agent('branch2').find_agent('alpha')
print(f' find_agent("alpha") -> the one under {first.parent_agent.name}')
print(f' the one under branch2 is reachable by name? '
f'{first is under_b2} <- unreachable\n')
root_b = build('B. NEGATIVE CONTROL — two agents named "alpha" as SIBLINGS',
lambda: BaseAgent(name='root', sub_agents=[
BaseAgent(name='alpha'), BaseAgent(name='alpha')]))
print()
root_c = build('C. NEGATIVE CONTROL — unique names',
lambda: BaseAgent(name='root', sub_agents=[
BaseAgent(name='branch1', sub_agents=[BaseAgent(name='alpha')]),
BaseAgent(name='branch2', sub_agents=[BaseAgent(name='beta')]),
]))
print(f' find_agent("beta") -> under {root_c.find_agent("beta").parent_agent.name} (correct)')
```
**How often has this issue occurred?:**
- Always (100%) — deterministic; no model, no network, no timing involved.
Guida per i contributori
Apri la guida per i contributori
Valutazione
Questa issue non è ancora stata valutata.