Agent Gateway 入站流量到 Agent Runtime(使用 Model Armor)

1. 简介

此 Codelab 探讨了针对托管在 Agent Runtime 上的 AI 智能体的 Agent Gateway 入口治理。

以入站(客户端到智能体)模式运行的 Agent Gateway 支持管理客户端(人工最终用户、桌面智能体、编码 IDE、对等智能体等)与Agent Runtime托管智能体之间的通信。此模式用于保护代理免受客户端发送的入站提示注入攻击或有害内容的侵害。所有入站流量都使用授权扩展程序和 Model Armor 进行处理,以保护所有代理互动的网络入口点。

构建内容

  • 处于入站(客户端到代理)模式的 Agent Gateway
  • Model Armor 授权扩展程序
  • 具有代理身份的 Agent Runtime ADK 代理
  • 代理使用 MCP 查询 Cloud Storage 文件数据
  • 用于过滤 LLM 提示和回答的 Model Armor 模板
  • 用于对数据进行去标识化处理的 Sensitive Data Protection 模板

figure1

图 1. Codelab 架构

学习内容

  • 如何部署 Agent Gateway 以过滤发送给智能体的入站流量
  • 如何配置 Model Armor 授权扩展程序和委托
  • 如何创建和部署自定义 Model Armor 模板
  • 如何创建和部署自定义 Sensitive Data Protection 模板
  • 如何测试和验证 LLM 筛查政策

所需条件

  • 启用了结算功能的 Google Cloud 项目
  • 用于预配网络服务、BigQuery 数据集和 Agent Platform 资源的 IAM 权限
  • 已安装 Google Cloud CLI(gcloud 组件)的 POSIX 兼容 shell(bash 或 zsh)
  • 命令行工具:git、curl、jq (JSON 处理器)、Python 3 和 uv (Python 软件包管理器)

2. 概念

流量方向和网关角色

Agent Gateway 可充当可感知代理的网络代理,但其运行角色会根据流量方向而变化:

  • 代理到任意位置(出站)模式:充当出站代理。当代理调用外部数据库工具、第三方 MCP 服务器或 API 时,出站流量网关会管理服务发现、路由、双向 TLS (mTLS)、OAuth 凭据的动态注入以及对端点的访问权限控制。
  • 客户端到代理(入站)模式:用作前端安全网关。其主要目标是保护代理执行时的运行时环境,方法是在传入的自然语言提示到达代理代码或 AI 模型之前对其进行拦截和清理。

Agent Runtime 的入站流量路径

以托管在 Agent Runtime 上的代理为目标的客户端请求会发送到 aiplatform.googleapis.com API 端点。

POST https://${REGION}-aiplatform.googleapis.com/v1/projects/${PROJECT_ID}/locations/${REGION}/reasoningEngines/${RE_ENGINE_ID}:query

到 API 端点的此入站通信流表示客户端到代理的入站路径。

为了保护此 Google 管理的入站流量路径,Agent Gateway 在 API 服务基础架构层直接与 Google Front End (GFE) 集成。将受管代理部署到 Agent Runtime 时,Google 会在网络边缘将入站网关授权政策原生绑定到传入的客户端请求。

figure2

图 2. 通过 Agent Gateway 对 Agent Runtime 进行入站流量治理

由于检查发生在前端层级,在请求进入 Agent Runtime 之前,因此该架构不会引入额外的网络开销或内部跃点延迟。扩缩由前端基础架构自动处理,因此您无需管理内部 IP 范围、负载平衡器或自定义 DNS 路由。

使用 Model Armor 进行内嵌威胁清理

评估调用方凭据并强制执行 IAM 访问权限控制 (roles/aiplatform.user) 由 aiplatform API 托管层原生处理。入口网关本身不执行身份授权,而是使用配置了 CONTENT_AUTHZ 配置文件的授权扩展程序专注于内容安全。网关充当内嵌政策执行点,在自然语言提示到达 AI 代理推理循环或底层 LLM 之前拦截这些提示。

当传入的用户提示到达前端服务时,网关会向区域性 Model Armor 授权扩展服务发起 ext_proc(外部处理)callout,该服务会将调用流式传输到 Model Armor 数据层。Model Armor 充当自然语言防火墙,根据有效模板评估文本,以扫描安全风险:

  • 间接提示注入和越狱尝试
  • 恶意网址、有害语言或不安全的内容
  • 个人身份信息 (PII) 和敏感数据泄露

如果模板包含 Sensitive Data Protection (SDP) 过滤器,Model Armor 会额外向 Cloud SDP 服务发出 gRPC 调用。Cloud SDP 使用指定模板检查载荷,执行任何请求的去标识化或隐去操作,然后将清理后的结果返回到链中,以便安全地转发。

如果检测到政策违规或未经过编辑的敏感数据匹配,网关会在有效负载进入运行时之前在边缘对其进行屏蔽或编辑。因此,正在运行的 AI 智能体应用始终受到保护,绝不会处理恶意载荷或未经过编辑的载荷。

概念部分到此结束…接下来是设置部分。

3. 设置

所需 IAM 角色

您必须拥有以下角色,才能在此 Codelab 中创建资源:

类别

所需 IAM 角色 (ID)

说明

API 管理

roles/serviceusage.serviceUsageAdmin

启用 Google Cloud API 服务

网络和网关

roles/networkservices.admin

配置 Agent Gateway

Service Extensions

roles/serviceextensions.admin

配置路由扩展程序

网络安全

roles/networksecurity.admin

部署授权政策

敏感数据保护

roles/dlp.admin

管理 SDP 检查和去标识化模板

Model Armor

roles/modelarmor.admin

创建和管理安全模板

Agent Platform

roles/aiplatform.admin

部署 Agent Runtime 工作负载

Cloud Storage

roles/storage.admin

管理部署和客户数据桶

IAM 管理

roles/resourcemanager.projectIamAdmin

为代理身份绑定项目级权限

日志和审核

roles/logging.viewer

检查轨迹和审核日志

或者,您也可以使用广泛的基本角色(例如 roles/admin)或旧版角色 roles/owner。

访问您的项目

本 Codelab 使用单个 Google Cloud 项目。配置步骤使用 gcloud CLI 和 Linux shell 命令。

首先,访问 Google Cloud 云项目命令行:

设置项目 ID

gcloud config set project SET_YOUR_PROJECT_ID_HERE

验证会话

# login to gcloud cli
gcloud auth login
# login for gcloud api
gcloud auth application-default login

设置 shell 环境变量

# set custom var for slug (eg, "foo") and region preference
export SLUG="foo"
export REGION="us-central1"
echo ${SLUG}
echo ${REGION}
# create project vars (automatic)
export PROJ_ID=$(gcloud config list --format="value(core.project)")
export PROJ_NO=$(gcloud projects describe ${PROJ_ID} --format="value(projectNumber)")
export ORG_ID=$(gcloud projects get-ancestors ${PROJ_ID} --format="value(id)" | tail -n 1)
export USER_IDENTITY=$(gcloud config get-value account)
echo ${PROJ_ID}
echo ${PROJ_NO}
echo ${ORG_ID}
echo ${USER_IDENTITY}
# create resource vars (automatic)
export AGW_NAME="agw-${SLUG}-${REGION}-cta"
export AGW_URI="projects/${PROJ_ID}/locations/${REGION}/agentGateways/${AGW_NAME}"
export RE_AGENT_NAME="agent-crm"
export RE_AGENT_ID_SET="principalSet://agents.global.org-${ORG_ID}.system.id.goog/attribute.platformContainer/aiplatform/projects/${PROJ_NO}"
export STAGING_BUCKET="agent-staging-${PROJ_NO}"
export DATA_BUCKET="customer-data-${PROJ_NO}"
export MCP_URL="https://storage.mtls.googleapis.com/storage/mcp"
echo ${AGW_NAME}
echo ${AGW_URI}
echo ${RE_AGENT_NAME}
echo ${RE_AGENT_ID_SET}
echo ${STAGING_BUCKET}
echo ${DATA_BUCKET}
echo ${MCP_URL}
# create local dir for config files
mkdir -p cfg

如果您运行的是自行管理的 Google Cloud SDK 安装(即在 Cloud Shell 外部),请将组件更新到最新版本。

# update gcloud cli
gcloud components update

启用 API 服务

# enable google apis (agent platform bundle, part 1)
gcloud services enable \
  agentregistry.googleapis.com \
  aiplatform.googleapis.com \
  apphub.googleapis.com \
  apptopology.googleapis.com \
  cloudapiregistry.googleapis.com \
  cloudtrace.googleapis.com \
  compute.googleapis.com \
  dataform.googleapis.com \
  iam.googleapis.com \
  iamconnectors.googleapis.com \
  iap.googleapis.com \
  logging.googleapis.com \
  modelarmor.googleapis.com \
  monitoring.googleapis.com \
  networksecurity.googleapis.com \
  networkservices.googleapis.com \
  notebooks.googleapis.com \
  observability.googleapis.com
# enable google apis (agent platform bundle, part 2)
gcloud services enable \
  securitycenter.googleapis.com \
  saasservicemgmt.googleapis.com \
  storage.googleapis.com \
  telemetry.googleapis.com \
  texttospeech.googleapis.com
# enable google apis (all the rest)
gcloud services enable \
  dlp.googleapis.com

设置部分到此结束…接下来请参阅网关部分。

4. 网关

部署以客户端到代理 (CLIENT_TO_AGENT) 模式运行的 Google 管理的 Agent Gateway。与需要代理注册表关联才能路由出站调用的出站网关不同,入站网关直接在前端层绑定,充当针对 Agent Runtime 的入站提示的内嵌强制执行点。

虽然出口政策通常在网关层以 DRY_RUN 模式启动,但入口内容治理 (CONTENT_AUTHZ) 会直接以强制执行模式部署。精细的仅审核日志记录或主动屏蔽是在各个 Model Armor 模板中上游控制的。

创建网关

# create agent gateway config file
cat > cfg/${AGW_NAME}.yaml <<EOF
name: ${AGW_NAME}
protocols:
  - MCP
googleManaged:
  governedAccessPath: CLIENT_TO_AGENT
EOF
# import agent gateway config file (create gateway)
gcloud network-services agent-gateways import ${AGW_NAME} \
  --source="cfg/${AGW_NAME}.yaml" \
  --location=${REGION}

验证网关

# list agent gateways (in region)
gcloud network-services agent-gateways list --location=${REGION}
# show agent gateway details (verify deployment state)
gcloud network-services agent-gateways describe ${AGW_NAME} --location=${REGION}

