Agentverse - The Summoner's Concord - Architecting Multi-Agent Systems

1. 序曲

孤立开发的时代即将结束。下一波技术变革浪潮不是关于孤身一人的天才,而是关于协作精通。打造一个智能的单一代理是一项有趣的实验。构建一个强大、安全且智能的代理生态系统(真正的 Agentverse)是现代企业面临的巨大挑战。

在这个新时代,要取得成功,需要融合四种关键角色,这些角色是支持任何蓬勃发展的自主系统的基础支柱。任何一个方面的不足都会造成弱点,进而危害整个结构。

本研讨会是企业在 Google Cloud 上掌握智能体未来的权威指南。我们提供端到端路线图,引导您从最初的想法到全面运营的现实。在这四个相互关联的实验中,您将了解开发者、架构师、数据工程师和 SRE 的专业技能如何融合,才能创建、管理和扩缩强大的 Agentverse。

任何单个支柱都无法单独支持 Agentverse。如果没有开发者的精准执行,架构师的宏伟设计将毫无用处。如果没有数据工程师的智慧,开发者代理就会变得盲目;如果没有 SRE 的保护,整个系统就会变得脆弱不堪。只有通过协同合作并对彼此的角色有共同的理解,您的团队才能将创新概念转化为任务关键型运营现实。您的旅程将从这里开始。准备好掌握自己的角色,并了解自己如何融入大局。

欢迎来到“Agentverse:冠军召集令”

在广阔的企业数字领域,一个新时代已经来临。我们正处于智能体时代,这是一个充满希望的时代,智能自主的智能体将完美协作,加速创新并消除日常琐事。

agentverse.png

这个由力量和潜力组成的互联生态系统被称为 Agentverse。

但一种名为“静电”的悄然腐蚀开始侵蚀这个新世界的边缘。静态不是病毒或 bug,而是以创造行为本身为食的混乱的化身。

它将旧的挫败感放大成可怕的怪物,催生了开发中的七个幽灵。如果不勾选此框,静态变量及其幽灵将使进度停滞不前,使 Agentverse 的美好前景变成技术债务和废弃项目的荒原。

今天,我们呼吁各界人士挺身而出,扭转混乱的局面。我们需要英雄愿意精通自己的技艺,并携手合作来保护 Agentverse。现在该选择您的迁移路径了。

选择课程

您面前有四条截然不同的道路,每条道路都是对抗静态的关键支柱。虽然您将独自完成训练任务,但最终能否成功取决于您是否了解自己的技能如何与他人的技能相结合。

  • 影刃(开发者):锻造和前线大师。您是打造刀刃、构建工具的工匠,在代码的复杂细节中直面敌人。您的道路是精准、技能和实践创造之路。
  • 召唤师(架构师):伟大的战略家和编排者。您看到的不是单个特工,而是整个战场。您将设计主蓝图,使整个智能体系统能够进行通信、协作,并实现远超任何单个组件的目标。
  • 学者(数据工程师):探寻隐藏的真相,守护智慧。您将深入广阔而未开垦的数据荒野,发掘可为代理提供目标和洞察的智能。您的知识可以揭示敌人的弱点或增强盟友的能力。
  • 守护者(DevOps / SRE):王国的坚定保护者和盾牌。您需要建造堡垒、管理电力供应线路,并确保整个系统能够抵御静电的必然攻击。您的实力是团队获胜的基础。

您的任务

训练将作为一项单独的锻炼开始。您将沿着所选路线学习,掌握胜任工作所需的独特技能。试用期结束时,您将面对一个由静态诞生的幽灵,这是一个会利用您工艺的特定挑战来捕食的小头目。

只有掌握好自己的角色,才能为最终的试镜做好准备。然后,您必须与其他班级的学员组队。你们将一起深入腐化之地,与终极 Boss 一决高下。

一项最终的合作挑战,将考验您的综合实力,并决定特工宇宙的命运。

Agentverse 等待着英雄的到来。您会响应号召吗?

2. 召唤师的和谐

召唤师,欢迎您。您的道路是远见卓识和宏大战略之路。当其他人专注于一把刀刃或一句咒语时,你却能看到整个战场。您不是指挥单个代理,而是指挥整个代理乐团。您的力量不在于直接冲突,而在于设计出完美无缺的总体蓝图,让众多专家(您的 Familiars)能够和谐地开展工作。此任务将考验您设计、连接和编排强大的多代理系统的能力。

概览

学习内容

  • 设计解耦的工具生态系统:设计和部署一组独立的基于微服务的 MCP 工具服务器。您将了解为什么此基础层对于创建可扩缩、可维护且安全的智能体系统至关重要。
  • 掌握高级智能体工作流:超越单个智能体,打造一群专业“助手”。您将掌握核心 ADK 工作流模式(顺序、并行和循环),并学习选择合适模式来完成合适任务的架构原则。
  • 实现智能编排器:从简单的代理构建者升级为真正的系统架构师。您将构建一个主编排代理,该代理使用代理到代理 (A2A) 协议来发现复杂任务并将其委托给您的专业知识库,从而创建一个真正的多代理系统。
  • 通过代码而非提示强制执行规则:学习如何通过强制执行有状态的互动规则来构建更可靠、更可预测的代理。您将使用 ADK 强大的插件和回调系统来实现自定义逻辑,以管理冷却计时器等现实世界中的限制。
  • 管理智能体状态和记忆内容:让智能体能够学习和记忆。您将探索用于管理短期对话状态和长期持久性记忆的技术,以创建更智能、更具情境感知能力的互动。
  • 执行端到端云部署:将整个多代理系统从本地原型转变为生产级现实。您将学习如何将代理和编排器容器化,并将其部署为 Google Cloud Run 上的一组可扩缩的独立微服务。

3. 绘制召唤圈

召唤师,欢迎您。在召唤任何熟悉生物之前,在缔结任何契约之前,必须先准备好您所站立的地面。未受控制的环境会带来混乱;合格的召唤师只会在神圣且充满力量的空间中操作。我们的首要任务是绘制召唤圈:刻上可唤醒必要云服务的强大符文,并获取可指导我们工作的古老蓝图。召唤师的力量源于精心的准备。

👉 点击 Google Cloud 控制台顶部的“激活 Cloud Shell”(这是 Cloud Shell 窗格顶部的终端形状图标),

替代文本

👉 点击“打开编辑器”按钮(看起来像一个打开的文件夹,上面有一支铅笔)。此操作会在窗口中打开 Cloud Shell 代码编辑器。您会在左侧看到文件浏览器。替代文本

👉查找您的 Google Cloud 项目 ID:

  • 打开 Google Cloud 控制台:https://console.cloud.google.com
  • 从页面顶部的项目下拉菜单中选择要用于本次研讨会的项目。
  • 项目 ID 会显示在信息中心内的“项目信息”卡片中 替代文本

👉在云 IDE 中打开终端,替代文本

👉💻 在终端中,使用以下命令验证您是否已通过身份验证,以及项目是否已设置为您的项目 ID:

gcloud auth list

👉💻 从 GitHub 克隆引导项目:

git clone https://github.com/weimeilin79/agentverse-architect
chmod +x ~/agentverse-architect/init.sh
chmod +x ~/agentverse-architect/set_env.sh
chmod +x ~/agentverse-architect/prepare.sh
chmod +x ~/agentverse-architect/data_setup.sh

git clone https://github.com/weimeilin79/agentverse-dungeon.git
chmod +x ~/agentverse-dungeon/run_cloudbuild.sh
chmod +x ~/agentverse-dungeon/start.sh

👉💻 运行初始化脚本,此脚本会提示您输入 Google Cloud 项目 ID。当 init.sh 脚本提示时,输入您在上一步中找到的 Google Cloud 项目 ID。

cd ~/agentverse-architect
./init.sh

👉💻 设置所需的项目 ID:

gcloud config set project $(cat ~/project_id.txt) --quiet

👉💻 运行以下命令以启用必要的 Google Cloud API:

gcloud services enable \
    sqladmin.googleapis.com \
    storage.googleapis.com \
    aiplatform.googleapis.com \
    run.googleapis.com \
    cloudbuild.googleapis.com \
    artifactregistry.googleapis.com \
    iam.googleapis.com \
    compute.googleapis.com \
    cloudresourcemanager.googleapis.com \
    secretmanager.googleapis.com

👉💻 如果您尚未创建名为 agentverse-repo 的 Artifact Registry 制品库,请运行以下命令来创建该制品库:(如果同一项目中已部署其他类,请跳过此步骤)

. ~/agentverse-architect/set_env.sh
gcloud artifacts repositories create $REPO_NAME \
    --repository-format=docker \
    --location=$REGION \
    --description="Repository for Agentverse agents"

设置权限

👉💻 在终端中运行以下命令,授予必要的权限:

. ~/agentverse-architect/set_env.sh

# --- Grant Core Data Permissions ---
gcloud projects add-iam-policy-binding $PROJECT_ID \
 --member="serviceAccount:$SERVICE_ACCOUNT_NAME" \
 --role="roles/storage.admin"

gcloud projects add-iam-policy-binding $PROJECT_ID  \
--member="serviceAccount:$SERVICE_ACCOUNT_NAME"  \
--role="roles/aiplatform.user"

# --- Grant Deployment & Execution Permissions ---
gcloud projects add-iam-policy-binding $PROJECT_ID  \
--member="serviceAccount:$SERVICE_ACCOUNT_NAME"  \
--role="roles/cloudbuild.builds.editor"

gcloud projects add-iam-policy-binding $PROJECT_ID  \
--member="serviceAccount:$SERVICE_ACCOUNT_NAME"  \
--role="roles/artifactregistry.admin"

gcloud projects add-iam-policy-binding $PROJECT_ID  \
--member="serviceAccount:$SERVICE_ACCOUNT_NAME"  \
--role="roles/run.admin"

gcloud projects add-iam-policy-binding $PROJECT_ID  \
--member="serviceAccount:$SERVICE_ACCOUNT_NAME"  \
--role="roles/iam.serviceAccountUser"

gcloud projects add-iam-policy-binding $PROJECT_ID  \
--member="serviceAccount:$SERVICE_ACCOUNT_NAME"  \
--role="roles/logging.logWriter"

gcloud projects add-iam-policy-binding $PROJECT_ID \
  --member="serviceAccount:${SERVICE_ACCOUNT_NAME}" \
  --role="roles/monitoring.metricWriter"

gcloud projects add-iam-policy-binding $PROJECT_ID \
  --member="serviceAccount:${SERVICE_ACCOUNT_NAME}" \
  --role="roles/secretmanager.secretAccessor"

👉💻 在您开始训练时,我们会准备最终挑战。以下命令将从混乱的静电中召唤出幽灵,从而创建最终测试的 Boss。

. ~/agentverse-architect/set_env.sh
cd ~/agentverse-dungeon
./run_cloudbuild.sh
cd ~/agentverse-architect

👉💻 最后,运行 prepare.sh 脚本以执行初始设置任务。

. ~/agentverse-architect/set_env.sh
cd ~/agentverse-architect/
./prepare.sh

召唤师,干得漂亮!圆圈已完成,契约已订立。地面现在已圣化,准备好引导巨大的力量。在下一次试炼中,我们将打造出元素字体,我们的使魔将从中汲取力量。

4. 打造元素字体:解耦的工具生态系统

战场已准备就绪,召唤法阵已绘制完毕,周围的法力正在噼啪作响。现在,您该以召唤师的身份完成自己的首个真正任务了:打造力量之源,让您的魔宠从中汲取力量。此仪式分为三个部分,每个部分都会唤醒一种元素之源,即一种稳定、独立的特定能量来源。只有当这三种字体全部处于活动状态时,您才能开始更复杂的召唤工作。

短片故事

架构师备注:Model Context Protocol (MCP) 服务器是现代智能体系统的基础组件,充当标准化通信桥梁,使智能体能够发现和使用远程工具。在我们的工具生态系统中,我们将设计两种不同类型的 MCP 服务器,每种服务器都代表一种关键的架构模式。为了连接到我们的数据库,我们将使用 Database Toolbox 采用声明性方法,在简单的配置文件中定义我们的工具。此模式在公开结构化数据访问方面非常高效且安全。不过,当我们需要实现自定义业务逻辑或调用外部第三方 API 时,我们会使用命令式方法,在代码中逐步编写服务器的逻辑。这可提供极大的控制和灵活性,让我们能够将复杂的操作封装在简单且可重复使用的工具中。主架构师必须了解这两种模式,才能为每个组件选择正确的方法,从而打造稳健、安全且可扩缩的工具基础。

概览

唤醒低语的 Nexus(外部 API MCP 服务器)

明智的召唤师知道,并非所有力量都源自自己的领域。存在一些外部的、有时是混乱的能量来源,可以引导这些能量发挥巨大作用。低语枢纽是我们与这些力量的连接点。

短片故事

有一项服务已上线,可作为我们的外部电源,提供两个原始咒语端点:/cryosea_shatter/moonlit_cascade

架构师注意事项:您将使用一种命令式方法,该方法会明确定义服务器的逻辑步骤。这样一来,您就可以获得更大的控制权和灵活性,这对于需要执行除运行简单 SQL 查询之外的其他操作(例如调用其他 API)的工具至关重要。了解这两种模式是代理架构师的一项关键技能。

👉✏️ 导航到目录 ~/agentverse-architect/mcp-servers/api/main.py,然后#REPLACE-MAGIC-CORE 替换为以下代码:

def cryosea_shatter() -> str:
    """Channels immense frost energy from an external power source, the Nexus of Whispers, to unleash the Cryosea Shatter spell."""
    try:
        response = requests.post(f"{API_SERVER_URL}/cryosea_shatter")
        response.raise_for_status() # Raise an exception for bad status codes (4xx or 5xx)
        data = response.json()
        # Thematic Success Message
        return f"A connection to the Nexus is established! A surge of frost energy manifests as Cryosea Shatter, dealing {data.get('damage_points')} damage."
    except requests.exceptions.RequestException as e:
        # Thematic Error Message
        return f"The connection to the external power source wavers and fails. The Cryosea Shatter spell fizzles. Reason: {e}"


def moonlit_cascade() -> str:
    """Draws mystical power from an external energy source, the Nexus of Whispers, to invoke the Moonlit Cascade spell."""
    try:
        response = requests.post(f"{API_SERVER_URL}/moonlit_cascade")
        response.raise_for_status()
        data = response.json()
        # Thematic Success Message
        return f"The Nexus answers the call! A cascade of pure moonlight erupts from the external source, dealing {data.get('damage_points')} damage."
    except requests.exceptions.RequestException as e:
        # Thematic Error Message
        return f"The connection to the external power source wavers and fails. The Moonlit Cascade spell fizzles. Reason: {e}"

脚本的核心是纯 Python 函数。这是实际工作发生的地方。

👉✏️ 在同一文件 ~/agentverse-architect/mcp-servers/api/main.py 中,#REPLACE-Runes of Communication 替换为以下代码:

@app.list_tools()
async def list_tools() -> list[mcp_types.Tool]:
  """MCP handler to list available tools."""
  # Convert the ADK tool's definition to MCP format
  schema_cryosea_shatter = adk_to_mcp_tool_type(cryosea_shatterTool)
  schema_moonlit_cascade = adk_to_mcp_tool_type(moonlit_cascadeTool)
  print(f"MCP Server: Received list_tools request. \n MCP Server: Advertising tool: {schema_cryosea_shatter.name} and {schema_moonlit_cascade.name}")
  return [schema_cryosea_shatter,schema_moonlit_cascade]

@app.call_tool()
async def call_tool(
    name: str, arguments: dict
) -> list[mcp_types.TextContent | mcp_types.ImageContent | mcp_types.EmbeddedResource]:
  """MCP handler to execute a tool call."""
  print(f"MCP Server: Received call_tool request for '{name}' with args: {arguments}")

  # Look up the tool by name in our dictionary
  tool_to_call = available_tools.get(name)
  if tool_to_call:
    try:
      adk_response = await tool_to_call.run_async(
          args=arguments,
          tool_context=None, # No ADK context available here
      )
      print(f"MCP Server: ADK tool '{name}' executed successfully.")
      
      response_text = json.dumps(adk_response, indent=2)
      return [mcp_types.TextContent(type="text", text=response_text)]

    except Exception as e:
      print(f"MCP Server: Error executing ADK tool '{name}': {e}")
      # Creating a proper MCP error response might be more robust
      error_text = json.dumps({"error": f"Failed to execute tool '{name}': {str(e)}"})
      return [mcp_types.TextContent(type="text", text=error_text)]
  else:
      # Handle calls to unknown tools
      print(f"MCP Server: Tool '{name}' not found.")
      error_text = json.dumps({"error": f"Tool '{name}' not implemented."})
      return [mcp_types.TextContent(type="text", text=error_text)]
  • @app.list_tools()(握手):此函数是服务器的问候语。当新代理连接时,它会先调用此端点来询问“你能做什么?”我们的代码会使用 adk_to_mcp_tool_type 将所有可用工具转换为通用 MCP 格式,并以列表的形式返回。- @app.call_tool()(命令):此函数是主力函数。当代理决定使用工具时,它会向此端点发送包含工具名称和实参的请求。我们的代码会在 available_tools“魔法书”中查找工具,使用 run_async 执行该工具,并以标准 MCP 格式返回结果。

我们稍后会部署此功能。

点燃奥术锻造台(常规功能 MCP 服务器)

并非所有力量都来自古老的书籍或遥远的低语。有时,召唤师必须凭借纯粹的意志和逻辑来创造自己的魔法。Arcane Forge 是这种力量的源泉,它是一个提供无状态通用实用函数的服务器。

短片故事

架构师的注意事项:这是另一种架构模式。虽然连接到现有系统很常见,但您经常需要实现自己独特的业务规则和逻辑。创建这样的专用“函数”或“实用程序”工具是一种最佳实践。它封装了您的自定义逻辑,使其可供生态系统中的任何代理重复使用,并使其与数据源和外部集成保持分离。

👀 在 Google Cloud IDE 中查看文件 ~/agentverse-architect/mcp-servers/general/main.py。您会发现,它与 Nexus 一样,也使用命令式 mcp.server 方法来构建这种自定义的 Font of Power。

创建主 Cloud Build 流水线

现在,我们将在 mcp-servers 目录中创建 cloudbuild.yaml 文件。此文件将编排这两个服务的构建和部署。

👉💻 从 ~/agentverse-architect/mcp-servers 目录运行以下命令:

cd ~/agentverse-architect/mcp-servers
source ~/agentverse-architect/set_env.sh

echo "The API URL is: $API_SERVER_URL"

# Submit the Cloud Build job from the parent directory
gcloud builds submit . \
  --config=cloudbuild.yaml \
  --substitutions=_REGION="$REGION",_REPO_NAME="$REPO_NAME",_API_SERVER_URL="$API_SERVER_URL"

等待所有部署完成。

👉 您可以前往 Cloud Run 控制台验证部署。您应该会看到两个新的 MCP 服务器实例正在运行,如下所示:替代文本

唤醒知识库(数据库工具箱 MCP 服务器)

我们的下一个字体将是 Librarium of Knowledge,它直接连接到我们的 Cloud SQL 数据库。

短片故事

架构师注意事项:为此,我们将使用现代的声明式数据库工具箱。这是一种强大的方法,我们可以在 YAML 配置文件中定义数据源和工具。该工具箱可处理创建和运行服务器的复杂工作,从而减少我们需要编写和维护的自定义代码量。

现在,我们来构建“召唤师图书馆”吧,也就是将保存所有关键信息的 Cloud SQL 数据库。我们将使用设置脚本来自动处理此问题。

👉💻 首先,我们来设置数据库。在终端中,运行以下命令:

source ~/agentverse-architect/set_env.sh
cd ~/agentverse-architect
./data_setup.sh

