本实验讲解如何在 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 的商标。本页面为面向实验学员的中文改编版。