1. 概览
欢迎来到“第二天”。虽然构建应用并点击“发布”按钮很神奇,但实际流量会带来实际故障。您无需再花费大量时间处理 YAML 或在日志中搜索,而是可以构建一群专门的代理来为您管理运营管道。此 Codelab 将向您展示 Google Cloud 的统一堆栈(Eventarc、Cloud Run、Firestore、Cloud Build、BigQuery)如何使代理能够轻松安全地提取密钥、流式传输日志和开箱即用地解决问题。

在此 Codelab 中,您将从头开始构建 DinoQuest(一款由 Gemini 提供支持的恐龙冒险游戏),并将其连接到完全智能化的 CI/CD 流水线。完成本课程后,您将能够:
- 在 Cloud Run 上运行的 DinoQuest Web 应用(服务名称:
dinoquest) - 一种日志分析流水线,可将 Cloud Run 日志流式传输到 BigQuery 并生成交互式游戏数据分析信息中心
- 修复代理 (
remediation-agent) - 一种 ADK 修复代理,用于监控 Cloud Run 错误并自动修复这些错误,部署为由 Eventarc 触发的自有 Cloud Run 服务 - 一个 CI 代理 (
ci-agent),用于读取 PR diff、智能确定测试范围、通过 Cloud Build 构建 Docker 映像,并将提交状态发布回 GitHub - 一种 CD 代理,可对部署风险进行评分、拆分流量、监控指标,并自动升级或回滚
学习内容
- 如何在 Cloud Run 上将全栈 Vite + FastAPI 应用部署为单个容器
- 如何为 React 应用配置 Firebase Auth 和 Firestore
- 如何构建和部署通过 Eventarc 对 Pub/Sub 事件做出反应的 ADK 代理
- 如何将 Cloud Run 日志路由到 BigQuery 并查询游戏分析
- 如何为 CI 和 Canary 部署编写 agentic 技能
所需条件
- 启用了结算功能的 Google Cloud 项目
- Firebase 项目(可以是同一个 GCP 项目)
- 一个 GitHub 账号以及 DinoQuest 代码库的一个 fork
- 使用内置 Gemini 的 Antigravity(Google 的代理运行器)
- 已安装
gcloudCLI 并已进行身份验证 - 请参阅下方的安装说明 node≥ 18 且npmpython3≥ 3.11git和gh(GitHub CLI)
安装 gcloud CLI
macOS
brew install --cask google-cloud-sdk
您也可以从 cloud.google.com/sdk/docs/install 下载安装程序。
Windows
winget install Google.CloudSDK
或者,从 cloud.google.com/sdk/docs/install 下载 Windows 安装程序 (.exe) 并运行它。
安装后,初始化并进行身份验证:
gcloud init
gcloud auth login
gcloud auth application-default login
2. 设置 Firebase
每个代理都需要数据来进行推理。DinoQuest 使用 Firestore 和 Firebase Auth 提供可用于生产用途的数据层,我们的代理稍后将使用自然语言发现、探索和更新该数据层。
由于此应用是通过 AI Studio 生成的,因此与 Firebase 深度集成。使用 Firebase 有多项优势,其中最主要的是它提供预先保护的架构和开箱即用的受管理数据访问权限,可确保游戏状态从第一天起就受到保护。
A. 创建 Firebase 项目
- 前往 console.firebase.google.com
- 点击添加项目(隐藏在“创建新项目”选项中)→ 选择现有的 GCP 项目(或创建新项目)
- 如果系统提示您停用 Google Analytics,请停用 → 点击创建项目(您也可以选择使用默认设置)
B. 启用 Google 身份验证
- 在 Firebase 控制台中,依次前往安全性 → 身份验证(开始使用)→ 登录方法
- 依次点击 Google → 切换 启用 → 保存支持电子邮件地址 → 保存
C. 将 localhost 添加为已获授权的网域
- 在身份验证界面中,点击设置标签页
- 在已获授权的网域下,确认
localhost已列出(默认情况下应已列出)
D. 创建 Firestore 数据库
- 前往数据库和存储空间 → Firestore 数据库 → 创建数据库
- 选择标准版 → 下一步
- 选择区域
us-central1(或与您的 Cloud Run 区域保持一致) - 依次选择以生产模式开始 → 创建
创建数据库后,请记下您的数据库 ID,除非您为其命名,否则该 ID 看起来会像 (default)。
E. 设置 Firebase 安全规则
在 Firestore 数据库 → 规则中,将默认规则替换为:
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
// ===============================================================
// Helper Functions
// ===============================================================
function isAuthenticated() {
return request.auth != null;
}
function isOwner(userId) {
return isAuthenticated() && request.auth.uid == userId;
}
function isValidUser(data) {
return data.keys().hasAll(['uid', 'email']) &&
data.uid is string && data.uid.size() > 0 &&
(data.email == null || (data.email is string && data.email.matches("^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$")));
}
function isValidDinosaur(data) {
return data.keys().hasAll(['userId', 'name', 'type']) &&
data.userId == request.auth.uid &&
data.name is string && data.name.size() > 0 && data.name.size() < 50 &&
data.type in ['Speedy', 'Tank', 'Balanced', 'Agile'];
}
function isValidGame(data) {
return data.keys().hasAll(['userId', 'score']) &&
data.userId == request.auth.uid &&
data.score is number && data.score >= 0;
}
match /users/{userId} {
allow read: if isOwner(userId);
allow create: if isOwner(userId) && isValidUser(request.resource.data);
allow update: if isOwner(userId) && isValidUser(request.resource.data);
match /dinosaurs/{dinoId} {
allow read: if isOwner(userId);
allow create: if isOwner(userId) && isValidDinosaur(request.resource.data);
allow update: if isOwner(userId) && isValidDinosaur(request.resource.data);
}
match /games/{gameId} {
allow read: if isOwner(userId);
allow create: if isOwner(userId) && isValidGame(request.resource.data);
}
match /seenAnnouncements/{announcementId} {
allow read, create: if isOwner(userId);
}
}
match /announcements/{announcementId} {
allow read: if isAuthenticated();
}
// Default deny
match /{document=**} {
allow read, write: if false;
}
match /scores/{scoreId} {
allow read: if true;
allow create: if isAuthenticated();
allow update: if false;
}
}
}
点击发布。
F. 添加 Web 应用并获取配置
- 依次前往项目设置(齿轮图标)→ 常规标签页
- 滚动到您的应用 → 点击添加应用 → 选择网站图标 (
) - 为其命名
dinoquest→ 注册应用 - 复制显示的
firebaseConfig对象 - 您稍后会用到它
3. 运行游戏
代理角色:环境。在让代理开始工作之前,我们需要为它们提供一个可供管理的虚拟世界。在此步骤中,我们将部署 DinoQuest 的“第一天”版本。这会创建我们的 swarm 稍后将发现和管理的实时服务、日志和状态。

从以下两个选项中选择一个。任一方法都会生成一个 GEMINI_API_KEY,您可以在后续每个步骤中以相同方式使用该 GEMINI_API_KEY,无需进行其他更改。
A. 设置 Gemini API 密钥
选项 A - Vertex AI Gemini API 密钥(如果您有 GCP 项目,建议使用此选项)
借助 Vertex AI,您可以创建直接与您的 GCP 项目相关联并向该项目收取费用的 Gemini API 密钥,使用项目的默认服务账号即可,无需单独的 AI Studio 账号。
- 导出您的 GCP 项目 ID:
export PROJECT_ID=<YOUR_PROJECT_ID> - 启用所需的 API 并向 Compute Engine 默认服务账号授予必要的权限:
gcloud auth application-default set-quota-project $PROJECT_ID gcloud config set project $PROJECT_ID # Enable Vertex AI, Compute Engine, and Generative Language APIs gcloud services enable aiplatform.googleapis.com \ compute.googleapis.com \ generativelanguage.googleapis.com # Grant Vertex AI User role to the default compute service account PROJECT_NUMBER=$(gcloud projects describe $PROJECT_ID --format="value(projectNumber)") gcloud projects add-iam-policy-binding $PROJECT_ID \ --member="serviceAccount:${PROJECT_NUMBER}-compute@developer.gserviceaccount.com" \ --role="roles/aiplatform.user" \ --condition=None - 在 Cloud 控制台中打开 Vertex AI API 密钥页面
- 点击创建凭据 → 选择 API 密钥
- 在创建对话框中:
- 将该密钥命名为
Dino_Key - 勾选通过服务账号对 API 调用进行身份验证复选框
- 在“服务账号”下,选择默认计算服务账号 (
PROJECT_NUMBER-compute@developer.gserviceaccount.com) - 返回到选择 API 限制,勾选 GEMINI API
- 点击创建
- 将该密钥命名为
- 复制生成的密钥。
选项 B - AI Studio(最适合本地开发)
- 打开 aistudio.google.com
- 点击左侧边栏中的 Get API key(获取 API 密钥)
- 点击创建 API 密钥 → 选择您的 GCP 项目 → 复制密钥
在后续步骤中,任一键都将设置为 GEMINI_API_KEY,后端会以相同的方式处理这两个键。
克隆存储库
课程代码库位于 https://github.com/gca-americas/dinoquest 中。请先将其派生到您自己的 GitHub 账号。我们会让代理处理您的报告。
派生后,克隆派生的 DinoQuest 代码库的 main 分支,然后进入项目目录:
git clone https://github.com/YOUR_GITHUB_USERNAME/dinoquest.git
cd dinoquest
B. 设置环境变量
在本 Codelab 的学习过程中,每次打开新的 Bash 终端时,请务必设置这些必需的环境变量。将占位值替换为您的实际项目详细信息:
首先,导出您的 GitHub 代码库网址:
export GITHUB_REPO_URL=https://github.com/YOUR_GITHUB_USERNAME/dinoquest
然后,导出其余环境变量:
export PROJECT_ID=your-project-id
export GOOGLE_CLOUD_PROJECT=$PROJECT_ID
export CLOUD_RUN_REGION=us-central1
export GOOGLE_GENAI_USE_VERTEXAI=True
export HARNESS_EVENTS_TOPIC=projects/$PROJECT_ID/topics/harness-events
export CLOUD_BUILD_REPO=<YOUR_GITHUB_USERNAME>-dinoquest
确认结构是否正确:
dinoquest/
├── backend/ # FastAPI backend (serves frontend + Gemini API calls)
├── frontend/ # React/Vite frontend
├── skills/ # Agentic CI/CD skill files
├── Dockerfile # Multi-stage build (React → Python)
├── start.sh # Local dev launcher
└── README.md
B. 创建后端环境文件
首先,导出 Gemini API 密钥:
export GEMINI_API_KEY=YOUR_GEMINI_API_KEY_FROM_STEP_2
然后创建 .env 文件:
cat > backend/.env <<EOF
GEMINI_API_KEY=$GEMINI_API_KEY
GOOGLE_GENAI_USE_VERTEXAI=False
GOOGLE_CLOUD_PROJECT=$PROJECT_ID
EOF
C. 启用 Firebase App Check / 服务账号(适用于 Cloud Run)
在 Cloud Run 上运行时,后端使用应用默认凭证与 Firebase 通信,无需服务账号密钥文件。backend/main.py 中的 firebase_admin.initialize_app() 调用会自动处理此问题。
对于本地开发,请进行一次身份验证:
gcloud auth application-default login
D. 创建 Firebase 应用配置文件
在 frontend/ 目录中,使用上一步中的配置创建 firebase-applet-config.json:
{
"apiKey": "YOUR_API_KEY",
"authDomain": "YOUR_PROJECT_ID.firebaseapp.com",
"projectId": "YOUR_PROJECT_ID",
"storageBucket": "YOUR_PROJECT_ID.appspot.com",
"messagingSenderId": "YOUR_SENDER_ID",
"appId": "YOUR_APP_ID",
"firestoreDatabaseId": "(default)"
}
注意: firestoreDatabaseId 应与您在上一步中创建的数据库 ID 一致。如果您使用的是默认值,请将其保留为 "(default)"。
将更改提交回代码库:
git add frontend/firebase-applet-config.json
git commit -m "chore: add firebase config"
git push origin main
C. 在本地运行 DinoQuest
1. 启用必需的 API
gcloud services enable \
run.googleapis.com \
cloudbuild.googleapis.com \
artifactregistry.googleapis.com \
secretmanager.googleapis.com \
firestore.googleapis.com \
logging.googleapis.com \
pubsub.googleapis.com \
eventarc.googleapis.com \
aiplatform.googleapis.com \
bigquery.googleapis.com \
aiplatform.googleapis.com
2. 开始 DinoQuest
start.sh 脚本会构建 React 前端,并将终端交给 FastAPI 后端,后者会提供已编译的静态文件:
cd backend
python3 -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt -q
cd ..
# Force-remove the Vertex AI flag from the current terminal session to avoid conflicts
unset GOOGLE_GENAI_USE_VERTEXAI
./start.sh
在浏览器中打开 http://localhost:8000。您应该会看到 DinoQuest 标题界面。使用 Google 账号登录,生成您的第一个恐龙,并确认它已保存到 Firestore。
问题排查:如果您看到空白页面或 Firebase 身份验证错误,请仔细检查 frontend/firebase-applet-config.json 是否具有正确的值,以及 localhost 是否在已获授权的网域列表中。
E. 将 DinoQuest 部署到 Cloud Run
1. 设置项目
export PROJECT_ID=$(gcloud config get-value project)
3. 创建 Artifact Registry 仓库
gcloud artifacts repositories create dinoquest \
--repository-format=docker \
--location=$CLOUD_RUN_REGION \
--description="DinoQuest container images"
4. 将 Gemini API 密钥存储在 Secret Manager 中
echo -n $GEMINI_API_KEY | \
gcloud secrets create gemini-api-key --data-file=-
# Grant the default compute service account access to the secret
PROJECT_NUMBER=$(gcloud projects describe $PROJECT_ID --format="value(projectNumber)")
gcloud secrets add-iam-policy-binding gemini-api-key \
--member="serviceAccount:${PROJECT_NUMBER}-compute@developer.gserviceaccount.com" \
--role="roles/secretmanager.secretAccessor"
5. 使用 Cloud Build 构建容器映像
gcloud builds submit \
--tag $CLOUD_RUN_REGION-docker.pkg.dev/$PROJECT_ID/dinoquest/app:latest .
此命令会运行多阶段 Dockerfile:先构建 React 应用,然后将输出打包到 FastAPI 映像中。大约需要 3-5 分钟。
6. 部署到 Cloud Run
首先,导出您的管理员电子邮件地址:
export ADMIN_EMAIL=<YOUR_TEST_ACCOUNT_EMAIL>
然后部署服务:
gcloud run deploy dinoquest \
--image=$CLOUD_RUN_REGION-docker.pkg.dev/$PROJECT_ID/dinoquest/app:latest \
--region=$CLOUD_RUN_REGION \
--platform=managed \
--allow-unauthenticated \
--memory=128Mi \
--set-secrets="GEMINI_API_KEY=gemini-api-key:latest" \
--set-env-vars="ADMIN_EMAILS=$ADMIN_EMAIL" \
--set-env-vars="GOOGLE_GENAI_USE_VERTEXAI=False" \
--set-env-vars="GOOGLE_CLOUD_PROJECT=$PROJECT_ID"
命令完成后,Cloud Run 会输出服务网址。复制此网址 - 您需要使用此网址在 Firebase 中授权网域。
7. 在 Firebase 中授权 Cloud Run 网域
如要允许用户从已部署的应用登录,您必须将 Cloud Run 网址添加到 Firebase 的已获授权的网域:
- 返回 Firebase 控制台 → 身份验证 → 设置 → 授权网域
- 点击添加网域
- 粘贴 Cloud Run 服务网址(例如
dinoquest-xxxxx.us-central1.run.app)- 移除https://前缀 - 点击保存
8. 播种排行榜数据
为了让游戏具备一些初始“活力”,并确保代理有数据可供使用,您可以为排行榜添加一些初始得分。
- 确保您位于
dinoquest根目录中:cd ~/dinoquest - 创建并激活虚拟环境:
python3 -m venv venv source venv/bin/activate - 安装所需的 Firestore 依赖项:
pip install google-cloud-firestore - 运行初始配置脚本:
python3 prep/seed_scores.py - 停用虚拟环境:
deactivate
现在,您可以在浏览器中打开服务网址,DinoQuest 已完全上线!
4. 设置 Dino Theater
代理角色:可视化工具。如何监控自主代理团队?Dino Theater 可实时展示智能体群组的思维过程。您无需盯着终端日志,而是可以在实时可视化信息中心内观看智能体在云端进行推理、相互调用和执行任务。