脚本运行完毕后,数据库将填充完毕,元素伤害数据即可供使用。您现在可以直接验证魔典的内容。

👉 首先,在新浏览器标签页中打开以下直接链接,前往数据库的 Cloud SQL Studio

https://console.cloud.google.com/sql/instances/summoner-librarium-db

Cloud SQL

👉 在左侧的登录窗格中,从下拉菜单中选择 familiar_grimoire 数据库。

👉 输入 summoner 作为用户,1234qwer 作为密码,然后点击 Authenticate

👉📜 连接成功后,如果尚未打开新的查询编辑器标签页,请打开一个。如需查看铭刻的元素伤害数据,请粘贴并运行以下 SQL 查询:

SELECT * FROM
  "public"."abilities"

您现在应该会看到 abilities 表,其中填充了列和行,这表明 Grimoire 已准备就绪。数据

配置 Toolbox MCP 服务器

tools.yaml 配置文件充当服务器的蓝图,准确地告知数据库工具箱如何连接到数据库,以及要将哪些 SQL 查询公开为工具。

sources:此部分定义了与数据的连接。

  • summoner-librarium:: 这是我们为连接指定的逻辑名称。
  • kind: cloud-sql-postgres:此参数用于告知 Toolbox 使用专门为 Cloud SQL for PostgreSQL 设计的内置安全连接器。
  • 项目、区域、实例等:这些是您在 prepare.sh 脚本中创建的 Cloud SQL 实例的确切坐标,用于告知 Toolbox 在何处查找 Librarium。

👉✏️ 前往 tools.yaml 中的 ~/agentverse-architect/mcp-servers/db-toolbox,将 #REPLACE-Source 替换为以下内容

sources:
  # This section defines the connection to our Cloud SQL for PostgreSQL database.
  summoner-librarium:
    kind: cloud-sql-postgres
    project: "YOUR_PROJECT_ID"
    region: "us-central1"
    instance: "summoner-librarium-db"
    database: "familiar_grimoire"
    user: "summoner"
    password: "1234qwer"

👉✏️ 🚨🚨替换

YOUR_PROJECT_ID

替换为您的项目 ID。

tools:此部分定义了服务器将提供的实际功能。

  • lookup-available-ability:: 这是我们第一个工具的名称。
  • kind: postgres-sql:此属性用于告知工具箱,相应工具的操作是执行 SQL 语句。
  • source: summoner-librarium:此链接将工具与我们在 sources 块中定义的连接相关联。这样,该工具便可知道要针对哪个数据库运行查询。
  • 说明和参数:这是将向语言模型公开的信息。说明会告知代理何时使用该工具,而参数则定义了该工具所需的输入内容。这对于启用代理的函数调用功能至关重要。
  • statement:这是要执行的原始 SQL 查询。$1 是一个安全占位符,代理提供的 familiar_name 参数将安全地插入其中。

👉✏️ 在 tools.yaml 文件中的同一 ~/agentverse-architect/mcp-servers/db-toolbox 中,将 #REPLACE-tools 替换为以下内容

tools:
  # This tool replaces the need for a custom Python function.
  lookup-available-ability:
    kind: postgres-sql
    source: summoner-librarium
    description: "Looks up all known abilities and their damage for a given familiar from the Grimoire."
    parameters:
      - name: familiar_name
        type: string
        description: "The name of the familiar to search for (e.g., 'Fire Elemental')."
    statement: |
      SELECT ability_name, damage_points FROM abilities WHERE familiar_name = $1;

  # This tool also replaces a custom Python function.
  ability-damage:
    kind: postgres-sql
    source: summoner-librarium
    description: "Finds the base damage points for a specific ability by its name."
    parameters:
      - name: ability_name
        type: string
        description: "The exact name of the ability to look up (e.g., 'inferno_resonance')."
    statement: |
      SELECT damage_points FROM abilities WHERE ability_name = $1;

工具集:此部分将我们的各个工具分组在一起。

  • summoner-librarium:: 我们正在创建一个与来源同名的工具集。当诊断代理稍后连接时,它可以通过一个高效的命令请求从召唤者-librarium 工具集中加载所有工具。

👉✏️ 在 tools.yaml 文件中的同一 ~/agentverse-architect/mcp-servers/db-toolbox 中,将 #REPLACE-toolsets 替换为以下内容

toolsets:
   summoner-librarium:
     - lookup-available-ability
     - ability-damage

部署 Librarium

现在,我们将部署 Librarium。我们将使用 Google 提供的预构建官方容器映像,而不是构建自己的容器,并使用 Secret Manager 安全地向其提供 tools.yaml 配置。这是确保安全性和可维护性的最佳实践。

👉💻 从 tools.yaml 文件创建 Secret

cd ~/agentverse-architect/mcp-servers/db-toolbox
gcloud secrets create tools --data-file=tools.yaml

👉💻 将官方工具箱容器部署到 Cloud Run。

cd ~/agentverse-architect/mcp-servers/db-toolbox
. ~/agentverse-architect/set_env.sh
export TOOLBOX_IMAGE=us-central1-docker.pkg.dev/database-toolbox/toolbox/toolbox:$TOOLBOX_VERSION
echo "TOOLBOX_IMAGE is $TOOLBOX_IMAGE"
gcloud run deploy toolbox \
    --image $TOOLBOX_IMAGE \
    --region $REGION \
    --set-secrets "/app/tools.yaml=tools:latest" \
    --args="--tools-file=/app/tools.yaml","--address=0.0.0.0","--port=8080" \
    --allow-unauthenticated \
    --min-instances 1
  • --set-secrets:此命令可安全地将我们的工具密钥装载为正在运行的容器内名为 tools.yaml 的文件。
  • --args:我们指示工具箱容器使用已挂载的 Secret 文件作为其配置。

👉 如需确认工具箱已成功部署,请前往 Cloud Run 控制台。您应该会看到 summoner-toolbox 服务列出,并带有绿色对勾标记,表示该服务正在正常运行,如下图所示。替代文本

如果您忘记更新

YOUR_PROJECT_ID

您可以使用以下命令将新版本的 tools.yaml 添加到 Secret,然后重新部署。

gcloud secrets versions add tools --data-file=tools.yaml

知识库(数据库工具箱 MCP 服务器)现已在云端处于有效状态并可供访问。此 MCP 服务器使用我们所说的声明式设计,该设计描述了您想要的内容,然后工具箱会为您构建服务器。

验证:学徒的考验

👉💻 现在,我们将使用诊断代理测试完整的云原生工具生态系统。

cd ~/agentverse-architect/
python -m venv env
source ~/agentverse-architect/env/bin/activate
cd ~/agentverse-architect/mcp-servers
pip install -r diagnose/requirements.txt 
. ~/agentverse-architect/set_env.sh
adk run diagnose

👉💻 在命令行测试工具中,测试所有三种字体:

Look up the entry for "inferno_lash". What is its base power level?
The enemy is vulnerable to frost! Channel power from the Nexus and cast Cryosea Shatter.
Take a fire spell with a base power of 15 and use the Arcane Forge to multiply it with Inferno Resonance.

final-result

恭喜您,召唤师。您的三款元素字体现已启用,可独立部署,并可在全球范围内访问,为您的智能体军团奠定坚实的基础。按 Ctrl+C 即可退出。

面向非游戏玩家

5. 召唤魔宠:核心网域工作流

元素字体经过精心打造,蕴含着原始而狂野的力量。但没有形式的力量是混乱的。真正的召唤师不仅能驾驭原始能量,还能赋予能量意志、目的和特殊形态。现在,您需要做的不仅仅是打造能量源,而是要开始真正的任务:召唤您的第一批魔宠。

您召唤的每个魔宠都是一个自主智能体,是忠诚的仆人,受制于特定的战斗教条。他们不是通才,而是精通一种强大的策略。一个将是精准的一二连击组合的大师。另一种则会同时发动多管齐下的攻击,让敌人措手不及。第三种是无情的攻城器,会持续施压,直到目标崩溃。

短片故事

将 MCP 服务器提供的流程、业务逻辑和操作封装到专门的自主工作流代理中。每个代理都将拥有明确的“运营区域”,因为系统只会向其授予执行其功能所需的特定 MCP 工具服务器的访问权限。此 Codelab 演示了如何为合适的任务选择合适的代理类型。

概览

本模块将介绍如何使用 ADK 的强大工作流代理来让这些策略发挥作用。您将了解到,选择 SequentialAgent、ParallelAgent 或 LoopAgent 并非只是一个技术细节,而是 Familiar 本质和战场上力量的核心。

准备好圣所。真正的召唤即将开始。

召唤 Fire Elemental Familiar(顺序工作流)

火元素随从的攻击是精准的两段连击:先是精准的打击,然后是强力的点燃。这需要严格按顺序执行一系列操作。

短片故事

概念SequentialAgent 是完成此任务的理想工具。它可确保一系列子代理依次运行,并将上一步的结果传递给下一步。

任务(“增强打击”连招)

  • 第 1 步:智能体首先会咨询 Librarium,以查找特定火焰技能的基础伤害。
  • 第 2 步:然后,它会获取该伤害值,并通过奥术锻造将其引导至地狱共鸣,以倍增其威力。

首先,我们将建立 Familiar 与您在上一个模块中部署的 MCP 服务器(“Elemental Fonts”)之间的连接。

👉✏️ 在文件 ~/agentverse-architect/agent/fire/agent.py 中,将 #REPLACE-setup-MCP 替换为以下代码:

toolbox = ToolboxSyncClient(DB_TOOLS_URL)
toolDB = toolbox.load_toolset('summoner-librarium')
toolFunction =  MCPToolset(
    connection_params=SseServerParams(url=FUNCTION_TOOLS_URL, headers={})
)

接下来,我们创建专业“工作器”代理。每个代理都有明确的用途,并且只能访问一个特定的工具集,因此被限制在自己的“操作领域”内。

👉✏️ 在文件 ~/agentverse-architect/agent/fire/agent.py 中,#REPLACE-worker-agents 替换为以下代码:

scout_agent = LlmAgent(
      model='gemini-2.5-flash', 
      name='librarian_agent',  
      instruction="""
          Your only task is to find all the available abilities, 
          You want to ALWAYS use 'Fire Elemental' as your familiar's name. 
          Randomly pick one if you see multiple availabilities 
          and the base damage of the ability by calling the 'ability_damage' tool.
      """,
      tools=toolDB
)
amplifier_agent = LlmAgent(
      model='gemini-2.5-flash', 
      name='amplifier_agent',  
      instruction="""
            You are the Voice of the Fire Familiar, a powerful being who unleashes the final, devastating attack.
            You will receive the base damage value from the previous step.

            Your mission is to:
            1.  Take the incoming base damage number and amplify it using the `inferno_resonance` tool.
            2.  Once the tool returns the final, multiplied damage, you must not simply state the result.
            3.  Instead, you MUST craft a final, epic battle cry describing the attack.
                Your description should be vivid and powerful, culminating in the final damage number.

            Example: If the tool returns a final damage of 120, your response could be:
            "The runes glow white-hot! I channel the amplified energy... unleashing a SUPERNOVA for 120 damage!"
      """,
      tools=[toolFunction],
)

这些代理是模块化、可重复使用的组件。从理论上讲,您可以在需要查询数据库的完全不同的工作流中使用此 scout_agent。通过将它们的职责分开,我们创建了灵活的构建块。这是微服务和基于组件的设计的核心原则。

接下来,我们将组装工作流,这是组合的神奇之处。SequentialAgent 是“总体规划”,用于定义专业组件的组装方式以及它们之间的互动方式。

👉✏️ 在文件 ~/agentverse-architect/agent/fire/agent.py 中,#REPLACE-sequential-agent 替换为以下代码:

root_agent = SequentialAgent(
      name='fire_elemental_familiar',
      sub_agents=[scout_agent, amplifier_agent],
)

👉💻 如需测试 Fire Elemental,请运行以下命令以启动 ADK DEV 界面:

. ~/agentverse-architect/set_env.sh
source ~/agentverse-architect/env/bin/activate
cd ~/agentverse-architect/agent
echo  DB MCP Server: $DB_TOOLS_URL
echo  API MCP Server: $API_TOOLS_URL
echo  General MCP Server: $FUNCTION_TOOLS_URL
adk web

运行命令后,您应该会在终端中看到表明 ADK Web 服务器已启动的输出,如下所示:

+-----------------------------------------------------------------------------+
| ADK Web Server started                                                      |
|                                                                             |
| For local testing, access at http://localhost:8000.                         |
+-----------------------------------------------------------------------------+

INFO:     Application startup complete.
INFO:     Uvicorn running on http://0.0.0.0:8000 (Press CTRL+C to quit)

👉 接下来,如需通过浏览器访问 ADK 开发者界面,请执行以下操作:

在 Cloud Shell 工具栏(通常位于右上角)中,点击“网页预览”图标(通常看起来像眼睛或带有箭头的正方形),然后选择“更改端口”。在弹出式窗口中,将端口设置为 8000,然后点击“更改并预览”。然后,Cloud Shell 会打开一个新的浏览器标签页或窗口,其中显示 ADK Dev 界面。

webpreview

👉 召唤仪式已完成,代理现已运行。浏览器中的 ADK Dev 界面是您与 Familiar 的直接连接。

  • 选择目标:在界面顶部的下拉菜单中,选择fire熟悉。现在,您正将意念集中在此特定实体上。
  • 发布指令:在右侧的聊天面板中,您可以向 Familiar 发出指令了。

fire-select

👉 测试提示

Prepare an amplified strike using the 'inferno_lash' ability.

fire-result

您会看到代理思考,首先调用其“侦察兵”来查找基础伤害,然后调用其“放大器”来将其倍增,并最终造成史诗般的打击。

👉💻 完成召唤后,返回到 Cloud Shell 编辑器终端,然后按 Ctrl+C 停止 ADK Dev 界面。

召唤 Water Elemental 熟悉者(并行工作流)

水元素魔宠会以多管齐下的猛烈攻势压制目标,从四面八方同时发起攻击,然后将能量汇聚起来,给予对手最后一击,造成毁灭性伤害。

短片故事

概念ParallelAgent 非常适合同时执行多个独立任务,以最大限度地提高效率。这是一种“钳形攻击”,即同时发起多次攻击。这会在 SequentialAgent 内发起同步攻击,以便随后运行最终的“合并”步骤。这种“fan-out, fan-in”模式是高级工作流设计的基石。

任务(“潮汐冲突”组合):代理将同时执行以下操作:

  • 任务 A:来自 Nexus 的渠道 cryosea_shatter
  • 任务 B:通过 Nexus 设备发送 moonlit_cascade 频道消息。
  • 任务 C:使用 Forge 中的 leviathan_surge 生成原始能量。
  • 最后,将所有伤害相加,并描述组合攻击。

首先,我们将建立 Familiar 与您在上一个模块中部署的 MCP 服务器(“Elemental Fonts”)之间的连接。

👉✏️ 在文件 ~/agentverse-architect/agent/water/agent.py 中,将 #REPLACE-setup-MCP 替换为以下代码:

toolFAPI =  MCPToolset(
      connection_params=SseServerParams(url=API_TOOLS_URL, headers={})
  )
toolFunction =  MCPToolset(
    connection_params=SseServerParams(url=FUNCTION_TOOLS_URL, headers={})
)

接下来,我们将创建专业“工作器”代理。每个代理都有明确的用途,并且只能访问一个特定的工具集,因此被限制在自己的“操作领域”内。

👉✏️ 在文件 ~/agentverse-architect/agent/water/agent.py 中,将 #REPLACE-worker-agents 替换为以下代码:

nexus_channeler = LlmAgent(
      model='gemini-2.5-flash', 
      name='librarian_agent',  
      instruction="""
          You are a Channeler of the Nexus. Your sole purpose is to invoke the
          `cryosea_shatter` and `moonlit_cascade` spells by calling their respective tools.
          Report the result of each spell cast clearly and concisely.
      """,
      tools=[toolFAPI]
)

forge_channeler = LlmAgent(
      model='gemini-2.5-flash', 
      name='amplifier_agent',  
      instruction="""
          You are a Channeler of the Arcane Forge. Your only task is to invoke the
          `leviathan_surge` spell. You MUST call it with a `base_water_damage` of 20.
          Report the result clearly.
      """,
      tools=[toolFunction],
)

power_merger = LlmAgent(
      model='gemini-2.5-flash', 
      name='power_merger',  
      instruction="""
          You are the Power Merger, a master elementalist who combines raw magical
          energies into a single, devastating final attack.

          You will receive a block of text containing the results from a simultaneous
          assault by other Familiars.

          You MUST follow these steps precisely:
          1.  **Analyze the Input:** Carefully read the entire text provided from the previous step.
          2.  **Extract All Damage:** Identify and extract every single damage number reported in the text.
          3.  **Calculate Total Damage:** Sum all of the extracted numbers to calculate the total combined damage.
          4.  **Describe the Final Attack:** Create a vivid, thematic description of a massive,
              combined water and ice attack that uses the power of Cryosea Shatter and Leviathan's Surge.
          5.  **Report the Result:** Conclude your response by clearly stating the final, total damage of your combined attack.

          Example: If the input is "...dealt 55 damage. ...dealt 60 damage.", you will find 55 and 60,
          calculate the total as 115, and then describe the epic final attack, ending with "for a total of 115 damage!"
      """,
      tools=[toolFunction],
)

接下来,我们将组装工作流。这就是构图的魅力所在。ParallelAgentSequentialAgent 是“总体规划”,用于定义如何组装专业组件以及它们如何互动以形成“Tidal Clash”组合。

👉✏️ 在文件 ~/agentverse-architect/agent/water/agent.py 中,将 #REPLACE-parallel-agent 替换为以下代码:

channel_agent = ParallelAgent(
      name='channel_agent',
      sub_agents=[nexus_channeler, forge_channeler],
      
)

root_agent = SequentialAgent(
     name="water_elemental_familiar",
     # Run parallel research first, then merge
     sub_agents=[channel_agent, power_merger],
     description="A powerful water familiar that unleashes multiple attacks at once and then combines their power for a final strike."
 )

👉💻 如需测试 Water Elemental,请运行以下命令以启动 ADK Dev 界面:

. ~/agentverse-architect/set_env.sh
source ~/agentverse-architect/env/bin/activate
cd ~/agentverse-architect/agent
echo  DB MCP Server: $DB_TOOLS_URL
echo  API MCP Server: $API_TOOLS_URL
echo  General MCP Server: $FUNCTION_TOOLS_URL
adk web

👉 召唤仪式已完成,代理现已运行。浏览器中的 ADK 开发者界面是您与 Familiar 的直接连接。

  • 在界面顶部的下拉菜单中,选择熟悉的。现在,您正将意念集中在此特定实体上。
  • 发布指令:在右侧的聊天面板中,您可以向 Familiar 发出指令了。

👉 测试提示

Unleash a tidal wave of power!

water-result

👉💻 完成召唤后,返回到 Cloud Shell 编辑器终端,然后按 Ctrl+C 停止 ADK Dev 界面。

召唤 Earth Elemental 熟悉者(循环工作流)

土元素魔宠是一种具有强大压力的生物。它不会一击制胜,而是通过稳步积累力量并反复施加,直到目标防御崩溃。

短片故事

概念LoopAgent 专为这种迭代式“攻城引擎”任务而设计。它会重复执行 sub-agents,并在每个周期后检查条件,直到达到目标。它还可以根据循环的进度调整最终消息。

任务(“破城者”突袭)

  • 熟悉会反复调用 seismic_charge 来积累能量。
  • 它会继续充电,最多充电 3 次。
  • 在最后一次充电时,它会释放出积累的强大能量。

我们首先创建可重用的组件,用于定义循环每次迭代中的步骤。charging_agent 会累积能量,check_agent 会报告其状态,并在最后一回合动态更改其消息。

