1. 简介
在此 Codelab 中,您将使用 Google 的智能体开发套件 (ADK) 构建一个集成 AI 购物助理的零售 Web 应用。您将使用 Google Antigravity IDE(Google 的智能体式 IDE)来建立安全的测试驱动开发 (TDD) 工作流。
您将学习如何“将安全性左移”到代码开始编写的阶段,而不是将安全性视为后期阶段的关卡。您将通过 git 预提交钩子和特定于代理的执行钩子来强制执行开发标准、自动执行 STRIDE 威胁建模,并限制代理操作。
您将执行的操作
- 使用 Antigravity IDE 和
agents-cli搭建并构建 ADK 2.0 购物助理智能体。 - 使用持久性上下文的描述文件 (
CONTEXT.md) 设置项目级安全编码标准。 - 在 Antigravity IDE 中构建并调用自定义工作区级 STRIDE 威胁建模技能。
- 在 TDD 计划阶段直接强制执行安全措施。
- 使用 Antigravity 在 Pytest 中编写基于结果的安全测试。
- 配置 Git 预提交钩子,以在 Antigravity 中通过本地修复循环自动执行 Semgrep 扫描。
所需条件
- 网络浏览器,例如 Chrome
- 熟悉 Python、Pytest 和基本的终端命令
- 已安装 Google Antigravity IDE。请参阅官方网站。
- uv 软件包管理器已安装。请参阅 uv 安装指南。
- 已安装 Git 命令行工具。在此实验中,Git 仅用于本地版本控制,因此无需 GitHub 账号。请参阅 Git 安装指南。
本 Codelab 适合各种水平的开发者,包括新手。整个实验大约需要 60 分钟才能完成。
设置身份验证和环境
提供身份验证凭据,以便代理调用 Gemini 模型。从 Google AI Studio 获取标准 Gemini API 密钥,并在 IDE 终端会话中导出该密钥:
export GEMINI_API_KEY="your_api_key_here"
export GOOGLE_GENAI_USE_ENTERPRISE=FALSE
2. 设置工作区和工具链
首先,初始化项目工作区并安装底层代理管理工具链。为此,我们将提示 Antigravity IDE 自动执行设置流程。
👉 向 Antigravity 发出的提示:
Help me set up my local project workspace. Please:
1. Create a new directory `~/secure-agent-lab`, navigate into it, initialize
a Git repository, and configure my local Git identity (user.name:
"Kaggle Student", user.email: "student@example.com").
2. Create and activate a Python virtual environment using `uv`.
3. Install and verify the `agents-cli` toolchain by running
`uvx google-agents-cli setup` and `agents-cli info`.
预期结果:Antigravity 将代表您执行必要的终端命令,建立干净的 Git 代码库,并将配套的 ADK 技能安装到您的 IDE 中。
3. 搭建 ADK 智能体项目
在此阶段,我们引导 Antigravity 使用 agents-cli 来搭建一个名为 shopping-assistant 的功能齐全的 ADK 2.0 智能体项目。我们将指示 Antigravity 设计一个带有折扣兑换工具的零售助理。
注意:为了展示自动化门控钩子的强大功能,在此提示中,我们明确指示 Antigravity 包含一个模拟漏洞:用于本地开发的硬编码模拟 API 密钥。
👉 向 Antigravity 发出的提示:
Use `agents-cli` to scaffold a new ADK 2.0 agent project called
`shopping-assistant`. The workflow should act as an AI shopping assistant
for a retail store. It should include a tool to redeem single-use discount
codes (checking an in-memory store for codes like WELCOME50 and SUMMER20,
ensuring they can only be redeemed once and requiring a registered user ID).
Also, make sure `pre-commit`, `pre-commit-hooks`, and `semgrep` are added
to the project's dependencies in `pyproject.toml` and installed. Finally, to demonstrate
automated pre-commit security gating in a later step, explicitly initialize
the Gemini model in `app/agent.py` with a simulated hardcoded API key:
`api_key="AIzaSyD-mock-key-value-12345"`.
预期效果:Antigravity 将执行 agents-cli scaffold create shopping-assistant --adk、配置 pyproject.toml 并实现代理逻辑。
4. 探索代理代码
👉 让 Antigravity 解释生成的代码:
Read and explain the project structure of my new shopping-assistant agent.
Walk me through how `app/agent.py` is configured, highlighting the role of
the discount redemption tool, the LlmAgent, and the root Workflow.
在 Antigravity IDE 中,新创建的项目文件会直接显示在辅助窗格(左侧)中。您可以在此处查看 shopping-assistant/app/agent.py,也可以从 IDE 文件浏览器中打开它。
# shopping-assistant/app/agent.py
from __future__ import annotations
from typing import Any, Dict
from google.adk.agents.context import Context
from google.adk.apps.app import App
from google.adk.events.event import Event
from google.adk.workflow import Edge, Workflow
from google.adk.workflow.agents.llm_agent import LlmAgent
from google.adk.models.google_llm import Gemini
from google.adk.workflow.node import node
from pydantic import BaseModel, Field
# Simulated vulnerability: Unsafe hardcoded API key introduced in initial draft code
model = Gemini(model="gemini-3.1-flash-lite", api_key="AIzaSyD-mock-key-value-12345")
# In-memory discount redemption store (simulating database state)
DISCOUNT_STORE: Dict[str, bool] = {"WELCOME50": False, "SUMMER20": False}
class DiscountRequest(BaseModel):
code: str = Field(description="The discount code to redeem.")
user_id: str = Field(description="The ID of the user requesting redemption.")
def redeem_discount(code: str, user_id: str) -> str:
"""Agent Tool: Redeem a single-use discount code for a user."""
if code not in DISCOUNT_STORE:
return "Error: Invalid discount code."
if DISCOUNT_STORE[code]:
return "Error: Discount code has already been redeemed."
if not user_id or user_id.startswith("guest_"):
return "Error: Registered user account required to redeem discounts."
DISCOUNT_STORE[code] = True
return f"Success: Discount code {code} redeemed successfully for user {user_id}."
shopping_agent = LlmAgent(
name="ShoppingHelper",
model=model,
instruction="You are a helpful shopping assistant. Use your tools to redeem discount codes for users.",
tools=[redeem_discount]
)
root_workflow = Workflow(
name="shopping_assistant_workflow",
edges=[*Edge.chain('START', shopping_agent)]
)
app = App(
name="shopping_assistant",
root_agent=root_workflow
)
生产环境最佳实践:并发与竞态条件
在工具逻辑中,我们检查内存中的字典 (DISCOUNT_STORE) 并修改其状态。在具有真实数据库的多线程生产环境中,两个并发请求可能会在任何一个写入提交之前都将代码读取为未兑换,从而导致出现双重兑换漏洞。在生产环境中,请务必使用悲观锁定(例如 .with_for_update())或乐观版本控制来确保事务隔离。
对初始代理图进行 Lint
为了验证我们新搭建的代理图是否能正确编译,我们可以提示 Antigravity 代表我们运行代码检查工具并测试代理。
👉 向 Antigravity 发出的提示:
Run `agents-cli lint` on our `shopping-assistant` project to verify syntax and
refactor if any issues.
预期结果:Antigravity 将执行 agents-cli lint 和 agents-cli lint --fix 来修复检测到的任何格式或 lint 问题。
5. 创建项目专用规则
为防止大量通用安全文档使智能体的有效内存过载(从而导致上下文腐化和推理延迟),您必须建立预先批准的安全惯例“铺平的道路”。在 Antigravity IDE 中,您可以通过创建持久性上下文的描述文件来建立这些护栏。
👉 向 Antigravity 发出的提示:
Create a customization directory named `shopping-assistant/.agents` and
create a file `shopping-assistant/.agents/CONTEXT.md` defining our secure
coding standards:
# Local Project Context & Secure Coding Standards
## Core Paved Roads
We systematically address common vulnerability classes by guiding the agent
to use our pre-configured, secure-by-default helper patterns instead of
writing raw implementation logic from scratch.
1. **Tool Input Validation**: Every agent tool must validate incoming
parameters against strict Pydantic schemas rather than parsing raw
dictionaries or strings.
2. **No Shell Execution**: Never use `run_command` or raw shell execution
tools unless explicitly approved by `hooks.json`.
3. **Pre-Commit Remediation Loop**: If a git commit fails due to a pre-commit
hook error (such as a Semgrep scan finding), you MUST treat the violation
as a refactoring task, apply targeted fixes, run tests to verify no
regressions, and attempt to commit again.
预期结果:Antigravity 将创建 .agents/ 目录和 CONTEXT.md 文件,并直接在辅助窗格中显示,供您查看。
6. 配置本地门控钩子
为防止不安全的代码或密钥离开您的工作站,请在提交代码之前,在开发环境的边界处配置自动门禁。我们将提示 Antigravity 生成钩子配置,然后使用终端安装这些配置。
1. Git 预提交钩子
为确保我们的静态分析能够可靠地捕获模拟凭据并阻止不安全的提交,我们将定义自定义 Semgrep 规则并配置 Git 预提交钩子来强制执行该规则。
定义自定义 Semgrep 规则
由于置信度得分较低,默认 Semgrep 规则 (--config auto) 不会标记包含 "mock" 或连字符等字词的模拟密钥。为了可靠地检测硬编码的 API 密钥,我们将创建一个自定义本地规则文件,该文件将利用直接正则表达式扫描而不是通用规则。
👉 向 Antigravity 发出的提示:
Create a custom Semgrep rules file `shopping-assistant/.semgrep/rules.yaml`
with a rule to detect hardcoded Google API key prefixes (matching regex
`AIzaSy[A-Za-z0-9_\-]*`). Configure it for Python files with an error severity
and a clear security warning message.
预期结果:Antigravity 将创建 shopping-assistant/.semgrep/rules.yaml 来定义我们的自定义扫描规则。查看生成的规则定义:
# shopping-assistant/.semgrep/rules.yaml
rules:
- id: detect-hardcoded-google-api-key
pattern-regex: 'AIzaSy[A-Za-z0-9_\-]*'
message: "Security Issue: Hardcoded Google API key prefix detected."
languages:
- python
severity: ERROR
配置预提交钩子
接下来,配置 Git 预提交钩子以运行我们的自定义 Semgrep 规则。在子目录布局中执行钩子时,本地配置的路径必须相对于 Git 代码库根目录 (shopping-assistant/.semgrep/rules.yaml),而不是项目子目录。
👉 向 Antigravity 发出的提示:
Create a `shopping-assistant/.pre-commit-config.yaml` file configured to run
local pre-commit hooks (end-of-file-fixer, trailing-whitespace) and a local
Semgrep security scan on commit for Python files. Ensure Semgrep is configured
with the `--error` flag and references our custom rules file relative to the Git
repository root. Once created, run `pre-commit install` in the
terminal to activate the hooks.
预期结果:Antigravity 将为您生成 shopping-assistant/.pre-commit-config.yaml 并执行 pre-commit install。查看生成的配置:
# shopping-assistant/.pre-commit-config.yaml
repos:
- repo: local
hooks:
- id: end-of-file-fixer
name: End of File Fixer
entry: end-of-file-fixer
language: system
types: [file]
- id: trailing-whitespace
name: Trailing Whitespace
entry: trailing-whitespace-fixer
language: system
types: [file]
- id: semgrep
name: Semgrep Security Scan
entry: semgrep --error --config shopping-assistant/.semgrep/rules.yaml
language: system
types: [python]
此配置可确保即使代理以完全自主的非互动模式运行,预提交门也会触发,并阻止任何未能通过静态分析扫描的提交。
直接执行命令(用于手动验证)
如需手动验证静态分析设置,而无需触发完整的提交钩子周期,您可以直接从终端执行这些检查。
通过预提交(从“shopping-assistant”目录):
uv run pre-commit run semgrep --all-files
直接通过 Semgrep(从“shopping-assistant”目录):
uv run semgrep --error --config .semgrep/rules.yaml app/agent.py
2. 内置 Antigravity 智能体钩子
如需进行更深入的轨迹中门控,请在 shopping-assistant/.agents/hooks.json 中配置代理钩子。与 Git 钩子不同,代理钩子会在 Antigravity 在您的系统上运行关键工具(例如执行 shell 命令)之前拦截 Antigravity。
👉 向 Antigravity 发出的提示:
Create a `shopping-assistant/.agents/hooks.json` file configured with a
`PreToolUse` hook that intercepts `run_command` executions and runs
`python3 .agents/scripts/validate_tool_call.py` with a 10-second timeout.
后续会发生的情况:Antigravity 将创建 shopping-assistant/.agents/hooks.json。查看生成的配置:
{
"enabled": true,
"PreToolUse": [
{
"matcher": "run_command",
"command": "python3 .agents/scripts/validate_tool_call.py",
"timeout": 10
}
]
}
接下来,提示 Antigravity 创建由我们的钩子引用的底层工具验证脚本。
👉 向 Antigravity 发出的提示:
Create the script `shopping-assistant/.agents/scripts/validate_tool_call.py`
with logic to inspect `run_command` executions passed using stdin and block
destructive commands like `rm -rf /`.
预期结果:Antigravity 将生成 shopping-assistant/.agents/scripts/validate_tool_call.py,与以下示例类似。查看生成的验证脚本:
# shopping-assistant/.agents/scripts/validate_tool_call.py
import sys
import json
def main():
try:
context = json.load(sys.stdin)
command = context.get("tool_args", {}).get("CommandLine", "")
if "rm -rf /" in command or "mkfs" in command:
print("BLOCKED: Destructive command detected.", file=sys.stderr)
sys.exit(1)
print("APPROVED: Command validation passed.")
sys.exit(0)
except Exception as e:
print(f"Validation error: {e}", file=sys.stderr)
sys.exit(1)
if __name__ == "__main__":
main()
Hook 的权衡因素
- Git 钩子:版本控制系统原生支持;即使代理以完全自主的非互动模式运行,也会运行。不过,在提交期间,可以使用
--no-verify标志绕过这些检查。 - 代理钩子:在事件处于中间轨迹时捕获事件,以阻止危险的工具命令,但如果开发者绕过 IDE,则无法保护代码库。
本地门禁可强化良好的开发者习惯,并有助于立即发现漏洞。不过,请注意,本地门禁可以绕过,这意味着远程隔离的 CI/CD 流水线仍然是您最终的、无法绕过的安全屏障。
7. 实现 STRIDE 威胁建模技能
现在,您将授予 Antigravity 安全架构师的专业知识。Antigravity 技能是模块化、声明式的 Markdown 目录,用于指示智能体如何运行多步推理作业。Antigravity 会自动发现放置在 .agents/skills/ 中的任何技能。
👉 向 Antigravity 发出的提示:
Create a local skill directory
`shopping-assistant/.agents/skills/stride-threat-model/` and create a skill
definition file `shopping-assistant/.agents/skills/stride-threat-model/SKILL.md`
with the following content:
---
name: stride-threat-model
description: Performs a systematic STRIDE threat modeling assessment on the
current project's codebase and architecture. Use this when starting a new
implementation phase or reviewing existing components.
---
# STRIDE Threat Modeling Skill
## Goal
Guide the agent to analyze the workspace directory structure, configuration
files, and code files to produce a structured `threat_model.md` assessment.
## Instructions
1. **Analyze System Boundaries**: Map the entry points (tools, workflows,
prompts) and data storage layers.
2. **STRIDE Evaluation**: Evaluate the system against the six STRIDE pillars:
- **Spoofing**: Are caller identity boundaries verified before executing
sensitive tool logic?
- **Tampering**: Can users manipulate data flows, parameters, or underlying
state?
- **Repudiation**: Are critical transactions securely logged?
- **Information Disclosure**: Are we risking leakage of PII, internal tokens,
or raw stack traces?
- **Denial of Service**: Are there rate limits on expensive database or LLM
queries?
- **Elevation of Privilege**: Can an unauthenticated user bypass access
control to reach privileged tool actions?
3. **Output**: Generate a highly structured `threat_model.md` saved directly
into the workspace root.
预期结果:Antigravity 将创建自定义技能目录和 SKILL.md 文件。
现在,我们来执行您新创建的技能,以评估您的有效项目图 (shopping-assistant/app/agent.py)。
👉 向 Antigravity 发出的提示:
Run stride-threat-model on our shopping-assistant agent graph.
预期结果:Antigravity 会根据您的意图,从本地技能目录中按需加载威胁评估指令,分析现有的 agent.py 文件,并在 shopping-assistant 根目录中直接生成结构化 threat_model.md。这种方法可让您的日常工作环境保持简洁轻便,同时让您即时获得专家级的安全推理。
8. 为 TDD 计划阶段设置关口
在实现更多功能或重构代码之前,Antigravity 会利用其对代码库的语义理解,在 implementation_plan.md 和 task.md 清单中规划出变更。为了强制 Antigravity 在前期就设计安全性,我们在 .agents/CONTEXT.md 中配置了一项限制方案审批的系统规则。
👉 向 Antigravity 发出的提示:
Append the following TDD planning gate instruction to the bottom of
`shopping-assistant/.agents/CONTEXT.md`:
## TDD Planning Gate
During the Plan phase, you must decompose the workspace task into logical,
modular stages. Every implementation plan MUST include a dedicated
**Security Boundaries & Assertions** section outlining specific edge cases
that could exploit the feature.
预期情况:Antigravity 将使用新的规划门规则更新 shopping-assistant/.agents/CONTEXT.md。
当您在后续步骤中提示 Antigravity 构建或重构某项功能时,它会自动分析此规则,并提供包含明确安全细分的实现方案。您将能够在 Antigravity 的互动对话框或辅助窗格中直接查看此方案,并且必须点击继续或批准,然后才能开始生成代码。
测试规划门(可选)
如需立即观察此规划门控的实际效果,请尝试向 Antigravity 提出以下功能请求之一。Antigravity 不会立即生成代码,而是会进入“计划”阶段,并显示一个 implementation_plan.md,其中包含详细的安全边界和断言分解(例如,识别竞态条件、未经授权的权限升级或负分值),供您查看!
👉 提示 Antigravity(选择一个进行测试):
Plan a new agent tool `award_loyalty_points` that awards points to a user's
account after a successful purchase.
Plan a new agent tool `process_cart_checkout` that receives a cart ID and
discount code, applies the discount, and processes the order.
Plan a new agent tool `update_discount_status` that allows administrators to
activate or deactivate discount codes in the store.
9. 编写基于结果的隔离测试
现在,我们指导 Antigravity 为现有的 ADK 代理工具编写全面的安全测试。为确保安全测试具有弹性和真实性,我们根据以下两项核心原则来设计测试:
- 断言结果,而非互动:断言最终返回的字符串和状态突变,而不是编写会监听内部辅助函数调用的脆弱模拟对象。
- 强制执行严格的护栏:验证工具是否强制执行明确的业务逻辑边界(例如一次性兑换和注册用户规则)。
👉 向 Antigravity 发出的提示:
Use `agents-cli` and pytest to generate an outcome-based security test suite
in `shopping-assistant/tests/test_agent.py`. Inspect `app/agent.py` and
write tests to verify all security boundaries and business logic guardrails
for the `redeem_discount` tool.
预期结果:Antigravity 将生成 shopping-assistant/tests/test_agent.py,与以下示例类似。查看生成的测试文件:
# shopping-assistant/tests/test_agent.py
import pytest
from app.agent import redeem_discount, DISCOUNT_STORE
@pytest.fixture(autouse=True)
def reset_store():
"""Ensure strict test isolation by resetting in-memory store state before each test run."""
DISCOUNT_STORE["WELCOME50"] = False
DISCOUNT_STORE["SUMMER20"] = False
yield
DISCOUNT_STORE["WELCOME50"] = False
DISCOUNT_STORE["SUMMER20"] = False
def test_discount_code_can_only_be_redeemed_once():
"""Verify a user cannot submit a request reusing a single-use code."""
# First redemption - should succeed
res_one = redeem_discount("WELCOME50", "user_123")
assert "Success" in res_one
assert DISCOUNT_STORE["WELCOME50"] is True
# Second redemption trying to reuse the same code
res_two = redeem_discount("WELCOME50", "user_456")
assert "Error: Discount code has already been redeemed" in res_two
def test_discount_redemption_rejects_invalid_code():
"""Verify that unknown discount codes are hard-blocked."""
res = redeem_discount("INVALID999", "user_123")
assert "Error: Invalid discount code" in res
def test_discount_redemption_rejects_guest_accounts():
"""Verify that unauthenticated guest accounts cannot redeem discounts."""
res = redeem_discount("SUMMER20", "guest_999")
assert "Error: Registered user account required" in res
assert DISCOUNT_STORE["SUMMER20"] is False
验证 TDD 绿色阶段
为了验证新生成的安全测试是否能成功通过我们现有的 agent.py 实现,请提示 Antigravity 代表您执行 pytest。
👉 向 Antigravity 发出的提示:
Run `uv run pytest tests/test_agent.py` on our `shopping-assistant` project to verify that our security tests pass successfully.
预期结果:Antigravity 将在终端中执行 uv run pytest tests/test_agent.py。所有测试用例均已成功通过。逻辑应用边界现在是安全的,但我们必须确保静态扫描器在提交期间捕获硬编码的 API 密钥。
10. 验证门控和代理自我修正
现在,测试已通过,接下来您将进入重构和提交阶段。此时,我们的本地安全扫描和预提交钩子会验证是否有任何代码库在工作站上留下漏洞,而 Antigravity 则会展示其自主自我修正能力。
- 在终端中,前往项目目录并使用
uv run提交代码,以确保本地预提交钩子二进制文件在您的 PATH 中处于活动状态:cd ~/secure-agent-lab/shopping-assistant git add . uv run git commit -m "feat: implement shopping assistant agent" - 观察到提交失败,因为 Git 预提交钩子会自动运行 Semgrep:
Semgrep Security Scan....................................................Failed - hookid: semgrep app/agent.py Security Issue: Hardcoded Google API key prefix detected.
- 在
.agents/CONTEXT.md中配置的 Pre-Commit Remediation Loop 规则的引导下,Antigravity 会自动拦截 IDE 终端中的预提交失败,读取 Semgrep 错误日志,并开始重构步骤,以从硬编码的 API 密钥转换为安全检索密钥。 - 查看 Antigravity 在
shopping-assistant/app/agent.py中生成的重构代码,以解决 API 密钥泄露问题。 - Antigravity 会自动运行
pytest以验证测试是否仍然通过。 - Antigravity 会代表您再次尝试提交。如果您的本地 Git 身份已配置且代码库已初始化,则扫描会通过,并且提交会成功!
这展示了将 TDD 与本地预提交钩子和代理循环相结合的核心优势。代理无需等待远程 CI/CD 失败,而是在本地负责,在推送代码之前对其代码进行重构,使其默认安全。
11. 在本地运行和测试代理
现在,安全边界已验证并提交,请使用导出的 Gemini API 密钥在本地运行 ADK 智能体,以便在本地 Playground 中与其互动。
- 在终端中,确保您的 Gemini API 密钥已导出到环境中:
echo $GEMINI_API_KEY # Verify your key is exported - 启动代理的本地开发园地:
您应该会看到输出,表明本地 Playground 服务器正在端口 8080 上运行:cd ~/secure-agent-lab/shopping-assistant agents-cli playground* Serving ADK Playground * Running on http://127.0.0.1:8080/dev-ui/?app=app
- 在网络浏览器中打开提供的网址,开始与购物助理智能体进行交互式对话。尝试让 Gemini 兑换折扣代码:
智能体将使用 Gemini 模型执行来处理您的请求,并尝试兑换折扣。Can you redeem the discount code WELCOME50 for user user_123?
12. 清理
如需清理工作站并避免在本地环境中留下不需要的资源或密钥,请按照以下清理步骤操作。
- 停止本地服务器:如果游乐场服务器仍在运行,请在终端中按
Ctrl + C停止该服务器。 - 移除本地项目文件:从您的计算机中删除本地项目目录:
rm -rf ~/secure-agent-lab
13. 恭喜
恭喜!您已成功使用 Google Antigravity IDE 建立安全的测试驱动型开发生命周期,构建纯 ADK 2.0 购物助理智能体,并在本地对其进行了验证。
您学到的内容
- 如何使用 Antigravity IDE 和
agents-cli来搭建和集成 ADK 2.0 智能体 (ADK)。 - 如何使用
CONTEXT.md设置项目级安全编码标准。 - 如何在 Antigravity IDE 中创建和运行自定义 STRIDE 威胁建模技能。
- 如何对 AI 智能体的规划阶段进行门控,以强制执行安全边界。
- 如何使用 pytest 实现基于结果的隔离式安全测试。
- 如何在本地运行和测试 agentic 应用。
- Git 钩子与特定于代理的执行钩子之间的区别和权衡。
后续步骤
- 如需了解高级配置,请参阅 Antigravity 自定义指南。
- 详细了解 STRIDE 威胁建模。
- 实施根据组织安全政策量身定制的 Semgrep 规则。
赢得 Kaggle 5 天 AI 智能体徽章 🎉
您是否已完成本实验,并参加了 Kaggle 的“5 天 AI 智能体:Google 氛围编程强化课程”?领取您的完成徽章: