构建使用 Google ADK 的多代理系统时的两个棘手陷阱
发布: (2026年4月28日 GMT+8 17:30)
4 分钟阅读
原文: Dev.to
Source: Dev.to
父代理冲突
发生了什么
# Agents/my_app/root_agent.py
from Agents.my_app.sub_agent_a.agent import sub_agent_a
from Agents.my_app.sub_agent_b.agent import sub_agent_b
def _build_sub_agents() -> list:
return [sub_agent_a, sub_agent_b]
root_agent = LlmAgent(
name="my_app",
sub_agents=_build_sub_agents(),
# …
)
- 在本地使用
adk web正常工作。 - 在 Cloud Run 上崩溃,报错:
pydantic_core._pydantic_core.ValidationError: 1 validation error for LlmAgent
Value error, Agent `SubAgentA` already has a parent agent,
current parent: `my_app`, trying to add: `my_app`
为什么会出现
ADK的agent_loader会在 每一次请求 上调用importlib.import_module(agent_name)。- 第一次请求时模块被加载,
root_agent被创建,每个子代理得到parent_agent = root_agent。 - 第二次请求时模块被重新导入,但模块级单例
sub_agent_a和sub_agent_b仍是上一次加载时的 同一 Python 对象,它们仍保留着旧的parent_agent。 - 当新的
LlmAgent再次尝试为它们分配parent_agent时,验证器抛出错误。
LlmAgent.__init__ 中的简化片段:
for sub in sub_agents:
if sub.parent_agent is not None:
raise ValueError(f"Agent `{sub.name}` already has a parent agent ...")
sub.parent_agent = self
解决办法
在构建新的根代理之前重置 parent_agent 引用:
def _build_sub_agents() -> list:
agents = [sub_agent_a, sub_agent_b]
for agent in agents:
agent.parent_agent = None # 在每次重新加载前重置
return agents
该赋值是同步且安全的,因为随后会立即设置新的父代理。
指令字符串中找不到上下文变量
发生了什么
KeyError: 'Context variable not found: `hostname`.'
堆栈指向 google/adk/utils/instructions_utils.py,该文件中 ADK 会将会话状态注入到指令字符串中。
为什么会出现
ADK 使用正则 r'{+[^{}]*}+' 扫描指令文本,并将每个 {var_name} 替换为对应的会话状态值。任何看起来像 {word} 的模式都会被当作变量,即使你本意是把它当作字面占位符。
示例指令:
The URL format is `https://{hostname}/api/{resource_id}/`
ADK 会尝试解析 {hostname} 和 {resource_id};如果它们不在会话状态中,就会抛出 KeyError。
使用双大括号转义 ({{hostname}}) 不起作用,因为正则仍然会匹配该模式。
解决办法
避免在指令中使用大括号作为字面模板文本。改用其他分隔符,例如尖括号、方括号或直接使用普通文字:
The URL format is `https:///api//`
任何 {word} 模式都会被解释为会话变量,所以在需要占位符但不希望被解析时,请使用反引号、[word] 或其他非大括号的分隔符。
两个坑的总结
| Bug | 触发条件 | 解决方案 |
|---|---|---|
| 父代理冲突 | 模块级单例子代理 + ADK 每次请求重新加载模块 | 在将子代理传给构造函数之前,将 agent.parent_agent = None 重置 |
| 找不到上下文变量 | 指令字符串中的 {word} 模式 | 使用反引号、[word](或其他非大括号分隔符)来表示字面占位符 |