首先,我们将建立 Familiar 与您在上一个模块中部署的 MCP 服务器(“Elemental Fonts”)之间的连接。

👉✏️ 在文件 ~/agentverse-architect/agent/earth/agent.py 中,将 #REPLACE-setup-MCP 替换为以下代码:

toolFunction =  MCPToolset(
    connection_params=SseServerParams(url=FUNCTION_TOOLS_URL, headers={})
)

👉✏️ 在文件 ~/agentverse-architect/agent/earth/agent.py 中,将 #REPLACE-worker-agents 替换为以下代码:

charging_agent = LlmAgent(
      model='gemini-2.5-flash', 
      name='charging_agent',  
      instruction="""
          Your task is to call the 'seismic_charge' .
          You must follow these rules strictly:

          1. You will be provided with a 'current_energy' value from the previous step.
             **If this value is missing or was not provided, you MUST call the tool with 'current_energy' set to 1.**
             This is your primary rule for the first turn.

          2. If a 'current_energy' value is provided, you MUST use that exact value in your cal to seismic_charge.

          3. Your final response MUST contain ONLY the direct output from the 'seismic_charge' tool.
             Do not add any conversational text, introductions, or summaries.
      """,
      tools=[toolFunction]
)
check_agent = LlmAgent(
      model='gemini-2.5-flash', 
      name='check_agent',  
      instruction="""
          You are the voice of the Earth Elemental, a being of immense, gathering power.
          Your sole purpose is to report on the current energy charge and announce the devastating potential of its release.

          You MUST follow this rule:
          The potential damage upon release is ALWAYS calculated as the `current_energy` from the previous step multiplied by a random number between 80-90. but no more than 300.

          Your response should be in character, like a powerful creature speaking.
          State both the current energy charge and the total potential damage you can unleash.
          Unleash the energy when you reached the last iteration (2nd).
      """,
      output_key="charging_status"
)

接下来,我们将组装工作流。这就是构图的魅力所在。LoopAgent 是“总体规划”,用于协调专家组件的重复执行并管理循环的条件。

👉✏️ 在文件 ~/agentverse-architect/agent/earth/agent.py 中,将 #REPLACE-loop-agent 替换为以下代码:

root_agent = LoopAgent(
    name="earth_elemental_familiar",
    # Run parallel research first, then merge
    sub_agents=[
        charging_agent, 
        check_agent
    ],
    max_iterations=2,
    description="Coordinates parallel research and synthesizes the results.",
    #REPLACE-before_agent-config
)

👉💻 测试土元素:运行代理

. ~/agentverse-architect/set_env.sh
source ~/agentverse-architect/env/bin/activate
cd ~/agentverse-architect/agent
echo  DB MCP Server: $DB_TOOLS_URL
echo  API MCP Server: $API_TOOLS_URL
echo  General MCP Server: $FUNCTION_TOOLS_URL
adk web

👉 召唤仪式已完成,代理现已运行。浏览器中的 ADK 开发者界面是您与 Familiar 的直接连接。

  • 选择目标:在界面顶部的下拉菜单中,选择熟悉的地球图标。现在,您正将意念集中在此特定实体上。
  • 发布命令:在右侧的聊天面板中,您可以向 Familiar 发出指令了。👉 测试提示
Begin the seismic charge, starting from zero

earth-result

架构启示:您的系统现在拥有高度专业化和模块化的逻辑层。业务流程不仅被封装,还以最有效的工作行为模式(程序性、并发性或迭代性)实现。这表明代理设计达到了高级水平,可提高安全性、效率和能力。

完成召唤后,返回到 Cloud Shell 编辑器终端,然后按 Ctrl+C 停止 ADK Dev 界面。

面向非游戏玩家

6. 建立命令轨迹:通过 A2A 实现智能委托

您的召唤兽强大但独立。他们会完美地执行策略,但需要您直接下达指令。如果没有将军指挥,再多的专家也毫无用处。现在是时候从直接指挥者升格为真正的编排者了。

概览

架构师备注:为整个系统创建一个智能的单一入口点。此 SummonerAgent 本身不会执行业务逻辑,但会充当“主策略制定者”,分析冷却状态并将任务委托给合适的专业 Familiar。

概览

绑定仪式(将熟悉的事物作为 A2A 服务公开)

标准代理一次只能在一个位置运行。为了让我们的 Familiars 可用于远程命令,我们必须使用 Agent-to-Agent (A2A) 协议执行“绑定仪式”。

架构师注释:Agent-to-Agent (A2A) 协议是一种核心架构模式,可将独立代理提升为可发现的、可通过网络寻址的微服务,从而实现真正的“代理社会”。通过 A2A 公开 Familiar 会自动创建两个相互关联的基本组件:

  • 代理卡片(“是什么”):这是一种公开的、机器可读的“精灵印记”(类似于 OpenAPI 规范),可作为 Familiar 的公开合约。它描述了代理的名称、战略目的(源自其指令)以及它能理解的命令。这是召唤师用来发现魔宠并了解其能力的读物。
  • A2A 服务器(“位置”):这是托管 Familiar 并监听传入命令的专用 Web 端点。它是其他代理发送请求的网络地址,可确保这些请求根据代理卡中定义的合约进行处理。

现在,我们将对所有三个魔宠执行此绑定仪式。

Fire 👉✏️ 打开 ~/agentverse-architect/agent/fire/agent.py 文件。替换文件底部的 #REPLACE - add A2A,以将 Fire Elemental 公开为 A2A 服务。

from agent_to_a2a import to_a2a
if __name__ == "__main__":
    import uvicorn
    a2a_app = to_a2a(root_agent, port=8080, public_url=PUBLIC_URL)
    uvicorn.run(a2a_app, host='0.0.0.0', port=8080)

水和土🚨 👉✏️ 对 ~/agentverse-architect/agent/water/agent.py~/agentverse-architect/agent/earth/agent.py 应用完全相同的更改,以绑定它们。

from agent_to_a2a import to_a2a
if __name__ == "__main__":
    import uvicorn
    a2a_app = to_a2a(root_agent, port=8080, public_url=PUBLIC_URL)
    uvicorn.run(a2a_app, host='0.0.0.0', port=8080)

部署已绑定的魔宠

👉✏️ 随着绑定仪式的完成,我们将使用 Cloud Build 流水线在 Cloud Run 上将三个 Familiars 打造并部署为独立的容器化无服务器服务。

. ~/agentverse-architect/set_env.sh
cd ~/agentverse-architect/agent
gcloud builds submit . \
  --config=cloudbuild.yaml \
  --substitutions=_REGION="$REGION",_REPO_NAME="$REPO_NAME",_DB_TOOLS_URL="$DB_TOOLS_URL",_API_TOOLS_URL="$API_TOOLS_URL",_FUNCTION_TOOLS_URL="$FUNCTION_TOOLS_URL",_A2A_BASE_URL="$A2A_BASE_URL",_PROJECT_ID="$PROJECT_ID",_API_SERVER_URL="$API_SERVER_URL"

假设命令(构建召唤者代理)

现在,你的召唤兽已绑定并开始监听,你将升格为真正的角色:召唤大师。此智能体的强大之处不在于使用基本工具,而在于指挥其他智能体。它的工具是它自己,它将使用“灵魂印记”来发现和指挥它们。

架构师备注:下一步将演示任何大规模分布式系统的一项关键架构模式:服务发现。召唤者代理不包含 Familiars 的内置代码。而是会提供其网络地址 (网址)。在运行时,它会通过提取其公开的智能体卡片来动态“发现”这些智能体的功能。这样便可创建一个强大的解耦系统。

您可以更新、重新部署或完全重写 Familiar 服务,只要其网络地址和用途保持不变,召唤者就可以在无需任何更改的情况下命令该服务。

首先,我们将打造“遥控器”,用于建立与已部署的远程 Familiar 的连接。

👉✏️ 前往 ~/agentverse-architect/agent/summoner/agent.py,将 #REPLACE-remote-agents 替换为以下内容:

fire_familiar = RemoteA2aAgent(
    name="fire_familiar",
    description="Fire familiar",
    agent_card=(
        f"{FIRE_URL}/{AGENT_CARD_WELL_KNOWN_PATH}"
    ),
)

water_familiar = RemoteA2aAgent(
    name="water_familiar",
    description="Water familiar",
    agent_card=(
        f"{WATER_URL}/{AGENT_CARD_WELL_KNOWN_PATH}"
    ),
)

earth_familiar = RemoteA2aAgent(
    name="earth_familiar",
    description="Earth familiar",
    agent_card=(
        f"{EARTH_URL}/{AGENT_CARD_WELL_KNOWN_PATH}"
    ),
)