A. 将 Dino Theater 部署到 Cloud Run
首先,返回到您的主目录并克隆 Dino Theater 代码:
cd ~
git clone https://github.com/gca-americas/dinoquest-theater.git
cd dinoquest-theater
- 构建并推送容器:
gcloud builds submit --tag $CLOUD_RUN_REGION-docker.pkg.dev/$PROJECT_ID/dinoquest/dino-theater:latest . - 设置服务账号和权限:
# Create the service account gcloud iam service-accounts create dino-theater # Create the Pub/Sub topic (if you haven't yet) gcloud pubsub topics create harness-events # Create the subscription gcloud pubsub subscriptions create harness-events-theater \ --topic=harness-events # Grant subscriber role gcloud pubsub subscriptions add-iam-policy-binding harness-events-theater \ --member="serviceAccount:dino-theater@${PROJECT_ID}.iam.gserviceaccount.com" \ --role="roles/pubsub.subscriber" - 部署应用:
注意:建议使用gcloud run deploy dino-theater \ --image $CLOUD_RUN_REGION-docker.pkg.dev/$PROJECT_ID/dinoquest/dino-theater:latest \ --region=$CLOUD_RUN_REGION \ --service-account=dino-theater@${PROJECT_ID}.iam.gserviceaccount.com \ --set-env-vars="GOOGLE_CLOUD_PROJECT=$PROJECT_ID" \ --allow-unauthenticated \ --min-instances=1--min-instances=1来保持事件之间的 SSE 连接处于有效状态。 - 验证其是否正常运行:在浏览器中打开已部署的服务网址(例如
https://dino-theater-xxx-uc.a.run.app/demo)。
5. IDE 中的智能体 DevOps
代理角色:原生反重力。为了弥合 IDE 与云之间的差距,我们将 Antigravity 连接到 Google Cloud 的受管 MCP 服务器。这样一来,原生代理就可以“看到”您的项目,从而能够解析日志、检查指标并推理基础架构,而无需您费心管理 API 密钥或切换到控制台。
在运行任何技能之前,您需要配置 Antigravity 对 Google Cloud 的访问权限,并加载 DinoQuest 技能剧本。
A. 安装 Google 管理的 MCP 服务
Google 的托管 MCP 服务通过单个托管端点提供对所有 Google Cloud API 的访问权限。
使用应用默认凭证进行身份验证:
gcloud auth application-default login
B. 配置 mcp_config.json
在 Antigravity 配置目录(通常为 ~/.gemini/antigravity/mcp_config.json)中或从控制台创建或更新 mcp_config.json。这样一来,Antigravity 就能访问技能所需的 Google Cloud 和 GitHub 工具:
{
"mcpServers": {
"google-developer-knowledge": {
"serverUrl": "https://developerknowledge.googleapis.com/mcp",
"authProviderType": "google_credentials"
},
"google-bigquery": {
"serverUrl": "https://bigquery.googleapis.com/mcp",
"authProviderType": "google_credentials"
},
"google-cloud-logging": {
"serverUrl": "https://logging.googleapis.com/mcp",
"authProviderType": "google_credentials"
},
"google-cloud-monitoring": {
"serverUrl": "https://monitoring.googleapis.com/mcp",
"authProviderType": "google_credentials",
"disabledTools": [
"get_dashboard",
"list_dashboards"
]
},
"google-cloud-run": {
"serverUrl": "https://run.googleapis.com/mcp",
"authProviderType": "google_credentials",
"disabledTools": [
"deploy_service_from_image",
"deploy_service_from_archive",
"deploy_service_from_file_contents"
]
},
"google-cloud-sql": {
"serverUrl": "https://sqladmin.googleapis.com/mcp",
"authProviderType": "google_credentials",
"disabled": true
},
"google-cloud-trace": {
"serverUrl": "https://cloudtrace.googleapis.com/mcp",
"authProviderType": "google_credentials"
},
"google-error-reporting": {
"serverUrl": "https://clouderrorreporting.googleapis.com/mcp",
"authProviderType": "google_credentials"
},
"google-firestore": {
"serverUrl": "https://firestore.googleapis.com/mcp",
"authProviderType": "google_credentials"
},
"google-resource-manager": {
"serverUrl": "https://cloudresourcemanager.googleapis.com/mcp",
"authProviderType": "google_credentials"
},
"gemini-cloud-assist": {
"serverUrl": "https://geminicloudassist.googleapis.com/mcp",
"authProviderType": "google_credentials"
}
}
}
C. 将技能加载到 Antigravity(可选)
Antigravity 会在特定的标准目录中发现技能。将 DinoQuest 技能从克隆的代码库复制到全局 Antigravity 技能文件夹:
# Create the standard skills directory if it doesn't exist
mkdir -p ~/.gemini/antigravity/skills
# Copy all DinoQuest skills into the global skills folder
cp -r skills/* ~/.gemini/antigravity/skills/
D. 重启 Antigravity(可选)
如需应用 mcp_config.json 更改并加载新复制的技能,请重启 Antigravity 应用。
重启后:
- 验证 google 和 github MCP 服务器在设置中显示绿色的“已连接”状态。
- 验证 DinoQuest 技能是否显示在技能列表中。
注意:每项技能的 SKILL.md 顶部都有一个 ## Configuration 表。复制后,您应更新 ~/.gemini/antigravity/skills/ 中的值,使其与您的项目相符。
E. 在本地 IDE 中修正云服务
- 触发错误:在浏览器中打开已部署的 DinoQuest 网址(在上一步中获得)。
- 前往排行榜:点击排行榜按钮。当前的排行榜实现故意效率低下,它会尝试将大量数据加载到内存中,从而触发内存不足 (OOM) 错误。
- 在 Antigravity Agent Manager(代理中心)中,让它帮助恢复错误并尽可能修复根本原因。
- 提示 1:找出 dinoquest 的问题。
- 提示 2:你能查看 Dinoquest 游戏的代码并修复导致内存不足错误的因素吗?
6. 将日志流式传输到 BigQuery 并生成分析数据
代理角色:数据智能体。将原始日志转化为可行的产品策略不应花费数小时的人工数据整理时间。借助 Data Agent Kit 和 BigQuery MCP,我们创建了一条“零 ETL”流水线,可将日志直接流式传输到 BigQuery 中,从而使代理能够在不到两分钟的时间内生成高级分析信息中心。
log-router-bq-report 技能会设置一个 Cloud Logging 接收器,该接收器可将 DinoQuest 的 Cloud Run 日志持续流式传输到 BigQuery,然后查询数据以生成流量报告和游戏分析数据洞见。

A. 配置技能变量
在 DinoQuest 代码库中打开 skills/log-router-bq-report/SKILL.md,然后更新顶部的配置部分:
| Variable | Your Value |
|---------------|--------------------|
| SERVICE_NAME | dinoquest |
| BQ_DATASET | dinoquest_logs |
| LOG_SINK_NAME | dinoquest-bq-sink |
B. 在 Antigravity 中运行技能
打开 Antigravity,将 DinoQuest 代码库作为上下文,然后告诉 Gemini:
Run the log-router-bq-report skill
该技能将:
- 自动解决您的 GCP 项目问题
- 检查 BigQuery 接收器是否已存在 - 如果不存在,则会创建数据集和接收器。
- 授予 IAM 权限:系统将向接收器的
writerIdentity授予数据集的 BigQuery Data Editor 角色。
注意:与 Eventarc 接收器类似,在此过程中,您可能会看到来自 gcloud 的警告:“请记得向 serviceAccount:service-... 授予数据集的 BigQuery Data Editor 角色。”技能会自动处理此问题。
C. 使用 Antigravity 生成报告
只需让 Antigravity “设置 BigQuery 日志接收器并生成分析报告”即可。智能体将:
- 配置基础架构:创建 BigQuery 数据集和 Cloud Logging 接收器。
- 管理权限:自动向接收器的写入者身份授予必要的 IAM 角色。
- 生成分析洞见:分析日志并生成包含游戏遥测数据和胜率分析的高级交互式 HTML 信息中心。
7. 自我修复修复智能体
代理角色:SRE 代理。如果生产服务在凌晨 2 点出现故障,您不必醒来。此代理会充当您的第一响应者。该功能由 Eventarc 通过 Cloud Run 错误日志触发,可在您登录控制台之前自动分析崩溃情况、提出修复建议并启动修复流水线。
DinoAgent 是一种 ADK 代理,可通过 Eventarc 监听 Cloud Run 错误日志,诊断根本原因,并自动进行补救,方法包括增加内存、回滚流量或在 GitHub 上提交代码修复 PR。

A. 克隆补救代理代码库
cd ~
git clone https://github.com/gca-americas/dinoquest-reme-agent.git
cd dinoquest-reme-agent
项目结构:
dinoquest-reme-agent/
├── main.py # Service entrypoint — receives Eventarc HTTP POST, runs agent
├── runner.py # ADK Runner + session service
├── agent.py # LlmAgent definition, loads skill from file
├── tools.py # Cloud Run v2 API tools (list/get/rollback/update)
├── skills/
│ └── remediation/
│ ├── SKILL.md # Agent playbook — edit this to change behavior
│ └── scripts/ # Shell scripts for the code-fix track
│ ├── clone_repo.sh
│ ├── read_file.sh
│ ├── apply_fix.sh
│ ├── commit_branch.sh
│ ├── open_pr.sh
│ └── rollback_fix.sh
├── requirements.txt
└── Dockerfile
B. 设置 GitHub 访问权限(代码修复轨道)
代码修复轨道会克隆您的 DinoQuest 代码库、读取源文件、应用补丁并打开 PR。它需要具有 repo 范围的 GitHub 个人访问令牌。
- 前往 github.com/settings/tokens → Generate new token (classic)(生成新令牌 [经典])
- 为其命名,选择
repo范围 → 生成令牌 → 复制令牌
将其存储在 Secret Manager 中:
首先,导出您的 GitHub 令牌:
export GH_TOKEN=ghp_YOUR_TOKEN_HERE
然后创建 Secret:
echo -n $GH_TOKEN | \
gcloud secrets create github-token --data-file=-
C. 设置 Slack 通知(可选)
补救措施完成后,DinoAgent 会将摘要发布到 Slack 频道。
- 前往 api.slack.com/apps → Create New App → From scratch
- 将其命名为
DinoAgent,选择您的工作区 → 创建应用 - 依次选择功能 → 传入的 Webhook → 开启切换开关
- 点击将新的 Webhook 添加到工作区 → 选择一个频道 → 点击允许
- 复制 webhook 网址 (
https://hooks.slack.com/services/...)
将其存储在 Secret Manager 中:
export SLACK_TOKEN=YOUR_SLACK_WEBHOOK
echo -n "https://hooks.slack.com/services/$SLACK_TOKEN" | \
gcloud secrets create slack-webhook --data-file=-
D. 创建 DinoAgent 服务账号
gcloud iam service-accounts create remediation-agent \
--display-name="Cloud Run Remediation Agent"
export SA="remediation-agent@${PROJECT_ID}.iam.gserviceaccount.com"
for ROLE in \
roles/run.admin \
roles/iam.serviceAccountUser \
roles/eventarc.eventReceiver \
roles/aiplatform.user \
roles/artifactregistry.reader \
roles/secretmanager.secretAccessor \
roles/pubsub.publisher \
roles/logging.viewer; do
gcloud projects add-iam-policy-binding $PROJECT_ID \
--member="serviceAccount:${SA}" --role="$ROLE" \
--condition=None
done
授予其对密文的访问权限:
for SECRET in github-token slack-webhook; do
gcloud secrets add-iam-policy-binding $SECRET \
--member="serviceAccount:${SA}" \
--role="roles/secretmanager.secretAccessor"
done
E. 构建 DinoAgent 并将其部署到 Cloud Run
# Get Project Number for the CIAgent URL
PROJECT_NUMBER=$(gcloud projects describe $PROJECT_ID --format="value(projectNumber)")
export CIAGENT_URL=https://ci-agent-${PROJECT_NUMBER}.${CLOUD_RUN_REGION}.run.app
export SA="remediation-agent@${PROJECT_ID}.iam.gserviceaccount.com"
export GITHUB_REPO_URL=https://github.com/YOUR_REPO
HARNESS_EVENTS_TOPIC=projects/$PROJECT_ID/topics/harness-events
AGENT_IMAGE="$CLOUD_RUN_REGION-docker.pkg.dev/${PROJECT_ID}/dinoquest/remediation-agent:latest"
gcloud builds submit --tag $AGENT_IMAGE .
gcloud run deploy remediation-agent \
--image=$AGENT_IMAGE \
--region=$CLOUD_RUN_REGION \
--service-account=$SA \
--memory=2Gi \
--set-env-vars="GOOGLE_CLOUD_PROJECT=${PROJECT_ID},GOOGLE_GENAI_USE_VERTEXAI=True" \
--set-env-vars="GITHUB_REPO_URL=${GITHUB_REPO_URL}" \
--set-secrets="SLACK_WEBHOOK_URL=slack-webhook:latest" \
--set-env-vars="HARNESS_EVENTS_TOPIC=${HARNESS_EVENTS_TOPIC}" \
--set-env-vars="CIAGENT_URL=${CIAGENT_URL}" \
--set-secrets="GITHUB_TOKEN=github-token:latest" \
--no-allow-unauthenticated \
--min-instances=1 \
--no-cpu-throttling \
--timeout=300
F. 关联 Eventarc 触发器
创建一个 Pub/Sub 主题以接收 Cloud Run 错误日志:
gcloud pubsub topics create cloud-run-errors
创建一个 Cloud Logging 接收器,用于过滤 dinoquest 服务的错误日志并将其路由到相应主题:
export SERVICE_NAME=dinoquest
FILTER="resource.type=\"cloud_run_revision\" resource.labels.service_name=\"$SERVICE_NAME\" severity=ERROR NOT logName=~\"cloudaudit\" NOT httpRequest.requestUrl=~\"/_ah/health\""
gcloud logging sinks create cloud-run-errors-sink \
pubsub.googleapis.com/projects/${PROJECT_ID}/topics/cloud-run-errors \
--log-filter="$FILTER"
注意:运行上述命令时,gcloud 会输出一条 Info 消息,内容为:“请记得向 serviceAccount:service-... 授予主题的 Pub/Sub Publisher 角色。”下一步将介绍如何实现这一点。
向接收器的写入者身份(警告中提及的服务账号)授予发布权限:
SINK_SA=$(gcloud logging sinks describe cloud-run-errors-sink \
--format='value(writerIdentity)')
gcloud pubsub topics add-iam-policy-binding cloud-run-errors \
--member="${SINK_SA}" --role="roles/pubsub.publisher"
验证该功能是否处于有效状态:
gcloud eventarc triggers describe remediation-trigger --location=$CLOUD_RUN_REGION
向 Eventarc 授予调用补救代理的权限:
gcloud run services add-iam-policy-binding remediation-agent \
--region=$CLOUD_RUN_REGION \
--member="serviceAccount:${SA}" \
--role="roles/run.invoker"
创建 Eventarc 触发器:
gcloud eventarc triggers create remediation-trigger \
--location=$CLOUD_RUN_REGION \
--destination-run-service=remediation-agent \
--destination-run-region=$CLOUD_RUN_REGION \
--event-filters="type=google.cloud.pubsub.topic.v1.messagePublished" \
--transport-topic=projects/${PROJECT_ID}/topics/cloud-run-errors \
--service-account=${SA}
现在,补救措施已完全自动化。除了扩展基础架构之外,DinoAgent 还会对应用代码进行深入的根本原因分析,应用语义补丁,并使用代理对代理 (A2A) 通信将修复移交给 CI 代理进行验证和部署。您可以在 reme-agent 代码库中探索实现细节。
8. 设置 CI 代理
代理角色:CI 流水线。无需再费力处理复杂的 YAML 文件和手动构建脚本。此代理管理 PR 的操作管道。它会读取您的代码更改,了解上下文,确定必要的测试范围,并通过 Cloud Build 构建您的 Docker 映像,确保每个提交在到达主分支之前都经过“代理批准”。
ci-agent 是一个自主 CI 流水线代理,以 Cloud Run 服务的形式部署。它会向 Cloud Build 提交 Docker 构建,轮询完成情况,验证 Artifact Registry 中的映像,并向 GitHub 报告。

为何要为 CI 流水线使用代理?与传统的静态脚本不同,智能体 CI 流水线提供:
- 认知范围分类:根据代码更改的语义影响,智能确定所需的测试深度,在类型检查、单元测试或完整集成套件之间切换。
- 自主 PR 管理:代理可以自动创建 PR、发布详细的更改摘要,甚至无需人工干预即可管理密钥扫描和安全审核。
- 实时故障诊断:当 build 失败时,代理不仅会显示日志,还会分析堆栈轨迹、确定可能的原因,并将人类可读的诊断信息直接发布到 PR。
A. 克隆 CIAgent 代码库
cd ~
git clone https://github.com/gca-americas/dinoquest-ci-agent.git
cd dinoquest-ci-agent
B. 创建 CIAgent 服务账号
gcloud iam service-accounts create ci-agent \
--display-name="CIAgent CI Pipeline"
export SA="ci-agent@${PROJECT_ID}.iam.gserviceaccount.com"
# Grant necessary roles to the service account
for ROLE in \
roles/cloudbuild.builds.editor \
roles/cloudbuild.builds.builder \
roles/artifactregistry.reader \
roles/artifactregistry.writer \
roles/aiplatform.user \
roles/secretmanager.secretAccessor \
roles/pubsub.publisher \
roles/developerconnect.admin; do
gcloud projects add-iam-policy-binding $PROJECT_ID \
--member="serviceAccount:${SA}" --role="$ROLE" \
--condition=None
done
# ci-agent needs to act as itself when running build steps
gcloud iam service-accounts add-iam-policy-binding $SA \
--member="serviceAccount:${SA}" \
--role="roles/iam.serviceAccountUser"
C. 将 GitHub 代码库连接到 Cloud Build
CIAgent 通过 repoSource 提交 build,这要求 GitHub 代码库已连接到 Cloud Build Developer Connect。
- 前往 GCP Console → Cloud Build → 代码库
- 点击关联代码库
- 选择 GitHub(Cloud Build GitHub 应用)
- 授权并选择您的
YOUR_GITHUB_USERNAME/dinoquest代码库 - 点击连接,并在系统提示创建触发器时点击跳过。
- 记下您的连接名称(默认名称通常是您的 GitHub 用户名或类似名称)。
D. 向 CIAgent 授予对 Secret 的访问权限
我们将重复使用之前为 DinoAgent 创建的 Secret:
gcloud secrets add-iam-policy-binding github-token \
--member="serviceAccount:${SA}" \
--role="roles/secretmanager.secretAccessor"
gcloud secrets add-iam-policy-binding slack-webhook-ci \
--member="serviceAccount:${SA}" \
--role="roles/secretmanager.secretAccessor"
E. 构建 CIAgent 并将其部署到 Cloud Run
# Set up required variables for orchestration
PROJECT_NUMBER=$(gcloud projects describe $PROJECT_ID --format="value(projectNumber)")
export CDAGENT_URL=https://cd-agent-${PROJECT_NUMBER}.${CLOUD_RUN_REGION}.run.app
export CI_AGENT_URL=ci-agent-${PROJECT_NUMBER}.${CLOUD_RUN_REGION}.run.app
export GITHUB_OWNER="YOUR_GITHUB_USERNAME"
export CLOUD_BUILD_CONNECTION="YOUR_CONNECTION_NAME" # Update this if your connection name is different
export CLOUD_BUILD_REPO="YOUR_GITHUB_USERNAME-dinoquest" # Update this if your connection name is different
export SLACK_TOKEN=YOUR_SLACK_WEBHOOK
echo -n "https://hooks.slack.com/services/$SLACK_TOKEN" | \
gcloud secrets create slack-webhook-ci --data-file=-
export SA="ci-agent@${PROJECT_ID}.iam.gserviceaccount.com"
AGENT_IMAGE="$CLOUD_RUN_REGION-docker.pkg.dev/${PROJECT_ID}/dinoquest/ci-agent:latest"
gcloud builds submit --tag $AGENT_IMAGE .
gcloud run deploy ci-agent \
--image=$AGENT_IMAGE \
--region=$CLOUD_RUN_REGION \
--service-account=$SA \
--memory=1Gi \
--timeout=600 \
--allow-unauthenticated \
--min-instances=1 \
--set-env-vars="GOOGLE_CLOUD_PROJECT=${PROJECT_ID},GOOGLE_GENAI_USE_VERTEXAI=True" \
--set-env-vars="HARNESS_EVENTS_TOPIC=${HARNESS_EVENTS_TOPIC}" \
--set-env-vars="HOST=${CI_AGENT_URL},PROTOCOL=https" \
--set-secrets="SLACK_WEBHOOK_URL=slack-webhook-ci:latest" \
--set-env-vars="GITHUB_OWNER=${GITHUB_OWNER},GITHUB_REPO=dinoquest" \
--set-env-vars="CLOUD_BUILD_CONNECTION=${CLOUD_BUILD_CONNECTION},CLOUD_BUILD_REPO=${CLOUD_BUILD_REPO},CLOUD_BUILD_REGION=${CLOUD_RUN_REGION}" \
--set-env-vars="CDAGENT_URL=${CDAGENT_URL}" \
--set-secrets="GITHUB_TOKEN=github-token:latest" \
--min-instances=1
F. Slack 斜杠命令设置
- 前往 api.slack.com/apps → 点击创建新应用 → 点击从头开始
- 为其命名为
CIAgent,选择工作区 → 创建应用 - 依次选择功能 → 斜杠命令 → 创建新命令
- 命令:
/runci - 请求网址:上述 CIAgent Cloud Run 网址,附加了
/slack(例如https://ci-agent-xxx-.us-central1.run.app/slack) - 简短说明:
Trigger CI - 保存
- 在设置 → 安装应用下,点击安装到 Workspace 并点击“允许”。
CI 代理充当“大脑”,位于 Cloud Build 和 Artifact Registry 等强大的 Google Cloud 服务之上。构建经过验证后,会通过 A2A 调用 CD 代理来触发最终部署阶段,从而确保在构建周期和发布周期之间实现无缝切换。
9. 设置部署
代理角色:CD 代理。部署不应是盲目信任。此代理会为您管理风险。它会评估部署安全性、管理 Canary 流量拆分,并监控实时指标,以决定是升级版本还是回滚版本。它是自主代理群中的最终把关者。
cd-agent 是一个自主 Canary 部署代理,以 Cloud Run 服务的形式部署。它从 ci-agent 接收 A2A 部署请求,计算风险得分,设置经过风险校准的 Canary 版百分比,监控指标,并自动升级或回滚。它还会从使用 Firestore 的过往部署中学习。

直接连接代理会创建一个“认知流水线”,其中每次移交都是对意图和上下文的丰富转移。与传统的 Webhook 不同,A2A 通信可实现以下功能:
- 智能上下文共享:智能体传递完整的会话记忆、PR 差异和风险评分,确保下一个智能体在开始工作之前了解完整的“原因”。
- 认知握手:代理可以协商门控。例如,在 Canary 发布期间,CD 代理可以向 CI 代理请求特定的烟雾测试,以实时验证修复。
- 协同修复:如果部署失败,CD 代理可以主动将失败的指标通知给修复代理,从而在人工介入之前启动自主的根本原因分析。
- 资源协商:智能体可以协商基础架构需求。例如,如果 CI 代理检测到大规模重构,可以要求修复代理提供更多 build 容量;或者,CD 代理可以在重大版本发布之前建议扩缩生产集群。
- 多智能体共识:对于高风险变更,多个智能体(例如安全智能体和 CD 智能体)可以通过 A2A 执行“联合签核”,确保代码不仅可以构建和部署,还可以在投入生产之前遵守安全政策。
A. 克隆 CDAgent 代码库
cd ~
git clone https://github.com/gca-americas/dinoquest-cd-agent.git
cd dinoquest-cd-agent
B. 创建 Firestore 数据库
CDAgent 将其部署内存模式存储在 Firestore 中:
gcloud firestore databases create \
--region=$CLOUD_RUN_REGION \
--project=$PROJECT_ID
(如果您已在此项目中创建数据库,可以跳过此步骤。)
C. 创建 CDAgent 服务账号
gcloud iam service-accounts create cd-agent \
--display-name="CDAgent Canary Deployer"
export SA="cd-agent@${PROJECT_ID}.iam.gserviceaccount.com"
# Grant necessary roles
for ROLE in \
roles/run.developer \
roles/iam.serviceAccountUser \
roles/artifactregistry.reader \
roles/artifactregistry.writer \
roles/monitoring.viewer \
roles/datastore.user \
roles/aiplatform.user \
roles/run.admin \
roles/pubsub.publisher; do
gcloud projects add-iam-policy-binding $PROJECT_ID \
--member="serviceAccount:${SA}" --role="$ROLE" \
--condition=None
done
D. 授予 CDAgent 对密文的访问权限
我们将沿用之前的 GitHub 令牌和 Slack 网络钩子,并授予 Cloud Run 计算服务账号对 Gemini API 密钥的访问权限,以便部署的应用可以使用该密钥:
gcloud secrets add-iam-policy-binding github-token \
--member="serviceAccount:${SA}" \
--role="roles/secretmanager.secretAccessor"
gcloud secrets add-iam-policy-binding slack-webhook-cd \
--member="serviceAccount:${SA}" \
--role="roles/secretmanager.secretAccessor"
# Grant the compute service account access to Gemini API key
PROJECT_NUMBER=$(gcloud projects describe $PROJECT_ID --format="value(projectNumber)")
gcloud secrets add-iam-policy-binding gemini-api-key \
--project=$PROJECT_ID \
--member="serviceAccount:${PROJECT_NUMBER}-compute@developer.gserviceaccount.com" \
--role="roles/secretmanager.secretAccessor"
E. 构建 CDAgent 并将其部署到 Cloud Run
export GITHUB_OWNER="YOUR_GITHUB_USERNAME"
export CD_AGENT_URL=cd-agent-${PROJECT_NUMBER}.${CLOUD_RUN_REGION}.run.app
export SLACK_TOKEN=YOUR_SLACK_WEBHOOK
echo -n "https://hooks.slack.com/services/$SLACK_TOKEN" | \
gcloud secrets create slack-webhook-cd --data-file=-
AGENT_IMAGE="$CLOUD_RUN_REGION-docker.pkg.dev/${PROJECT_ID}/dinoquest/cd-agent:latest"
gcloud builds submit --tag $AGENT_IMAGE .
export SA="cd-agent@${PROJECT_ID}.iam.gserviceaccount.com"
gcloud run deploy cd-agent \
--image=$AGENT_IMAGE \
--region=$CLOUD_RUN_REGION \
--service-account=$SA \
--memory=1Gi \
--set-env-vars="GOOGLE_CLOUD_PROJECT=${PROJECT_ID}" \
--set-env-vars="GOOGLE_GENAI_USE_VERTEXAI=True" \
--set-env-vars="HOST=${CD_AGENT_URL},PROTOCOL=https" \
--set-env-vars="CD_TARGET_SERVICE=dinoquest" \
--set-env-vars="HARNESS_EVENTS_TOPIC=${HARNESS_EVENTS_TOPIC}" \
--set-env-vars="GITHUB_OWNER=${GITHUB_OWNER}" \
--set-env-vars="GITHUB_REPO=dinoquest" \
--set-env-vars="DEMO_MODE=true" \
--set-env-vars="LEADERBOARD_ENABLED=true" \
--set-secrets="SLACK_WEBHOOK_URL=slack-webhook-cd:latest" \
--set-secrets="GITHUB_TOKEN=github-token:latest" \
--allow-unauthenticated \
--min-instances=1 \
--no-cpu-throttling \
--timeout=300
注意: --min-instances=1 --no-cpu-throttling 会保持实例处于暖状态,以便快速响应 Slack 和 A2A 命令。
CD 代理是生产环境的最终门卫。它会评估每次部署的风险,执行经过风险校准的金丝雀测试,并监控实时指标。如果检测到任何异常,系统会自动启动回滚。
10. 扩大规模:利用智能体群解锁 2 级
A. 测试完整群组
- 触发错误:在浏览器中打开已部署的 DinoQuest 网址(在上一步中获得)。
- 前往排行榜:点击排行榜按钮。当前的排行榜实现故意效率低下,它会尝试将大量数据加载到内存中,从而触发内存不足 (OOM) 错误。
- 等待代理:大约 60 秒后,
remediation-agent将通过 Eventarc 接收到错误事件,并开始诊断。 - 检查 Slack:您应该会在 Slack 频道中看到如下所示的通知:DinoAgent 补救摘要
- 服务:dinoquest
- 失败的修订版本:dinoquest-xxxx-xxxx
- 证据:“内存限制为 128 MiB,但已使用 13x MiB。”
- 此修订版本存在的问题:
/api/leaderboard端点 xxxxx 的效率低下,导致出现内存不足 (OOM) 错误。 - 采取的操作:将服务
dinoquest的内存从 xMi 增加到 yGi。创建了新修订版本。 - 根本原因 PR:https://github.com/YOUR_USERNAME/DinoQuest/pull/x
- 验证修正效果:
- GitHub:检查您的代码库中是否有新分支和拉取请求。代理已修补应用代码,以修复潜在的内存泄漏问题。
- Cloud Run:在 GCP Console 中,您会看到
dinoquest服务的新修订版本,其中包含更新后的内存分配。 - 排行榜:再次尝试排行榜,现在应该可以成功加载了,这得益于内存增加和最终的代码修复。
B. 进化:实现第 2 级游戏逻辑
您将添加一项重要的新功能:第 2 级(小行星摧毁者)。这样,得分高的恐龙就可以进入新的游戏模式。
- 返回到您的 dinoquest 代码库:
cd ~/dinoquest - 创建并切换到新分支:
git checkout -b level_2 - 应用第 2 级补丁:运行提供的脚本,以使用第 2 级资源、组件和游戏逻辑修补本地代码库:
bash level_2_backup/levelup.sh - 提交并推送更改:
git add . git commit -m "feat: add Level 2" git push origin level_2
我们将使用您之前配置的 Slack 斜杠命令,而不是使用 curl 手动触发代理。这就是您在实际应用场景中与自主 CI 流水线互动的方式。
- 打开 Slack,然后前往安装了
CIAgent应用的任意频道。 - 输入以下命令,触发 CI build:
/runci run ci on branch level_2 - 监控进度:
- Slack:代理会确认您的命令,并在构建过程中发布更新。
- Dino Theater:观看智能体在对更改进行分类、提交 Cloud Build 作业以及与 CD 智能体通信时的“想法气泡”。
- GitHub:检查您的
level_2PR;您会看到代理发布提交状态和完整的 CI 报告注释。
- 观看流程:
- 查看 Dino Theater,了解 CI 代理如何思考、对更改进行分类以及运行流水线。
- 检查您的 GitHub PR,查看 CI 代理是否发布了提交状态和最终报告。
11. 总结
您已在 Google Cloud 上构建了完整的智能体 DevOps 堆栈:
组件 | 作用 |
DinoQuest (Cloud Run | 由 Gemini 提供支持的游戏 - React 前端 + FastAPI 后端 |
Firebase Auth + Firestore | 用户身份验证和恐龙个人资料存储 |
补救代理 (Cloud Run + Eventarc) | 可自动修复 OOM 错误和代码 bug 的 SRE 代理 |
log-router-bq-report | 可将日志流式传输到 BigQuery 并生成数据洞见的 Data Agent |
CIAgent (Cloud Run) | 通过 A2A 调用 CD 的 CI 代理,用于确定测试范围和构建映像 |
CDAgent (Cloud Run) | CD 代理,可运行具有自动回滚功能的风险评分 Canary 部署 |
所有代理行为都以 Markdown playbook 的形式存在于 skills/ 目录中 - 通过修改技能(而非代码)来更改行为。技能在 Antigravity with Gemini 上运行,通过告知代理要运行的技能来调用。