本实验讲解如何在 Google Agent Development Kit(Google ADK) 中编排多智能体系统(Multi-Agent Systems)。
本实验假设你已经熟悉 ADK 基础知识和工具(Tool)的使用,即已完成以下两个实验:
在本实验中,你将:
本实验使用讲师提供的实验平台(Qwiklabs/Google Skills)分配的临时学员账号和临时 GCP 项目,不会用到你的个人账号。



片刻之后,Google Cloud 控制台就会在此标签页中打开。
qwiklabs-gcp-01-xxxxxxxxxxxx)复制下来,粘贴到本页面右上角的「Project ID」输入框中。填好后,本手册里所有命令中橙色高亮的 YOUR_PROJECT_ID 都会自动替换成你的真实项目 ID——之后所有命令直接点复制按钮粘贴到 Cloud Shell 即可,无需手工改任何参数。Agent Development Kit 让开发者能够从生成式模型中获得更可靠、更复杂的多步行为。与其编写一段冗长复杂、结果未必可靠的提示词,不如构建一个由多个简单 Agent 组成的流程,让它们通过分工协作来解决复杂问题。
这种架构方式有几个关键优势:

在 ADK 中,你以树状结构组织 Agent。这可以限制树中每个 Agent 的转移(transfer)选项,让对话在树中可能经过的路径更可控、更可预测。层级结构的好处包括:
整个结构始终从定义在 root_agent 变量中的 Agent 开始(它面向用户的 name 可以是别的名字)。root_agent 可以作为一个或多个子 Agent(sub-agents) 的父 Agent(parent),每个子 Agent 又可以有自己的子 Agent。
在本实验环境中,Agent Platform API 已经为你启用。如果你在自己的项目中操作,需要先进入 Agent Platform 页面,按提示启用该 API。
)。
Welcome to Cloud Shell! 且提示行显示你的项目 ID,即表示 Cloud Shell 已就绪。
。
)查看文件。
)打开文件浏览器。gcloud storage cp -r gs://YOUR_PROJECT_ID-bucket/* .
PATH 环境变量、安装 ADK 并安装本实验的其它依赖:export PATH=$PATH:"/home/${USER}/.local/bin"
python3 -m pip install google-adk[otel-gcp]==1.30.0 -r adk_multiagent_systems/requirements.txt
安装大约需要一两分钟,出现 Successfully installed ... 字样即为成功。
对话总是从定义为 root_agent 变量的 Agent 开始。
父 Agent 的默认行为是:理解每个子 Agent 的 description(描述),并判断在对话的某个时刻是否应当把对话控制权转移(transfer) 给某个子 Agent。
你也可以在父 Agent 的 instruction 中直接用子 Agent 的名字(即它们 name 参数的值,不是 Python 变量名)来引导转移。来试一个例子:
.env 文件:cd ~/adk_multiagent_systems
cat << EOF > parent_and_subagents/.env
GOOGLE_GENAI_USE_VERTEXAI=TRUE
GOOGLE_CLOUD_PROJECT=YOUR_PROJECT_ID
GOOGLE_CLOUD_LOCATION=global
MODEL=gemini-3.5-flash
EOF
这些变量的作用如下:GOOGLE_GENAI_USE_VERTEXAI=TRUE 表示使用 Agent Platform 进行认证,而不是 Gemini API key 认证。GOOGLE_CLOUD_PROJECT 和 GOOGLE_CLOUD_LOCATION 提供模型调用所关联的项目和位置。MODEL 存在这里,以便作为环境变量被加载。.env 文件复制到 workflow_agents 目录(实验后面会用到):cp parent_and_subagents/.env workflow_agents/.env
steering 的 root_agent(这个 name 用于在 ADK 的开发 UI 和命令行界面中标识它)。它会问用户一个问题(是已经知道想去哪旅行,还是需要帮忙决定),用户的回答会帮助这个"导流" Agent 决定把对话导向哪个子 Agent。注意它的 instruction 很简单,并没有提到子 Agent,但它能感知到子 Agent 的 description。sub_agents=[travel_brainstormer, attractions_planner]

Ctrl + S(Mac 为 Cmd + S)。仅关闭标签页不会自动保存。sub_agents 来定义。cd ~/adk_multiagent_systems
adk run parent_and_subagents
[user]: 提示符后,向 Agent 打个招呼:hello
示例输出(你的输出可能略有不同):[user]: hello
[steering]: Hello! Welcome to your travel adventure. Do you already know where you'd like to travel, or would you like some help deciding?
I could use some help deciding.
示例输出(你的输出可能略有不同):[user]: I could use some help deciding.
[travel_brainstormer]: Hello! I'd love to help you decide on the perfect destination for your next trip.
To start, here are a few highly popular countries that travelers love:
* **Italy:** Famous for food, history, art, and beautiful landscapes.
* **Japan:** A unique blend of ultra-modern cities, ancient temples, stunning nature, and incredible cuisine.
...
description 就把对话转移给了合适的子 Agent。user: 提示符处输入 exit 结束对话。instruction 中写明何时转移给哪个子 Agent。在 agent.py 文件中,把以下内容追加到 root_agent 的 instruction 里:If they need help deciding, send them to
'travel_brainstormer'.
If they know what country they'd like to visit,
send them to the 'attractions_planner'.
Ctrl + S(Mac 为 Cmd + S)。仅关闭标签页不会自动保存。adk run parent_and_subagents
hello
I would like to go to Japan.
示例输出(你的输出可能略有不同):[user]: I would like to go to Japan.
[attractions_planner]: Japan is an incredible destination with a perfect blend of ancient traditions and futuristic cities!
Here are some of the top attractions and regions to consider for your trip...
Actually I don't know what country to visit.
示例输出(你的输出可能略有不同):user: actually I don't know what country to visit
[travel_brainstormer]: Okay! I can help you brainstorm some countries for travel...
disallow_transfer_to_peers 参数设为 True。exit 结束会话。ADK 中的每一段对话都包含在一个 Session(会话)中,参与对话的所有 Agent 都可以访问它。会话包含对话历史,Agent 会把它作为生成回复的上下文的一部分。会话还包含一个 session state(会话状态)字典,你可以用它更精细地控制最重要的信息以及这些信息的访问方式。
这对于在 Agent 之间传递信息,或在与用户的整段对话中维护一个简单的数据结构(比如任务清单)特别有用。
来试试往 state 里写入和读取:
# Tools 注释标题之后,粘贴以下函数定义:def save_attractions_to_state(
tool_context: ToolContext,
attractions: List[str]
) -> dict[str, str]:
"""Saves the list of attractions to state["attractions"].
Args:
attractions [str]: a list of strings to add to the list of attractions
Returns:
None
"""
# Load existing attractions from state. If none exist, start an empty list
existing_attractions = tool_context.state.get("attractions", [])
# Update the 'attractions' key with a combo of old and new lists.
# When the tool is run, ADK will create an event and make
# corresponding updates in the session's state.
tool_context.state["attractions"] = existing_attractions + attractions
# A best practice for tools is to return a status message in a return dict
return {"status": "success"}

ToolContext 的形式传入你的工具函数。你只需要声明一个参数来接收它(这里的参数名是 tool_context),然后就可以通过 tool_context 访问会话信息,例如对话历史(tool_context.events)和会话状态字典(tool_context.state)。当工具函数修改了 tool_context.state 字典,这些变更会在工具执行结束后反映到会话的 state 中。tools 参数,把工具挂上去:tools=[save_attractions_to_state]

instruction 中追加以下要点:- When they reply, use your tool to save their selected attraction
and then provide more possible attractions.
- If they ask to view the list, provide a bulleted list of
{ attractions? } and then suggest some more.

{ attractions? }。这是 ADK 的 key templating(键模板) 特性,会从 state 字典中加载 attractions 键的值。键名后面的问号可以避免该字段尚不存在时报错。adk web --allow_origins "regex:https://.*\.cloudshell\.dev"
输出:INFO: Started server process [2434]
INFO: Waiting for application startup.
+-------------------------------------------------------+
| ADK Web Server started |
| |
| For local testing, access at http://localhost:8000. |
+-------------------------------------------------------+
INFO: Application startup complete.
INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)

hello 开始对话。I'd like to go to Egypt.
你应该会被转移到 attractions_planner,并得到一份景点列表。I'll go to the Sphinx

What is on my list?
instruction 的要求,把你的清单以项目符号列表的形式返回。"父 → 子" 转移适合这样的场景:你有多个专职子 Agent,并且希望用户与它们逐个交互。
但如果你希望多个 Agent 一个接一个自动执行、中间不等待用户输入,就可以使用工作流 Agent(workflow agents)。典型场景包括:
为完成这类任务,工作流 Agent 拥有子 Agent,并保证每个子 Agent 都会执行。ADK 提供三种内置工作流 Agent,也支持自定义:
SequentialAgent(顺序)LoopAgent(循环)ParallelAgent(并行)在实验的剩余部分,你将构建一个由多个 LLM Agent、工作流 Agent 和工具组成的多智能体系统,用来控制整个流程。
具体来说,你要构建的 Agent 将为一部新的爆款电影撰写立项提案(pitch document):一部以历史人物生平为原型的传记片。子 Agent 们将负责资料研究、由"编剧 + 评审"组成的迭代写作循环,最后还有几个子 Agent 帮忙头脑风暴选角方案,并利用历史票房数据对票房表现做出预测。
最终,你的多智能体系统会长这样(可点击图片放大):