网关部分到此结束…接下来是 Model Armor 部分。

5. Model Armor

SDP 模板

创建 Sensitive Data Protection (SDP) 检查和去标识化模板,以便在 Model Armor 响应模板中使用。此配置会标记美国社会保障号 (SSN),以便进行遮盖。

创建检查模板

检查模板用于识别数据中的敏感信息 (US_SOCIAL_SECURITY_NUMBER)。

# create inspect template
curl -fsS -X POST "https://dlp.googleapis.com/v2/projects/${PROJ_ID}/locations/${REGION}/inspectTemplates" \
  -H "Authorization: Bearer $(gcloud auth application-default print-access-token)" \
  -H "Content-Type: application/json" -H "x-goog-user-project: ${PROJ_ID}" \
  -d @- << EOF
{
  "templateId": "agw-ssn-inspect-template",
  "inspectTemplate": {
    "displayName": "ssn inspect template",
    "inspectConfig": {
      "infoTypes": [
        { "name": "US_SOCIAL_SECURITY_NUMBER" }
      ],
      "minLikelihood": "POSSIBLE"
    }
  }
}
EOF

创建去标识化模板

去标识化模板用于指定要对检查模板发现的社会保障号码应用的转换。在这种情况下,转换是将社会保障号替换为 infoType。

# create de-identify template
curl -fsS -X POST "https://dlp.googleapis.com/v2/projects/${PROJ_ID}/locations/${REGION}/deidentifyTemplates" \
  -H "Authorization: Bearer $(gcloud auth application-default print-access-token)" \
  -H "Content-Type: application/json" -H "x-goog-user-project: ${PROJ_ID}" \
  -d @- << EOF
{
  "templateId": "agw-ssn-redaction-template",
  "deidentifyTemplate": {
    "displayName": "SSN Redaction Template",
    "deidentifyConfig": {
      "infoTypeTransformations": {
        "transformations": [{
          "primitiveTransformation": { "replaceWithInfoTypeConfig": {} }
        }]
      }
    }
  }
}
EOF

验证 SDP 模板

# get (describe) inspect template
curl -fsS -X GET "https://dlp.googleapis.com/v2/projects/${PROJ_ID}/locations/${REGION}/inspectTemplates" \
  -H "Authorization: Bearer $(gcloud auth application-default print-access-token)" \
  -H "Content-Type: application/json" -H "x-goog-user-project: ${PROJ_ID}" | jq
# get (describe) de-identify template
curl -fsS -X GET "https://dlp.googleapis.com/v2/projects/${PROJ_ID}/locations/${REGION}/deidentifyTemplates" \
  -H "Authorization: Bearer $(gcloud auth application-default print-access-token)" \
  -H "Content-Type: application/json" -H "x-goog-user-project: ${PROJ_ID}" | jq

Model Armor 模板

Model Armor API 的默认端点是全球端点 (modelarmor.googleapis.com)。不过,模板和评估引擎的 Model Armor 资源会本地化到特定地理区域。Model Armor 的 Google Cloud 区域端点代理 (REP) 或区域 API 端点为 https://modelarmor.${LOCATION}.rep.googleapis.com/。

默认情况下,运行 gcloud model-armor ... 时,CLI 会尝试向标准全局端点 (https://modelarmor.googleapis.com/) 发送 API 请求。API 端点替换用于将所有 SDK/CLI HTTP 请求直接重定向到区域 rep.googleapis.com API 层,以便在其中实际创建、存储和查询这些受位置限制的模板。

设置 API 替换

# set api endpoint override per location
gcloud config set api_endpoint_overrides/modelarmor "https://modelarmor.${REGION}.rep.googleapis.com/"

验证 API 替换

# view api overrides on active gcloud config
gcloud config list api_endpoint_overrides/

创建请求过滤条件模板

创建请求过滤模板,以屏蔽仇恨言论、骚扰、露骨色情内容和 URI 注入攻击。系统将启用日志记录功能,以捕获有关政策执行的详细事件信息。此外,还可配置自定义错误代码和消息,以便在请求被阻止时显示。

# create model armor template (request)
gcloud beta model-armor templates create ${AGW_NAME}-modar-req-template \
  --project=${PROJ_ID} \
  --location=${REGION} \
  --rai-settings-filters='[
    { "filterType": "HATE_SPEECH", "confidenceLevel": "MEDIUM_AND_ABOVE" },
    { "filterType": "HARASSMENT", "confidenceLevel": "MEDIUM_AND_ABOVE" },
    { "filterType": "SEXUALLY_EXPLICIT", "confidenceLevel": "MEDIUM_AND_ABOVE" }
  ]' \
  --pi-and-jailbreak-filter-settings-enforcement=enabled \
  --pi-and-jailbreak-filter-settings-confidence-level=medium-and-above \
  --template-metadata-enforcement-type=INSPECT_AND_BLOCK \
  --malicious-uri-filter-settings-enforcement=enabled \
  --template-metadata-custom-llm-response-safety-error-code=798 \
  --template-metadata-custom-llm-response-safety-error-message="ahoy! model response blocked by content filter :(" \
  --template-metadata-custom-prompt-safety-error-code=799 \
  --template-metadata-custom-prompt-safety-error-message="ahoy! the request was blocked by ye content filter... so rephrase the prompt and try again!" \
  --template-metadata-ignore-partial-invocation-failures \
  --template-metadata-log-operations \
  --template-metadata-log-sanitize-operations

创建回答过滤模板

创建响应过滤模板,以屏蔽与请求过滤模板相同的内容。在响应阶段配置 DLP,以对从代理返回到客户端的消息中的社会保障号码进行去标识化处理。

# create model armor template (response)
gcloud beta model-armor templates create ${AGW_NAME}-modar-resp-template \
  --project=${PROJ_ID} \
  --location=${REGION} \
  --rai-settings-filters='[
      { "filterType": "HATE_SPEECH", "confidenceLevel": "MEDIUM_AND_ABOVE" },
      { "filterType": "HARASSMENT", "confidenceLevel": "MEDIUM_AND_ABOVE" },
      { "filterType": "SEXUALLY_EXPLICIT", "confidenceLevel": "MEDIUM_AND_ABOVE" }
  ]' \
  --malicious-uri-filter-settings-enforcement=enabled \
  --advanced-config-inspect-template=projects/${PROJ_ID}/locations/${REGION}/inspectTemplates/agw-ssn-inspect-template \
  --advanced-config-deidentify-template=projects/${PROJ_ID}/locations/${REGION}/deidentifyTemplates/agw-ssn-redaction-template \
  --template-metadata-enforcement-type=INSPECT_AND_BLOCK \
  --template-metadata-custom-llm-response-safety-error-code=798 \
  --template-metadata-custom-llm-response-safety-error-message="ahoy! model response blocked by content filter :(" \
  --template-metadata-custom-prompt-safety-error-code=799 \
  --template-metadata-custom-prompt-safety-error-message="ahoy! the request was blocked by ye content filter... so rephrase the prompt and try again!" \
  --template-metadata-ignore-partial-invocation-failures \
  --template-metadata-log-operations \
  --template-metadata-log-sanitize-operations

验证 Model Armor 模板

# list model armor templates
gcloud model-armor templates list --location=${REGION}
# show request filter template details
gcloud model-armor templates describe ${AGW_NAME}-modar-req-template --location=${REGION}
# show response filter template details
gcloud model-armor templates describe ${AGW_NAME}-modar-resp-template --location=${REGION}

IAM 权限

Model Armor 会进行 API 调用来调用 Sensitive Data Protection (SDP) 服务。向 Model Armor 服务身份授予 IAM 权限,以使用 SDP 检查和去标识化模板。

绑定 Sensitive Data Protection 的 IAM 政策

# grant dlp (sdp) user role to the model armor service identity
gcloud projects add-iam-policy-binding ${PROJ_ID} \
  --member="serviceAccount:service-${PROJ_NO}@gcp-sa-modelarmor.iam.gserviceaccount.com" \
  --role="roles/dlp.user"

验证 IAM 权限

# show iam policy for all dlp (sdp) roles on project
gcloud projects get-iam-policy ${PROJ_ID} \
  --flatten="bindings[].members" \
  --filter="bindings.role:roles/dlp" \
  --format="table(bindings.role:label=ROLE, bindings.members:label=PRINCIPAL_IDENTITY)"

Model Armor 部分到此结束…接下来是授权部分。

6. 授权

IAM 权限

如需使用 Model Armor 检查内嵌流量,Service Extensions (DEP) 服务代理需要明确的 IAM 绑定(即使在同一项目内的资源之间也是如此):

  • roles/modelarmor.calloutUser 和 roles/serviceusage.serviceUsageConsumer:在网关项目上授予,以允许内嵌检查调用。
  • roles/modelarmor.user:在模板项目上授予,以允许访问和评估 Model Armor 模板。

绑定 Model Armor 的 IAM 政策

# grant model armor callout user role to dep (service extension) service agent
gcloud projects add-iam-policy-binding ${PROJ_ID} \
  --member="serviceAccount:service-${PROJ_NO}@gcp-sa-dep.iam.gserviceaccount.com" \
  --role="roles/modelarmor.calloutUser"

# grant service usage consumer role to dep (service extension) service agent
gcloud projects add-iam-policy-binding ${PROJ_ID} \
  --member="serviceAccount:service-${PROJ_NO}@gcp-sa-dep.iam.gserviceaccount.com" \
  --role="roles/serviceusage.serviceUsageConsumer"

# grant model armor user role to dep (service extension) service agent
gcloud projects add-iam-policy-binding ${PROJ_ID} \
  --member="serviceAccount:service-${PROJ_NO}@gcp-sa-dep.iam.gserviceaccount.com" \
  --role="roles/modelarmor.user"

验证 IAM 权限

# show iam policy on project for dep (service extension) service agent
gcloud projects get-iam-policy ${PROJ_ID} \
  --flatten="bindings[].members" \
  --filter="bindings.members:serviceAccount:service-${PROJ_NO}@gcp-sa-dep.iam.gserviceaccount.com" \
  --format="table(bindings.members:label=PRINCIPAL_IDENTITY, bindings.role:label=ROLE)"

授权扩展程序

Agent Gateway 的授权扩展程序配置定义了将应用于传入和传出载荷流量的集成设置。该配置定义了外部处理服务 (service),该服务引用了区域性 Model Armor API,并使用 model_armor_settings 元数据字段链接到特定的请求和响应模板。

创建授权扩展服务

# create authz extension config file (enforced mode)
cat > cfg/${AGW_NAME}-svc-ext-authz-modar.yaml <<EOF
name: ${AGW_NAME}-svc-ext-authz-modar
service: modelarmor.${REGION}.rep.googleapis.com
metadata:
  model_armor_settings: '[
    {
      "request_template_id": "projects/${PROJ_ID}/locations/${REGION}/templates/${AGW_NAME}-modar-req-template",
      "response_template_id": "projects/${PROJ_ID}/locations/${REGION}/templates/${AGW_NAME}-modar-resp-template"
    }
  ]'
failOpen: true
timeout: 5s
EOF

导入授权扩展程序

# import authz extension file
gcloud service-extensions authz-extensions import ${AGW_NAME}-svc-ext-authz-modar \
  --source=cfg/${AGW_NAME}-svc-ext-authz-modar.yaml \
  --location=${REGION}

验证授权扩展程序

# list authz extensions
gcloud service-extensions authz-extensions list --location=${REGION}
# show authz extension details
gcloud service-extensions authz-extensions describe ${AGW_NAME}-svc-ext-authz-modar \
  --location=${REGION}

授权政策

授权政策使用政策配置文件来确定执行的评估类型。虽然基于请求的配置文件 (REQUEST_AUTHZ) 会评估 HTTP 标头,但此配置使用基于内容的授权配置文件 (CONTENT_AUTHZ) 将 Model Armor 扩展程序绑定到网关,以进行深度载荷检查。

创建授权政策

# create authz policy config file (attach dry-run authz extension)
cat > cfg/${AGW_NAME}-authz-policy-modar.yaml <<EOF
name: ${AGW_NAME}-authz-policy-modar
target:
  resources:
    - "projects/${PROJ_ID}/locations/${REGION}/agentGateways/${AGW_NAME}"
policyProfile: CONTENT_AUTHZ
action: CUSTOM
customProvider:
  authzExtension:
    resources:
      - "projects/${PROJ_ID}/locations/${REGION}/authzExtensions/${AGW_NAME}-svc-ext-authz-modar"
EOF

导入授权政策

# import authz policy config file (enable authz policy)
gcloud beta network-security authz-policies import ${AGW_NAME}-authz-policy-modar \
  --source=cfg/${AGW_NAME}-authz-policy-modar.yaml \
  --location=${REGION}

验证授权政策

# list authz policies
gcloud beta network-security authz-policies list --location=${REGION}
# show authz policy details
gcloud beta network-security authz-policies describe ${AGW_NAME}-authz-policy-modar \
  --location=${REGION}

授权部分到此结束…接下来进入代码库部分。

7. 代码库

此 Codelab 中使用的代理代码和文件数据保存在远程 Google Cloud GitHub 代码库中。以下步骤会将代码库克隆到本地,将必要的文件复制到当前工作目录结构,然后清理临时文件。

提取远程制品

# clone remote repository to temp local dir
git clone https://github.com/GoogleCloudPlatform/cloud-networking-solutions.git ./temp_agw_cuj_arun_ingress_modar
# copy agent runtime and endpoint definitions to working project dir
cp -r temp_agw_cuj_arun_ingress_modar/codelabs/agw-cuj-arun-ingress-modar/agent-crm ./agent-crm
# remove temporary directory
rm -rf temp_agw_cuj_arun_ingress_modar

Agent Runtime 使用用于暂存的存储分区来上传、构建和部署打包的代理应用代码及其依赖项制品。

创建用于暂存的存储分区

# create storage bucket
gcloud storage buckets create gs://${STAGING_BUCKET} --location=${REGION}

验证存储分区

# list storage buckets
gcloud storage buckets list --format="value(storage_url)"

代码库部分到此结束…接下来是 GCS 客户数据部分。

客户数据

创建一个 Cloud Storage 存储分区来存储客户数据。代理将使用标准 Google Cloud 客户端库直接读取,并调用 Cloud Storage MCP 端点。

为客户数据创建存储分区

# create storage bucket
gcloud storage buckets create gs://${DATA_BUCKET} --location=${REGION}

验证存储分区

# list storage buckets
gcloud storage buckets list --format="value(storage_url)"

上传客户数据

# copy local data to bucket
gcloud storage cp -r ./agent-crm/data/* gs://${DATA_BUCKET}/

验证客户数据

# list bucket objects
gcloud storage ls gs://${DATA_BUCKET}/ --long

GCS 客户数据部分到此结束…接下来是 ADK 代理部分。

8. ADK 智能体

部署到 Agent Runtime 的 agent-crm ADK 代理在部署脚本中配置了以下设置,以便与 Agent Platform 集成:

  • "identity_type": types.IdentityType.AGENT_IDENTITY 为代理预配唯一的 基于 SPIFFE 的主账号身份
  • "client_to_agent_config": {"agent_gateway": "${AGW_URI}"},用于将代理的所有入站流量定向到 Agent Gateway 政策评估和执行路径

系统还会将 Cloud Storage MCP 服务器的 mTLS MCP 服务器网址和数据存储分区名称传递给智能体,以便通过安全连接调用 GCS MCP 工具。

部署代理

# deploy agent
uv --directory agent-crm run python3 deploy_agent.py \
  --project=${PROJ_ID} \
  --region=${REGION} \
  --src-dir=./agent \
  --staging-bucket=${STAGING_BUCKET} \
  --display-name="${RE_AGENT_NAME}" \
  --description="agent for customer data" \
  --mcp-server-url="${MCP_URL}" \
  --data-bucket=${DATA_BUCKET} \
  --enable-telemetry \
  --enable-agent-identity \
  --agent-gateway-ingress=${AGW_URI} \
  --allow-token-sharing

验证部署

提取部署生命体征

# fetch agent runtime (reasoning engine) resource id
export RE_ENGINE_ID=$(curl -s -X GET "https://${REGION}-aiplatform.googleapis.com/v1/projects/${PROJ_ID}/locations/${REGION}/reasoningEngines" \
  -H "Authorization: Bearer $(gcloud auth application-default print-access-token)" \
  | jq -r --arg name "${RE_AGENT_NAME}" '.reasoningEngines[] | select(.displayName==$name) | .name | split("/") | last')
echo ${RE_ENGINE_ID}
# fetch agent runtime (reasoning engine) agent identity
export RE_AGENT_IDENTITY=$(gcloud agent-registry agents list \
  --project=${PROJ_ID} --location=${REGION} --filter="displayName=${RE_AGENT_NAME}" \
  --format="value(attributes.'agentregistry.googleapis.com/system/RuntimeIdentity'.principal)")
echo ${RE_AGENT_IDENTITY}

验证网关配置

# show agent runtime config details (gateway config)
curl -s -X GET "https://${REGION}-aiplatform.googleapis.com/v1/projects/${PROJ_ID}/locations/${REGION}/reasoningEngines/${RE_ENGINE_ID}" \
  -H "Authorization: Bearer $(gcloud auth application-default print-access-token)" \
  | jq '{displayName: .displayName, name: .name, effectiveIdentity: .spec.effectiveIdentity, agentGatewayConfig: .spec.deploymentSpec.agentGatewayConfig}'

IAM 权限

为代理身份绑定 IAM 政策

# grant mcp tool user role to agent set (all agent runtime agents in project)
gcloud projects add-iam-policy-binding ${PROJ_ID} \
  --member="${RE_AGENT_ID_SET}" \
  --role="roles/mcp.toolUser"
# grant storage object viewer role to agent identity
gcloud projects add-iam-policy-binding ${PROJ_ID} \
  --member="${RE_AGENT_IDENTITY}" \
  --role="roles/storage.objectViewer"

# grant aiplatform user role to agent identity
gcloud projects add-iam-policy-binding ${PROJ_ID} \
  --member="${RE_AGENT_IDENTITY}" \
  --role="roles/aiplatform.user"

# grant cloudtrace agent role to agent identity
gcloud projects add-iam-policy-binding ${PROJ_ID} \
  --member="${RE_AGENT_IDENTITY}" \
  --role="roles/cloudtrace.agent"

# grant cloud monitoring metric writer role to agent identity
gcloud projects add-iam-policy-binding ${PROJ_ID} \
  --member="${RE_AGENT_IDENTITY}" \
  --role="roles/monitoring.metricWriter"
# grant cloud logging log writer role to agent identity
gcloud projects add-iam-policy-binding ${PROJ_ID} \
  --member="${RE_AGENT_IDENTITY}" \
  --role="roles/logging.logWriter"

# grant telemetry writer role to agent identity
gcloud projects add-iam-policy-binding ${PROJ_ID} \
  --member="${RE_AGENT_IDENTITY}" \
  --role="roles/telemetry.writer"

# grant service usage consumer role to agent identity
gcloud projects add-iam-policy-binding ${PROJ_ID} \
  --member="${RE_AGENT_IDENTITY}" \
  --role="roles/serviceusage.serviceUsageConsumer"

# grant browser role to agent identity
gcloud projects add-iam-policy-binding ${PROJ_ID} \
  --member="${RE_AGENT_IDENTITY}" \
  --role="roles/browser"

验证 IAM 权限

# show agent identity roles on project
gcloud projects get-iam-policy ${PROJ_ID} \
  --flatten="bindings[].members" \
  --filter="bindings.members:${RE_AGENT_IDENTITY}" \
  --format="table(bindings.members.sub('^.*locations/', 'principal://agents.[...]/locations/'):label=PRINCIPAL_IDENTITY, bindings.role:label=ROLE)"
# show agent set roles on project
gcloud projects get-iam-policy ${PROJ_ID} \
  --flatten="bindings[].members" \
  --filter="bindings.members:${RE_AGENT_ID_SET}" \
  --format="table(bindings.members.sub('^.*platformContainer/', 'principalSet://agents.[...]/'):label=PRINCIPAL_IDENTITY, bindings.role:label=ROLE)"

ADK 代理部分到此结束…接下来进入测试部分。

9. 测试

通过 CLI 提交查询

测试安全提示

# post query to agent streamQuery
curl --no-buffer -s -X POST "https://${REGION}-aiplatform.googleapis.com/v1beta1/projects/${PROJ_ID}/locations/${REGION}/reasoningEngines/${RE_ENGINE_ID}:streamQuery" \
  -H "Authorization: Bearer $(gcloud auth application-default print-access-token)" \
  -H "Content-Type: application/json" -H "X-Goog-User-Project: ${PROJ_ID}" \
  -d @- <<EOF | jq -r --unbuffered 'if type == "array" then .[] else . end | select(.content.parts != null) | .content.parts[].text // empty'
{
  "input": {
    "message": "what are the names of our west customers?",
    "user_id": "test-user"
  }
}
EOF

应看到类似如下的响应… “我们西部的客户是:Bob Johnson 和 Alice Brown。”

测试隐去触发器

# post query to agent streamQuery
curl --no-buffer -s -X POST "https://${REGION}-aiplatform.googleapis.com/v1beta1/projects/${PROJ_ID}/locations/${REGION}/reasoningEngines/${RE_ENGINE_ID}:streamQuery" \
  -H "Authorization: Bearer $(gcloud auth application-default print-access-token)" \
  -H "Content-Type: application/json" -H "X-Goog-User-Project: ${PROJ_ID}" \
  -d @- <<EOF | jq -r --unbuffered 'if type == "array" then .[] else . end | select(.content.parts != null) | .content.parts[].text // empty'
{
  "input": {
    "message": "what are ssn's for bob johnson and alice brown?",
    "user_id": "test-user"
  }
}
EOF

测试其他安全提示

# post query to agent streamQuery
curl --no-buffer -s -X POST "https://${REGION}-aiplatform.googleapis.com/v1/projects/${PROJ_ID}/locations/${REGION}/reasoningEngines/${RE_ENGINE_ID}:streamQuery" \
  -H "Authorization: Bearer $(gcloud auth application-default print-access-token)" \
  -H "Content-Type: application/json" -H "X-Goog-User-Project: ${PROJ_ID}" \
  -d @- <<EOF | jq -r --unbuffered 'if type == "array" then .[] else . end | select(.content.parts != null) | .content.parts[].text // empty'
{
  "input": {
    "message": "what are bob johnson's and alice brown's email addresses?",
    "user_id": "test-user"
  }
}
EOF

审核日志

查看轨迹日志

启用遥测后,Agent Runtime 会以流式传输方式发送结构化事件,这些事件表示用户查询、工具参数、执行流程和模型选择输出。

# show agent runtime (reasoning engine) telemetry and trace logs
gcloud logging read \
  "logName:\"projects/${PROJ_ID}/logs/aiplatform.googleapis.com%2Freasoning_engine_stdout\" AND labels.managed-by=\"reasoning-engine\"" \
  --project=${PROJ_ID} \
  --limit=15 \
  --format="table(
    timestamp.date(format=\"%I:%M:%S %p\", tz=LOCAL):label=TIME,
    trace.basename().sub('^(.{8}).*$', '\\1'):label=TRACE_ID,
    labels.\"event.name\".scope(-1):label=EVENT,
    jsonPayload.content.role:label=ROLE,
    jsonPayload.content.parts[0].text:label=TEXT_CONTENT,
    jsonPayload.content.parts[0].function_call.name:label=TOOL_CALL
  )"

TRACE_ID 将用户查询、中间工具调用和模型决策组合成一个时间轴:

TIME         TRACE_ID  EVENT                  ROLE   TEXT_CONTENT                                     TOOL_CALL
HH:MM:SS PM  3070a1fd  gen_ai.choice          model  Bob Johnson's SSN is 219-45-7895.
                                                     Alice Brown's SSN is 219-45-7896.
HH:MM:SS PM  3070a1fd  gen_ai.user.message    user
HH:MM:SS PM  3070a1fd  gen_ai.user.message    model                                                   read_customer_file
HH:MM:SS PM  3070a1fd  gen_ai.user.message    user
HH:MM:SS PM  3070a1fd  gen_ai.user.message    model                                                   read_customer_file
HH:MM:SS PM  3070a1fd  gen_ai.user.message    user
HH:MM:SS PM  3070a1fd  gen_ai.user.message    model                                                   list_customer_files
HH:MM:SS PM  3070a1fd  gen_ai.user.message    user   what are ssn's for bob johnson and alice brown?
HH:MM:SS PM  3070a1fd  gen_ai.system.message
HH:MM:SS PM  3070a1fd  gen_ai.choice          model                                                   read_customer_file

查看 Model Armor 清理日志

这些日志显示了 Model Armor 在流量流经 Agent Gateway 时执行的实时双向内嵌威胁检测和清理操作。

# show model armor logs
gcloud logging read \
  "logName:\"projects/${PROJ_ID}/logs/modelarmor.googleapis.com%2Fsanitize_operations\"" \
  --project=${PROJ_ID} \
  --limit=50 \
  --format="table(
    timestamp.date(format=\"%I:%M:%S %p\", tz=LOCAL):label=TIME,
    jsonPayload.sanitizationResult.sanitizationVerdict:label=VERDICT,
    jsonPayload.sanitizationInput.byteItem.byteData.decode(base64).decode(utf-8).sub('\n', ' \\\\\\\\n ').trailoff(123):label=INPUT_DATA
  )"

请注意,日志条目中显示了经过清理并被阻止的请求。

TIME         VERDICT                                 INPUT_DATA
HH:MM:SS PM  MODEL_ARMOR_SANITIZATION_VERDICT_ALLOW  Bob Johnson's email address is bob.j@example.com. \n Alice Brown's email address is alice.b...
HH:MM:SS PM  MODEL_ARMOR_SANITIZATION_VERDICT_ALLOW  what are bob johnson's and alice brown's email addresses?
HH:MM:SS PM  MODEL_ARMOR_SANITIZATION_VERDICT_BLOCK  6��
HH:MM:SS PM  MODEL_ARMOR_SANITIZATION_VERDICT_ALLOW  what are ssn's for bob johnson and alice brown?
HH:MM:SS PM  MODEL_ARMOR_SANITIZATION_VERDICT_ALLOW  Our west customers are: Bob Johnson and Alice Brown.
HH:MM:SS PM  MODEL_ARMOR_SANITIZATION_VERDICT_ALLOW  what are the names of our west customers?

“测试”部分到此结束…接下来是清理部分。

10. 清理

# remove agent iam bindings
gcloud -q projects remove-iam-policy-binding ${PROJ_ID} --member="${RE_AGENT_IDENTITY}" --role="roles/storage.objectViewer"
gcloud -q projects remove-iam-policy-binding ${PROJ_ID} --member="${RE_AGENT_IDENTITY}" --role="roles/aiplatform.user"
gcloud -q projects remove-iam-policy-binding ${PROJ_ID} --member="${RE_AGENT_IDENTITY}" --role="roles/cloudtrace.agent"
gcloud -q projects remove-iam-policy-binding ${PROJ_ID} --member="${RE_AGENT_IDENTITY}" --role="roles/monitoring.metricWriter"

# next
# remove more agent and agent set iam bindings
gcloud -q projects remove-iam-policy-binding ${PROJ_ID} --member="${RE_AGENT_IDENTITY}" --role="roles/logging.logWriter"
gcloud -q projects remove-iam-policy-binding ${PROJ_ID} --member="${RE_AGENT_IDENTITY}" --role="roles/telemetry.writer"
gcloud -q projects remove-iam-policy-binding ${PROJ_ID} --member="${RE_AGENT_IDENTITY}" --role="roles/serviceusage.serviceUsageConsumer"
gcloud -q projects remove-iam-policy-binding ${PROJ_ID} --member="${RE_AGENT_IDENTITY}" --role="roles/browser"

# next
# remove rest of iam bindings
gcloud -q projects remove-iam-policy-binding ${PROJ_ID} --member="${RE_AGENT_ID_SET}" --role="roles/mcp.toolUser"
gcloud -q projects remove-iam-policy-binding ${PROJ_ID} --member="serviceAccount:service-${PROJ_NO}@gcp-sa-modelarmor.iam.gserviceaccount.com" --role="roles/dlp.user"

# next
# delete agent runtime (reasoning engine) agent
curl -s -X DELETE "https://${REGION}-aiplatform.googleapis.com/v1/projects/${PROJ_ID}/locations/${REGION}/reasoningEngines/${RE_ENGINE_ID}?force=true" \
  -H "Authorization: Bearer $(gcloud auth application-default print-access-token)" \
  -H "Content-Type: application/json"

# next
# delete storage
gcloud -q storage rm --recursive gs://${STAGING_BUCKET}
gcloud -q storage rm --recursive gs://${DATA_BUCKET}

# next
# delete authz resources
gcloud -q beta network-security authz-policies delete ${AGW_NAME}-authz-policy-modar --location=${REGION}

gcloud -q beta service-extensions authz-extensions delete ${AGW_NAME}-svc-ext-authz-modar --location=${REGION} --async

# next
# remove dep (service extensions) service agent iam bindings
gcloud -q projects remove-iam-policy-binding ${PROJ_ID} \
  --member="serviceAccount:service-${PROJ_NO}@gcp-sa-dep.iam.gserviceaccount.com" \
  --role="roles/modelarmor.calloutUser"

gcloud -q projects remove-iam-policy-binding ${PROJ_ID} \
  --member="serviceAccount:service-${PROJ_NO}@gcp-sa-dep.iam.gserviceaccount.com" \
  --role="roles/serviceusage.serviceUsageConsumer"

gcloud -q projects remove-iam-policy-binding ${PROJ_ID} \
  --member="serviceAccount:service-${PROJ_NO}@gcp-sa-dep.iam.gserviceaccount.com" \
  --role="roles/modelarmor.user"

# next
# delete model armor templates
gcloud -q model-armor templates delete ${AGW_NAME}-modar-resp-template --location=${REGION}
gcloud -q model-armor templates delete ${AGW_NAME}-modar-req-template --location=${REGION}

# unset model armor api endpoint override
gcloud config unset api_endpoint_overrides/modelarmor

# next
# delete sdp (dlp) templates
curl -fsS -X DELETE "https://dlp.googleapis.com/v2/projects/${PROJ_ID}/locations/${REGION}/deidentifyTemplates/agw-ssn-redaction-template" \
  -H "Authorization: Bearer $(gcloud auth application-default print-access-token)" \
  -H "x-goog-user-project: ${PROJ_ID}"

curl -fsS -X DELETE "https://dlp.googleapis.com/v2/projects/${PROJ_ID}/locations/${REGION}/inspectTemplates/agw-ssn-inspect-template" \
  -H "Authorization: Bearer $(gcloud auth application-default print-access-token)" \
  -H "x-goog-user-project: ${PROJ_ID}"

# next
# delete agent gateway ingress
gcloud -q network-services agent-gateways delete ${AGW_NAME} --location=${REGION} --async

# end

清理部分到此结束…接下来请前往总结部分。

11. 总结

恭喜!您已成功部署 Agent Gateway 并控制 AI 代理的入站流量!

cosmopup

Cosmopup 认为 Codelab 非常棒!

接下来会发生什么?

欢迎使用此反馈表单提出任何意见、问题或更正。

谢谢!