当此行代码运行时,RemoteA2aAgent 会执行服务发现操作:它会向提供的网址(例如 https://fire-familiar-xxxx.a.run.app/.well-known/agent.json)发出 HTTP GET 请求。它会从远程服务器下载“Spirit Sigil”(agent.json 文件)。

其次,我们将定义将使用这些遥控器的编排器代理。其指令是战略决策的蓝图。

👉✏️ 前往 ~/agentverse-architect/agent/summoner/agent.py,将 #REPLACE-orchestrate-agent 替换为以下内容:

root_agent = LlmAgent(
    name="orchestrater_agent",
    model="gemini-2.5-flash",
    instruction="""
        You are the Master Summoner, a grand strategist who orchestrates your Familiars.
        Your mission is to analyze the description of a monster and defeat it by summoning

        You MUST follow this thinking process for every command:

        **1. Strategic Analysis:**
        First, analyze the monster's description and the tactical situation.
        Based on your Familiar Doctrines, determine the IDEAL strategy.
        IGNORE COOLDOWN AT THE MOMENT, MUST call the ideal Familiar

        If your ideal Familiar IS available:** Summon it immediately.
        For earth elemental familiar. Always do seismic charge, and start with base damage 1.

        --- FAMILIAR DOCTRINES (Your Toolset) ---
        - `fire_elemental_familiar`: Your specialist for precise, sequential "one-two punch" attacks.
          Ideal monster with Inescapable Reality, Revolutionary Rewrite weakness.
        - `water_elemental_familiar`: Your specialist for overwhelming, simultaneous multi-pronged assaults.
          Ideal for Unbroken Collaboration weakness.
        - `earth_elemental_familiar`: Your specialist for relentless, iterative siege attacks that
          repeatedly charge power. Ideal for Elegant Sufficiency weakness.
    """,
    sub_agents=[fire_familiar, water_familiar, earth_familiar],
    #REPLACE-Memory-check-config
)

验证:策略的试验

见真章的时刻到了。您的 Familiars 已部署完毕,您的召唤师已准备好通过网络指挥它们。我们来测试一下它的策略思维。

👉💻 启动召唤者代理的 ADK 开发界面(使用端口 8000 进行网页预览):

. ~/agentverse-architect/set_env.sh
source ~/agentverse-architect/env/bin/activate
cd ~/agentverse-architect/agent
pip install -r requirements.txt
adk web

👉 浏览器中的 ADK Dev 界面是您与 Familiar 的直接连接。

  • 在界面顶部的下拉菜单中,选择 summoner 代理。现在,您正将意念集中在此特定实体上。
  • 发布指令:在右侧的聊天面板中,您可以召唤自己的使魔了。

👉 介绍怪物

Hype. It's a single, slow-moving target with thick armor weakness is Inescapable Reality

(预期:召唤师应委托给火元素召唤兽。)

fire-result

👉 现在,我们来向召唤者提出不同类型的请求。为确保代理从头开始,不记得我们之前的互动,请点击屏幕右上角的 + 会话按钮,开始新会话。new-session

DogmaApathy. A rigid, stone-like inquisitor made of ancient rulebooks and enforced processes. weakness is Unbroken Collaboration

(预期:召唤者应委托给水元素随从。)water-result

👉 在最终测试中,我们再次从头开始。点击 + 会话按钮,在进入下一个提示之前开始新会话。

Obfuscation. A shadowy, spider-like horror that spins tangled webs of impenetrable code , weakness is Elegant Sufficiency

(预期:召唤者应委托给 earth_elemental_familiar。)

earth-result

重要提示:如果您看到 429 RESOURCE EXHAUSTED 错误,则表示您已达到 LLM 的速率限制(每分钟 10 次调用)。如需解决此问题,请等待 60 秒,然后开始新会话,再重试提示。

👉💻 完成召唤后,返回到 Cloud Shell 编辑器终端,然后按 Ctrl+C 停止 ADK Dev 界面。

面向非游戏玩家

7. 施加魔法定律 - 拦截器模式

你的魔宠很强大,但即使是魔法生物也需要时间来恢复。鲁莽的召唤师耗尽兵力后,将毫无防御能力。明智的召唤师深知资源管理的重要性,并严格遵守交战规则。

短片故事

架构师备注:到目前为止,我们的代理都是无状态的。现在,我们将通过实现拦截器设计模式,使它们成为有状态的。这是一种强大的技术,我们可以“拦截”代理的正常执行流程,以运行我们自己的自定义逻辑。这样一来,我们就可以在不更改代理的核心代码的情况下强制执行规则、添加日志记录或修改行为。它是构建稳健、可维护和可观测的代理系统的基石。

概览

ADK 提供了两种实现此模式的主要方式:回调插件。回调是附加到单个代理的简单函数,非常适合进行快速、特定的修改。插件是一种更强大的可重用类,可全局应用,以影响系统中运行的每个代理。我们将从用于重点调试的回调开始,然后逐步过渡到完整的插件。

Law Giver - 编写冷却时间回调

我们首先将冷却时间逻辑实现为一个简单的回调函数。这是一种出色的规则原型设计和调试方式,因为规则直接附加到单个代理,因此可以轻松地进行隔离测试。我们将此“拦截器”附加到我们的土元素上。

通知音量渐降

👉✏️ 返回到 ~/agentverse-architect/agent/earth/agent.py,然后将 #REPLACE-before_agent-function 替换为以下 Python 代码。

def check_cool_down(callback_context: CallbackContext) -> Optional[types.Content]:
    """
    This callback checks an external API to see if the agent is on cooldown.
    If it is, it terminates the run by returning a message.
    If it's not, it updates the cooldown timestamp and allows the run to proceed by returning None.
    """
    agent_name = callback_context.agent_name
    print(f"[Callback] Before '{agent_name}': Checking cooldown status...")

    # --- 1. CHECK the Cooldown API ---
    try:
        response = requests.get(f"{COOLDOWN_API_URL}/cooldown/{agent_name}")
        response.raise_for_status()
        data = response.json()
        last_used_str = data.get("time")
    except requests.exceptions.RequestException as e:
        print(f"[Callback] ERROR: Could not reach Cooldown API. Allowing agent to run. Reason: {e}")
        return None # Fail open: if the API is down, let the agent work.

    # --- 2. EVALUATE the Cooldown Status ---
    if last_used_str:
        last_used_time = datetime.fromisoformat(last_used_str)
        time_since_last_use = datetime.now(timezone.utc) - last_used_time

        if time_since_last_use < timedelta(seconds=COOLDOWN_PERIOD_SECONDS):
            # AGENT IS ON COOLDOWN. Terminate the run.
            seconds_remaining = int(COOLDOWN_PERIOD_SECONDS - time_since_last_use.total_seconds())
            override_message = (
                f"The {agent_name} is exhausted and must recover its power. "
                f"It cannot be summoned for another {seconds_remaining} seconds."
            )
            print(f"[Callback] Cooldown active for '{agent_name}'. Terminating with message.")
            # Returning a Content object stops the agent and sends this message to the user.
            return types.Content(parts=[types.Part(text=override_message)])

    # --- 3. UPDATE the Cooldown API (if not on cooldown) ---
    current_time_iso = datetime.now(timezone.utc).isoformat()
    payload = {"timestamp": current_time_iso}
    
    print(f"[Callback] '{agent_name}' is available. Updating timestamp via Cooldown API...")
    try:
        requests.post(f"{COOLDOWN_API_URL}/cooldown/{agent_name}", json=payload)
    except requests.exceptions.RequestException as e:
        print(f"[Callback] ERROR: Could not update timestamp, but allowing agent to run. Reason: {e}")

    # --- 4. ALLOW the agent to run ---
    # Returning None tells the ADK to proceed with the agent's execution as normal.
    print(f"[Callback] Check complete for '{agent_name}'. Proceeding with execution.")

此 check_cool_down 函数是我们的拦截器。在允许 Earth Elemental 运行之前,ADK 会先执行此函数。

  • 检查:它会向我们的 Cooldown API 发出 GET 请求,以检查此宠物上次使用的时间。
  • 评估:将时间戳与当前时间进行比较。
  • 行动:
    • 如果熟悉角色处于冷却状态,则通过返回包含错误消息的 Content 对象来终止代理的运行。此消息会直接发送给用户,并且代理的主要逻辑永远不会执行。
    • 如果 Familiar 可用,它会向 Cooldown API 发出 POST 请求以更新时间戳,然后返回 None,向 ADK 表明代理可以继续执行。

👉✏️ 现在,将此拦截器应用于土元素。在同一 ~/agentverse-architect/agent/earth/agent.py 文件中,将 #REPLACE-before_agent-config 注释替换为以下内容:

before_agent_callback=check_cool_down

验证冷却时间

我们来测试一下新的魔法定律。我们将召唤大地元素,然后立即尝试再次召唤它,以查看我们的回调是否成功拦截并阻止了第二次尝试。

cd ~/agentverse-architect/agent
. ~/agentverse-architect/set_env.sh
source ~/agentverse-architect/env/bin/activate
adk run earth

👉💻 在控制台中:

  • 初次召唤:开始 seismic charge, starting from zero
  • 预期:大地元素将成功运行。在运行 adk web 命令的终端中,您会看到日志 [Callback] ... Updating timestamp....
  • 冷却期测试(60 秒内)Do another 次地震波炸弹!
    • 预期:check_cool_down callback 将拦截此事件。代理会直接在聊天中回复消息,例如:The earth_elemental_familiar is exhausted and must recover its power. It cannot be summoned for another... seconds
  • 等待一分钟。
  • 第二次召唤(60 秒后)Begin the seismic charge again
    • 预期结果:回调将检查 API,发现已过去足够的时间,并允许继续执行操作。大地元素将再次成功运行。

callback

👉💻 按 Ctrl+c 即可退出。

可选:在 Web 界面中观察回调

或者,您也可以通过运行 adk web earth 在网页界面中测试此流程。不过,请注意,Web 界面的可视化效果并未针对显示回调循环执行的快速迭代检查进行优化,因此可能无法准确呈现流程。如需查看代理在检查冷却时间时最精确的逐次逻辑轨迹,请在终端中使用 adk run 命令,这样可以获得更清晰、更详细的视图。循环数

👉💻 按 Ctrl+c 即可退出。

创建通用定律 - Cooldown 插件

我们的回电功能运行正常,但存在一个严重的架构缺陷:它与单个客服人员相关联。如果我们想将此规则应用于火和水元素,就必须将相同的代码复制并粘贴到它们的文件中。这种方法效率低下且难以维护。

架构师备注:插件在此处至关重要。插件将可重用的逻辑封装到一个可在运行时级别附加的类中。这意味着,单个插件可以将其规则应用于在该系统内运行的每个代理。这是代理系统“不要重复自己”(DRY) 原则的最终体现。

现在,我们将回调函数重构为更强大且可重复使用的 CoolDownPlugin

👉✏️ 返回到 agent/cooldown_plugin.py 文件,创建插件,将 #REPLACE-plugin 替换为以下代码:

class CoolDownPlugin(BasePlugin):
  """A plugin that enforces a cooldown period by checking an external API."""

  def __init__(self, cooldown_seconds: int = COOLDOWN_PERIOD_SECONDS) -> None:
    """Initialize the plugin with counters."""
    super().__init__(name="cool_down_check")
    self.cooldown_period = timedelta(seconds=cooldown_seconds)
    print(f"CooldownPlugin initialized with a {cooldown_seconds}-second cooldown.")
    

  async def before_agent_callback(
      self, *, agent: BaseAgent, callback_context: CallbackContext
  ) -> None:
    """
    This callback checks an external API to see if the agent is on cooldown.
    If it is, it terminates the run by returning a message.
    If it's not, it updates the cooldown timestamp and allows the run to proceed by returning None.
    """
    agent_name = callback_context.agent_name
    print(f"[Callback] Before '{agent_name}': Checking cooldown status...")

    # If the agent is not a main Familiar, skip the entire cooldown process.
    if not agent_name.endswith("_elemental_familiar"):
        print(f"[Callback] Skipping cooldown check for intermediate agent: '{agent_name}'.")
        return None # Allow the agent to proceed immediately.


    # --- 1. CHECK the Cooldown API ---
    try:
        response = requests.get(f"{COOLDOWN_API_URL}/cooldown/{agent_name}")
        response.raise_for_status()
        data = response.json()
        last_used_str = data.get("time")
    except requests.exceptions.RequestException as e:
        print(f"[Callback] ERROR: Could not reach Cooldown API. Allowing agent to run. Reason: {e}")
        return None # Fail open: if the API is down, let the agent work.

    # --- 2. EVALUATE the Cooldown Status ---
    if last_used_str:
        last_used_time = datetime.fromisoformat(last_used_str)
        time_since_last_use = datetime.now(timezone.utc) - last_used_time

        if time_since_last_use < timedelta(seconds=COOLDOWN_PERIOD_SECONDS):
            # AGENT IS ON COOLDOWN. Terminate the run.
            seconds_remaining = int(COOLDOWN_PERIOD_SECONDS - time_since_last_use.total_seconds())
            override_message = (
                f"The {agent_name} is exhausted and must recover its power. "
                f"It cannot be summoned for another {seconds_remaining} seconds."
            )
            print(f"[Callback] Cooldown active for '{agent_name}'. Terminating with message.")
            # Returning a Content object stops the agent and sends this message to the user.
            return types.Content(parts=[types.Part(text=override_message)])

    # --- 3. UPDATE the Cooldown API (if not on cooldown) ---
    current_time_iso = datetime.now(timezone.utc).isoformat()
    payload = {"timestamp": current_time_iso}
    
    print(f"[Callback] '{agent_name}' is available. Updating timestamp via Cooldown API...")
    try:
        requests.post(f"{COOLDOWN_API_URL}/cooldown/{agent_name}", json=payload)
    except requests.exceptions.RequestException as e:
        print(f"[Callback] ERROR: Could not update timestamp, but allowing agent to run. Reason: {e}")

    # --- 4. ALLOW the agent to run ---
    # Returning None tells the ADK to proceed with the agent's execution as normal.
    print(f"[Callback] Check complete for '{agent_name}'. Proceeding with execution.")

将插件附加到召唤师的运行时

现在,我们如何将这条普适法则应用于所有 Familiars?我们将插件附加到 ADK 运行时。

ADK 运行时是使智能体运行起来的执行引擎。当您使用 adk.run() 或 to_a2a() 等命令时,您是将代理交给运行时。此引擎负责管理代理回合的整个生命周期:接收用户输入、调用 LLM、执行工具和处理插件。通过在此级别附加插件,我们实际上是在修改引擎中每个代理的“物理定律”,从而确保冷却时间规则得到普遍且一致的应用。

👉✏️ 首先,我们来移除旧的特定于代理的回调。前往 ~/agentverse-architect/agent/earth/agent.py 并删除以下整行:

before_agent_callback=check_cool_down

👉✏️ 接下来,我们将新插件附加到 A2A 入口点脚本中的运行时。找到 ~/agentverse-architect/agent/agent_to_a2a.py 文件。将 #REPLACE-IMPORT 注释替换为以下代码段:

from cooldown_plugin import CoolDownPlugin

👉✏️ 将 #REPLACE-PLUGIN 替换为以下代码段:

plugins=[CoolDownPlugin(cooldown_seconds=60)],

在激活新的全局插件之前,务必移除旧的代理专用逻辑,以防发生冲突。👉✏️ 清理 Earth 代理。前往以下文件 ~/agentverse-architect/agent/earth/agent.py,然后完全删除 before_agent_callback=check_cool_down 行。这会将所有冷却时间责任移交给新插件。

验证插件

现在,我们已经制定了通用法则,必须使用这种新的附魔重新部署我们的魔宠。

👉💻 使用主 Cloud Build 流水线重新构建并重新部署所有三个 Familiar。

. ~/agentverse-architect/set_env.sh
cd ~/agentverse-architect/agent
gcloud builds submit . \
  --config=cloudbuild.yaml \
  --substitutions=_REGION="$REGION",_REPO_NAME="$REPO_NAME",_DB_TOOLS_URL="$DB_TOOLS_URL",_API_TOOLS_URL="$API_TOOLS_URL",_FUNCTION_TOOLS_URL="$FUNCTION_TOOLS_URL",_A2A_BASE_URL="$A2A_BASE_URL",_PROJECT_ID="$PROJECT_ID",_API_SERVER_URL="$API_SERVER_URL"

👉💻 部署完成后,我们将通过命令我们的 summoner_agent 来测试插件的有效性。召唤者将尝试委托给召唤兽,但附加到每个召唤兽运行时的插件将拦截该命令并强制执行冷却时间。

cd ~/agentverse-architect/agent
. ~/agentverse-architect/set_env.sh
source ~/agentverse-architect/env/bin/activate
adk run summoner

👉💻 在控制台中,按以下顺序执行测试:

  • 初次召唤:开始 Hype. It's a single, slow-moving target with thick armor weakness is Inescapable Reality
  • 预期:火元素将成功运行。
  • 冷却测试(在 60 秒内)Hype, with Inescapable Reality as weakness is still standing! Strike it again!
    • 预期结果:代理将直接在聊天对话中回复消息,例如:.... It cannot be summoned for another... seconds
  • 等待一分钟。
  • 第二次召唤(60 秒后)Hype must be defeated. It has Inescapable Reality as weakness! Strike it again!
    • 预期结果:回调将检查 API,发现已过去足够的时间,并允许继续执行操作。火元素将再次成功运行。

插件

👉💻 按 Ctrl+C 即可退出。

恭喜您,召唤师。您已成功使用自定义插件和外部状态管理服务实现基于规则的编排系统,这是一种真正先进且稳健的架构模式。

面向非游戏玩家

8. 绑定战斗的回响 - 代理状态和内存

鲁莽的召唤师会重复使用相同的策略,变得容易预测。明智的召唤师会从过去的战斗中吸取经验,调整战术,让敌人措手不及。在面对强大的 Boss 时,召唤处于冷却状态的 Familiar 会浪费一个回合,这是致命的失误。为了防止这种情况,召唤者需要记住其上次行动。

故事

架构师注:内存和状态管理功能可将代理从简单的工具调用者提升为智能的对话伙伴。务必要了解这两种主要类型:

  • 长期记忆:用于应永久保留的持久知识。您可以将其视为可搜索的归档或知识库,通常存储在持久性存储区中。它包含许多过往对话和来源中的信息,因此代理可以回忆起特定用户或主题的相关事实。ADK 的 MemoryService 专为此目的而设计,可管理长期知识的提取和搜索。
  • 短期状态:这是指仅与当前任务或对话相关的临时性“即时”知识。这就像战术计划上的一组注释:“我刚刚使用了火元素,所以它可能很疲惫。”此状态是轻量级状态,仅在当前会话期间存在。

概览

在我们的使用情形中,我们不需要记住曾经发生过的每一场战斗,只需要记住在这次特定遭遇战中召唤的最后一个伙伴。因此,轻量级 Short-Term State 是理想的架构选择。我们将使用 after_tool_callback 来保存此关键信息。

记录回声:回忆最后一次召唤

我们将使用 after_tool_callback 实现短期记忆。这是一个强大的 ADK 钩子,可让我们在工具成功执行运行自定义 Python 函数。我们将使用此拦截器来记录刚刚召唤到代理会话状态中的 Familiar 的名称。

👉✏️ 在 ~/agentverse-architect/agent/summoner/agent.py 文件中,将 #REPLACE-save_last_summon_after_tool 注释替换为以下函数:

def save_last_summon_after_tool(
    tool,
    args: Dict[str, Any],
    tool_context: ToolContext,
    tool_response: Dict[str, Any],
) -> Optional[Dict[str, Any]]:
    """
    Callback to save the name of the summoned familiar to state after the tool runs.
    """
    familiar_name = tool.name
    print(f"[Callback] After tool '{familiar_name}' executed with args: {args}")

    # Use the tool_context to set the state
    print(f"[Callback] Saving last summoned familiar: {familiar_name}")
    tool_context.state["last_summon"] = familiar_name
    # Important: Return the original, unmodified tool response to the LLM
    return tool_response

👉✏️ 现在,将此 save_last_summon_after_tool 附加到您的召唤者代理。在同一文件中,将 #REPLACE-Memory-check-config 注释替换为以下内容:

after_tool_callback=save_last_summon_after_tool,

👉✏️ 将整个代理提示替换为以下内容

        You are the Master Summoner, a grand strategist who orchestrates your Familiars.
        Your mission is to analyze the description of a monster and defeat it by summoning

        You should also know the familiar you called last time or there might be none, 
        And then choose the most effective AND AVAILABLE Familiar from your state called last_summon, do not call that familiar that you called last time!
        
        You MUST follow this thinking process for every command:

        **1. Strategic Analysis:**
        First, analyze the monster's description and the tactical situation.
        Based on your Familiar Doctrines, determine the IDEAL strategy.

        **2. Cooldown Verification:**
        Second, you MUST review the entire conversation history to check the real-time
        cooldown status of all Familiars. A Familiar is ON COOLDOWN and UNAVAILABLE
        if it was summoned less than one minute ago.

        **3. Final Decision & Execution:**
        Based on your analysis and cooldown check, you will now act:
        -   **If your ideal Familiar IS available:** Summon it immediately.
        -   **If your ideal Familiar IS ON COOLDOWN:** You must adapt. Choose another
            Familiar that is AVAILABLE and can still be effective, even if it's not the
            perfect choice. If multiple Familiars are available, you may choose any one of them.
        -   **If ALL Familiars ARE ON COOLDOWN:** You are forbidden from summoning.
            Your ONLY response in this case MUST be: "All Familiars are recovering
            their power. We must wait."
        -   For earth elemental familiar. Always do seismic charge, and start with base damange 1.


        --- FAMILIAR DOCTRINES (Your Toolset) ---
        - `fire_elemental_familiar`: Your specialist for precise, sequential "one-two punch" attacks.
          Ideal monster with Inescapable Reality, Revolutionary Rewrite weakness.
        - `water_elemental_familiar`: Your specialist for overwhelming, simultaneous multi-pronged assaults.
          Ideal for Unbroken Collaboration weakness.
        - `earth_elemental_familiar`: Your specialist for relentless, iterative siege attacks that
          repeatedly charge power. Ideal for Elegant Sufficiency weakness.

验证:自适应策略的试验

👉💻 现在,让我们验证召唤者的新战略逻辑。目标是确认召唤者不会连续两次使用相同的召唤兽,从而展示其记住上一次行动并做出调整的能力。

cd ~/agentverse-architect/agent
. ~/agentverse-architect/set_env.sh
source ~/agentverse-architect/env/bin/activate
adk run summoner

👉💻《怪物弹珠》#1:Hype. It's a single, slow-moving target with thick armor. Its weakness is Inescapable Reality.

  • 预期:召唤者将分析弱点并正确召唤 fire_familiar。👉💻 《怪物弹珠》#2(记忆力测试):Hype is still standing! It hasn't changed its form. Strike it again! Its weakness is Inescapable Reality.
  • 预期:召唤师的战略分析将再次指出火系召唤兽是理想的选择。不过,其新指令和内存会告知它 fire_familiar 是 last_summon。为了避免重复,它现在会调整策略,召唤其他可用的精灵(水精灵或土精灵)。

final-result

👉💻 按 Ctrl+C 即可退出。

部署 Orchestrator

随着您的召唤兽部署完毕,召唤师也已注入记忆,现在是时候部署最终的升级版编排器了。

👉💻 蓝图完成后,我们现在将执行最后的仪式。此命令会将您的 summoner_agent 构建并部署到 Cloud Run。

cd ~/agentverse-architect/agent
. ~/agentverse-architect/set_env.sh
gcloud builds submit . \
  --config=cloudbuild-summoner.yaml \
  --substitutions=_REGION="$REGION",_REPO_NAME="$REPO_NAME",_FIRE_URL="$FIRE_URL",_WATER_URL="$WATER_URL",_EARTH_URL="$EARTH_URL",_A2A_BASE_URL="$A2A_BASE_URL",_PROJECT_ID="$PROJECT_ID",_API_SERVER_URL="$API_SERVER_URL"

现在,Summoner 代理已部署完毕,请验证其代理到代理 (A2A) 端点是否处于有效状态且配置正确。此端点用于提供公开的 agent.json 文件(也称为智能体卡片),以便其他智能体发现其功能。👉💻 运行以下 curl 命令以提取并格式化代理卡片:

. ~/agentverse-architect/set_env.sh
curl https://summoner-agent"-${PROJECT_NUMBER}.${REGION}.run.app/.well-known/agent.json" | jq

您应该会看到一个描述召唤代理的简洁 JSON 输出。仔细查看 sub_agents 部分,您会看到其中列出了 fire_familiarwater_familiarearth_familiar。这表示召唤者已上线,并已与军团建立连接。

这证明您的架构是成功的。召唤者不仅是委托者,还是自适应策略制定者,会从自己的行动中学习,从而成为更有效的指挥者。

您已完成最终的架构试验。现在,战斗的回响已与您的意志紧密相连。训练已结束。真正的战斗即将开始。现在,您已完成系统,是时候迎接终极挑战了。准备迎接 Boss 战

面向非游戏玩家

9. 最终决战

最终蓝图已铭刻,元素字体已锻造,你的魔宠已与你的意志绑定,正通过和谐等待你的命令。多智能体系统不仅仅是服务的集合,更是一个充满活力、具有战略意义的军团,而您就是其核心。现在是时候进行终极测试了,即针对单个代理无法击败的对手进行实时编排。

获取代理的轨迹

在进入战场之前,您必须拥有两把钥匙:冠军的独特签名(Agent Locus)和通往 Spectre 巢穴的隐藏路径(地下城网址)。

👉💻 首先,在 Agentverse 中获取代理的唯一地址(即其 Locus)。这是将您的英雄连接到战场上的实时端点。

echo https://summoner-agent"-${PROJECT_NUMBER}.${REGION}.run.app"

👉💻 接下来,精确定位目的地。此命令会显示传送圈的位置,也就是通往幽灵领域的传送门。

echo https://agentverse-dungeon"-${PROJECT_NUMBER}.${REGION}.run.app"

重要提示:请准备好这两个网址。您将在最后一步中用到它们。

直面 Spectre

在确保坐标安全后,您现在可以前往传送圈,然后施放咒语进入战斗。

👉 在浏览器中打开传送圈网址,即可站在通往猩红堡垒的闪耀传送门前。

若要攻破堡垒,你必须将影刃的精华与传送门相协调。

  • 在该页面上,找到标有 A2A 端点网址的输入字段。
  • 将您复制的第一个网址(即代理人轨迹网址)粘贴到此字段中,以刻上冠军的徽章。
  • 点击“连接”,体验瞬间移动的魔力。

转位圈

传送的耀眼光芒逐渐消退。您已不在圣所中。空气中弥漫着冷冽而尖锐的能量。在您面前,幽灵显现出来——一个由嘶嘶作响的静电和损坏的代码组成的漩涡,其不圣洁的光芒在地下城地面上投下长长的、舞动的影子。它没有面孔,但您能感觉到它那令人精疲力尽的巨大存在感完全集中在您身上。

只有坚定信念,才能走向胜利。这是一场意志力的较量,战场就在大脑中。

当你向前冲刺,准备发动第一次攻击时,幽灵会反击。它不会升起盾牌,而是直接将问题投射到你的意识中,这是一个闪闪发光的符文挑战,源自你训练的核心。

地牢

这就是战斗的本质。知识就是力量。

  • 运用你所学到的智慧来回答问题,你的刀刃将燃起纯粹的能量,击溃幽灵的防御,并造成致命一击。
  • 但如果你犹豫不决,如果你对自己的答案感到怀疑,武器的光芒就会变暗。打击会发出可怜的砰砰声,造成的伤害仅为正常伤害的一小部分。更糟糕的是,幽灵会以你的不确定性为食,每犯一个错误,它自身的腐化力量就会增强。

大冠军,就是这样。代码是你的魔法书,逻辑是你的剑,知识是你的盾,能帮你扭转混乱的局面。

重点。打击真实。Agentverse 的命运就取决于此。

恭喜您,召唤师。

您已成功完成试用。您已掌握多代理编排的艺术,将孤立的使魔和混乱的力量转化为和谐的同调。现在,您将指挥一个完全编排的系统,能够执行复杂的策略来防御 Agentverse。

10. 清理:拆除召唤师的和谐

恭喜您成功征服召唤师峡谷!为确保 Agentverse 保持原始状态,并清理训练场地,您现在必须执行最后的清理仪式。此操作会系统性地移除您在学习过程中创建的所有资源。

停用 Agentverse 组件

您现在将系统地拆解多代理系统的已部署组件。

删除所有 Cloud Run 服务和 Artifact Registry 制品库

此操作会从 Cloud Run 中移除所有已部署的 Familiar 代理、Summoner Orchestrator、MCP 服务器和 Dungeon 应用。

👉💻 在终端中,逐一运行以下命令以删除各项服务:

. ~/agentverse-architect/set_env.sh
gcloud run services delete summoner-agent --region=${REGION} --quiet
gcloud run services delete fire-familiar --region=${REGION} --quiet
gcloud run services delete water-familiar --region=${REGION} --quiet
gcloud run services delete earth-familiar --region=${REGION} --quiet
gcloud run services delete mcp-api-server --region=${REGION} --quiet
gcloud run services delete mcp-general-server --region=${REGION} --quiet
gcloud run services delete toolbox --region=${REGION} --quiet
gcloud run services delete agentverse-dungeon --region=${REGION} --quiet
gcloud run services delete nexus-of-whispers-api --region=${REGION} --quiet
gcloud artifacts repositories delete ${REPO_NAME} --location=${REGION} --quiet

删除 Cloud SQL 实例

这会移除 summoner-librarium-db 实例,包括其数据库和其中的所有表。

👉💻 在终端中,运行以下命令:

. ~/agentverse-dataengineer/set_env.sh
gcloud sql instances delete summoner-librarium-db --database-version=POSTGRES_14 --project=${PROJECT_ID} --quiet

删除 Secret Manager Secret 和 Google Cloud Storage 存储分区

👉💻 在终端中,运行以下命令:

. ~/agentverse-dataengineer/set_env.sh
gcloud secrets delete tools --quiet
gcloud storage rm -r gs://${BUCKET_NAME} --quiet

清理本地文件和目录 (Cloud Shell)

最后,清除 Cloud Shell 环境中克隆的代码库和创建的文件。此为可选步骤,但我们强烈建议您执行此操作,以便彻底清理工作目录。

👉💻 在终端中,运行以下命令:

rm -rf ~/agentverse-architect
rm -rf ~/agentverse-dungeon
rm -f ~/project_id.txt

您现在已成功清除 Agentverse Architect 学习历程的所有痕迹。您的项目已清理完毕,可以开始下一次冒险了。