不过,你会先从一个更简单的版本开始。
SequentialAgent 按线性顺序执行它的子 Agent:sub_agents 列表中的每个子 Agent 按定义顺序依次执行。
这非常适合"任务必须按特定顺序执行、且上一个任务的输出是下一个任务的输入"的工作流。
在本任务中,你将运行一个 SequentialAgent,构建电影提案多智能体系统的第一个版本。初版结构如下:

SequentialAgent,包含:LoopAgent 中多轮执行的 Agent 特别有用——每次执行的输出都会被保存下来。wikipedia Python 库联网查资料,而 Wikimedia 现在会拦截该库默认的 User-Agent,导致工具收到空响应、运行时抛出 JSONDecodeError: Expecting value: line 1 column 1 (char 0)。请在 workflow_agents/agent.py 文件的最顶部加入以下两行,给它设置一个自定义 User-Agent:import wikipedia
wikipedia.set_user_agent("adk-lab/1.0 (https://example.com; lab@example.com)")
加好后按 Ctrl + S(Mac Cmd + S)保存。
--reload_agents 参数,让 Agent 代码变更后自动热加载:cd ~/adk_multiagent_systems
adk web --allow_origins "regex:https://.*\.cloudshell\.dev" --reload_agents
hello 开始对话。Agent 可能需要一点时间响应,它应该会请你输入一位历史人物,以启动电影剧情生成。Zhang Zhongjing——公元 2 世纪中国著名医学家(张仲景)。Ada Lovelace——英国数学家、作家,以早期计算机方面的工作闻名。Marcus Aurelius——以哲学著作闻名的罗马皇帝。
如果没看到 Agent 报告已生成文件,或者想换个人物再试一次,可以点击右上角的 + New Session 重新开始。
)(代表一轮对话),打开事件视图(event view)。
LoopAgent 按既定顺序执行它的子 Agent,然后不等待用户输入,直接从头再来一遍。循环会一直重复,直到达到迭代次数上限,或某个子 Agent 发起退出循环的调用(通常是调用内置的 exit_loop 工具)。
这适合需要持续打磨、监控或周期性工作流的任务,例如:
你将为电影提案 Agent 添加一个 LoopAgent,允许在创作故事时进行多轮研究与迭代。除了打磨剧本,这还允许用户给出更模糊的输入:用户可以不指定具体历史人物,只说想要一个"古代医生"的故事,研究-写作迭代循环会先找到合适的人选,再创作故事。

修改后的 Agent 流程如下:
SequentialAgent 现在包含: LoopAgent 作为序列的开始,它包含: SequentialAgent,它再把控制权交给序列中的下一个 Agent:file_writer——与之前一样,负责给电影起名并把结果写入文件。按以下步骤修改:
from google.adk.tools import exit_loop
from google.adk.models import Gemini

exit_loop,并且 instruction 中写明了何时使用它:critic = Agent(
name="critic",
model=Gemini(model=model_name, retry_options=RETRY_OPTIONS),
description="Reviews the outline so that it can be improved.",
instruction="""
INSTRUCTIONS:
Consider these questions about the PLOT_OUTLINE:
- Does it meet a satisfying three-act cinematic structure?
- Do the characters' struggles seem engaging?
- Does it feel grounded in a real time period in history?
- Does it sufficiently incorporate historical details from the RESEARCH?
If the PLOT_OUTLINE does a good job with these questions, exit the writing loop with your 'exit_loop' tool.
If significant improvements can be made, use the 'append_to_state' tool to add your feedback to the field 'CRITICAL_FEEDBACK'.
Explain your decision and briefly summarize the feedback you have provided.
PLOT_OUTLINE:
{ PLOT_OUTLINE? }
RESEARCH:
{ research? }
""",
before_model_callback=log_query_to_model,
after_model_callback=log_model_response,
tools=[append_to_state, exit_loop]
)

LoopAgent,构成 researcher、screenwriter、critic 的迭代循环。每一轮循环都以对当前成果的批评意见收尾,推动下一轮的改进。把以下内容粘贴到现有的 film_concept_team SequentialAgent 之上:writers_room = LoopAgent(
name="writers_room",
description="Iterates through research and writing to improve a movie plot outline.",
sub_agents=[
researcher,
screenwriter,
critic
],
max_iterations=5,
)

LoopAgent 的创建包含 max_iterations 参数,它定义循环最多运行多少轮。即使你计划用其它方式中断循环,给总迭代次数设置上限也是个好习惯。SequentialAgent,用刚创建的 writers_room LoopAgent 替换其中的 researcher 和 screenwriter;file_writer 仍然留在序列末尾。film_concept_team 现在应该是这样:film_concept_team = SequentialAgent(
name="film_concept_team",
description="Write a film plot outline and save it as a text file.",
sub_agents=[
writers_room,
file_writer
],
)

hello 开始新对话。an industrial designer who made products for the masses(为大众设计产品的工业设计师)a cartographer (a map maker)(制图师)that guy who made crops yield more food(让粮食增产的那个人)
ParallelAgent 支持并发执行它的子 Agent。每个子 Agent 在自己的分支中运行,默认情况下,并行执行期间它们彼此不直接共享对话历史或 state。
这对于可以拆成相互独立、可同时处理的子任务非常有价值——使用 ParallelAgent 能显著缩短整体执行时间。
在本任务中,你将为电影提案补充两份附加报告——潜在票房表现研究和初步选角建议——来强化这部新片的提案。

修改后的 Agent 流程如下:
SequentialAgent 现在包含: LoopAgent,保持不变,包含: ParallelAgent 随后执行,包含: 虽然这个示例展示的大多是现实中由人类团队完成的创意工作,但这个工作流演示了如何把一条复杂的任务链拆给多个子 Agent,产出复杂文档的草稿,再由人类团队成员编辑和润色。
ParallelAgent 粘贴到 workflow_agents/agent.py 文件的 # Agents 标题之下:box_office_researcher = Agent(
name="box_office_researcher",
model=Gemini(model=model_name, retry_options=RETRY_OPTIONS),
description="Considers the box office potential of this film",
instruction="""
PLOT_OUTLINE:
{ PLOT_OUTLINE? }
INSTRUCTIONS:
Write a report on the box office potential of a movie like that described in PLOT_OUTLINE based on the reported box office performance of other recent films.
""",
output_key="box_office_report"
)
casting_agent = Agent(
name="casting_agent",
model=Gemini(model=model_name, retry_options=RETRY_OPTIONS),
description="Generates casting ideas for this film",
instruction="""
PLOT_OUTLINE:
{ PLOT_OUTLINE? }
INSTRUCTIONS:
Generate ideas for casting for the characters described in PLOT_OUTLINE
by suggesting actors who have received positive feedback from critics and/or
fans when they have played similar roles.
""",
output_key="casting_report"
)
preproduction_team = ParallelAgent(
name="preproduction_team",
sub_agents=[
box_office_researcher,
casting_agent
]
)

preproduction_team 加在 writers_room 和 file_writer 之间:film_concept_team = SequentialAgent(
name="film_concept_team",
description="Write a film plot outline and save it as a text file.",
sub_agents=[
writers_room,
preproduction_team,
file_writer
],
)

INSTRUCTIONS:
- Create a marketable, contemporary movie title suggestion for the movie described in the PLOT_OUTLINE. If a title has been suggested in PLOT_OUTLINE, you can use it, or replace it with a better one.
- Use your 'write_file' tool to create a new txt file with the following arguments:
- for a filename, use the movie title
- Write to the 'movie_pitches' directory.
- For the 'content' to write, include:
- The PLOT_OUTLINE
- The BOX_OFFICE_REPORT
- The CASTING_REPORT
PLOT_OUTLINE:
{ PLOT_OUTLINE? }
BOX_OFFICE_REPORT:
{ box_office_report? }
CASTING_REPORT:
{ casting_report? }

Ctrl + S(Mac 为 Cmd + S)。仅关闭标签页不会自动保存。hello 开始对话。that actress who invented the technology for wifi(发明了 WiFi 底层技术的那位女演员)an exciting chef(一位充满魅力的厨师)key players in the worlds fair exhibitions(世界博览会上的关键人物)
当预定义的 SequentialAgent、LoopAgent、ParallelAgent 无法满足需求时,CustomAgent 提供了实现全新工作流逻辑的灵活性。你可以自定义流程控制、条件执行,或子 Agent 之间的状态管理模式。这适用于复杂工作流、有状态编排,或需要把自定义业务逻辑集成到框架编排层的场景。
创建 CustomAgent 超出了本实验的范围,但知道有这个能力就够了——需要时它就在那里!
在本实验中,你学会了:
原始实验手册最后更新:2026-07-26;实验最后验证:2026-07-26。
Copyright 2026 Google LLC. Google 和 Google 徽标是 Google LLC 的商标。本页面为面向实验学员的中文改编版。