如何在部署到 Cloud Run 之前对写入 Google 表格的 ADK 代理进行本地测试

1. 简介

概览

在这篇博文中,您将使用 Google 智能体开发套件 (ADK) 和 FastAPI 构建一个 Gemini 智能体。在部署之前,您将配置应用默认凭据 (ADC),以便使用与 Cloud Run 服务身份相同的服务账号在本地进行测试。

如需与 Google 表格通信,您的应用需要具有适当范围的 OAuth 访问令牌才能访问相应电子表格。您将了解如何在本地运行以及在 Cloud Run 上使用 ADC 时获取此访问令牌:

术语说明:

您可能更熟悉“冒用身份”或“冒用相同权限”这两个术语。在 Google Cloud 中,经过身份验证的主账号可以通过模拟服务账号来访问服务账号可以访问的任何内容。只有具有适当权限的经过身份验证的主账号才能模拟服务账号。如需了解详情,请参阅 https://docs.cloud.google.com/iam/docs/service-account-overview#impersonation

学习内容

  • 如何为本地环境配置应用默认凭证 (ADC)
  • 如何创建可读取和写入 Google 表格的 ADK 智能体
  • 如何将代理部署到 Cloud Run

2. 设置和要求

前提条件

  • 您已登录 Cloud 控制台。
  • 您之前已部署过 Cloud Run 服务。例如,您可以按照部署 Cloud Run 服务中的步骤开始操作。

设置环境变量

您可以设置将在本 Codelab 中全程使用的环境变量。

复制共享链接后,您可以在 Google 表格的网址中找到表格 ID,例如 https://docs.google.com/spreadsheets/d/YOUR_SPREADSHEET_ID/edit?usp=sharing

PROJECT_ID=<YOUR-PROJECT-ID>
REGION=<YOUR_REGION>
SPREADSHEET_ID=<YOUR_SPREADSHEET_ID>
SA_NAME=sheet-agent-sa
SERVICE_ACCOUNT_EMAIL=$SA_NAME@$PROJECT_ID.iam.gserviceaccount.com

启用必需的 Google Cloud API

gcloud services enable \
  sheets.googleapis.com \
  aiplatform.googleapis.com \
  artifactregistry.googleapis.com \
  cloudbuild.googleapis.com \
  run.googleapis.com \
  logging.googleapis.com \
  --project=$PROJECT_ID

创建服务账号

首先,使用以下命令创建服务账号。

gcloud iam service-accounts create $SA_NAME \
    --description="Service account for spreadsheet agent codelab" \
    --display-name="Spreadsheet Agent Service Account" \
    --project=$PROJECT_ID

接下来,向服务账号授予 Vertex AI User 角色(这是使用 Vertex AI 上的 Gemini 模型所必需的)

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

授予您的 gcloud 身份模拟服务账号的权限(本地开发需要此权限)。您可以运行 gcloud auth list 查看您的 gcloud 身份

USER_EMAIL=$(gcloud config get-value account)

gcloud iam service-accounts add-iam-policy-binding $SERVICE_ACCOUNT_EMAIL \
    --member="user:$USER_EMAIL" \
    --role="roles/iam.serviceAccountTokenCreator" \
    --project=$PROJECT_ID
  1. 在浏览器中打开 Google 工作表。
  2. 点击“分享”按钮。
  3. 粘贴服务账号电子邮件地址 $SERVICE_ACCOUNT_EMAIL,并授予其“编辑者”访问权限。

3. 创建应用

首先,创建一个用于存放源代码的目录,然后通过 cd 命令进入该目录。

mkdir local-adk-sheets-codelab && cd $_

然后,创建一个包含以下内容的 main.py 文件:

import os
from fastapi import FastAPI, HTTPException
from pydantic import BaseModel
from google.auth import default
from googleapiclient.discovery import build
from google.adk.agents.llm_agent import Agent
from google.adk.runners import InMemoryRunner

# 1. Environment and Global Google Sheets Client Setup (ADC)
SPREADSHEET_ID = os.environ.get("SPREADSHEET_ID")
if not SPREADSHEET_ID:
    raise ValueError("SPREADSHEET_ID environment variable is missing. Please set it before running.")

credentials, _ = default(scopes=['https://www.googleapis.com/auth/spreadsheets'])
sheets_api = build('sheets', 'v4', credentials=credentials).spreadsheets()

# 2. Tools
def read_spreadsheet(range_name: str) -> dict:
    """Reads values from the Google Spreadsheet for a given range.
   
    Args:
        range_name: The A1 notation or R1C1 notation of the range to retrieve, e.g., 'Sheet1!A1:D10'.
    """
    return sheets_api.values().get(spreadsheetId=SPREADSHEET_ID, range=range_name).execute()

def update_spreadsheet(range_name: str, values: list[list[str]]) -> dict:
    """Updates the Google Spreadsheet range with the specified grid of values.
   
    Args:
        range_name: The A1 notation or R1C1 notation of the range to update, e.g., 'Sheet1!C2'.
        values: A list of lists of strings representing the grid of values to write.
    """
    return sheets_api.values().update(
        spreadsheetId=SPREADSHEET_ID,
        range=range_name,
        valueInputOption="USER_ENTERED",
        body={"values": values}
    ).execute()

# 3. Agent Definition
MODEL_NAME = os.environ.get("GEMINI_MODEL", "gemini-3.1-flash-lite")
root_agent = Agent(
    name="spreadsheet_agent",
    model=MODEL_NAME,
    instruction="""You are a helpful spreadsheet assistant.
    You can read and write to the user's Google Spreadsheet using the tools provided.
    Always use the appropriate tools when the user asks you to read or update a spreadsheet.
    When updating a range, make sure the values parameter is a list of lists (e.g. [[value]]).
    Be concise and helpful in your responses.""",
    description="An agent that can read and write to a specific Google Spreadsheet.",
    tools=[read_spreadsheet, update_spreadsheet]
)

# 4. FastAPI Application Setup
app = FastAPI()

class ChatRequest(BaseModel):
    prompt: str

@app.post("/chat")
async def chat_endpoint(request: ChatRequest):
    try:
        async with InMemoryRunner(app_name="sheets_agent_app", agent=root_agent) as runner:
            events = await runner.run_debug(request.prompt, quiet=True)
           
            # Extract the final response text
            texts = [
                "".join(p.text for p in e.content.parts if p.text)
                for e in reversed(events) if e.content and e.content.parts
            ]
            return {"response": next((t for t in texts if t), "No response from agent.")}
    except Exception as e:
        raise HTTPException(status_code=500, detail=str(e))

接下来,创建一个包含以下内容的 requirements.txt 文件:

fastapi>=0.100.0
uvicorn>=0.22.0
google-adk>=1.27.1
google-auth
google-api-python-client

4. 在本地测试服务

gcloud auth application-default login --impersonate-service-account=$SERVICE_ACCOUNT_EMAIL
GOOGLE_GENAI_USE_VERTEXAI=1 GOOGLE_CLOUD_PROJECT=$PROJECT_ID SPREADSHEET_ID=$SPREADSHEET_ID uvicorn main:app --host 0.0.0.0 --port 8080

现在,在另一个终端中测试您的代理:

curl -X POST http://localhost:8080/chat \
    -H "Content-Type: application/json" \
    -d '{"prompt": "Update spreadsheet '$SPREADSHEET_ID' at range Sheet1!A1 with the value hello world"}'

服务器应响应:{"response":"The spreadsheet at Sheet1!A1 has been updated with the value \"hello world\"."}

5. 部署到 Cloud Run

现在,您已了解服务账号拥有访问该电子表格的相应权限,接下来将使用该服务账号作为服务身份部署到 Cloud Run。

gcloud run deploy local-adk-sheets-codelab \
    --source . \
    --region=$REGION \
    --set-env-vars GOOGLE_GENAI_USE_VERTEXAI=1,GOOGLE_CLOUD_PROJECT=$PROJECT_ID,GOOGLE_CLOUD_LOCATION=global,SPREADSHEET_ID=$SPREADSHEET_ID \
    --service-account $SERVICE_ACCOUNT_EMAIL \
    --no-allow-unauthenticated \
    --project=$PROJECT_ID

部署完成后,向 Cloud Run 服务发送提示。

# Retrieve the Service URL
SERVICE_URL=$(gcloud run services describe local-adk-sheets-codelab --region $REGION --format 'value(status.url)')
curl -X POST $SERVICE_URL/chat \
    -H "Authorization: bearer $(gcloud auth print-identity-token)" \
    -H "Content-Type: application/json" \
    -d '{"prompt": "Update spreadsheet '$SPREADSHEET_ID' at range Sheet1!A1 with the value hello world"}'

您应该会收到 {"response":"OK. I've updated cell A1 in Sheet1 with \"hello world\"."} 的响应

现在,打开您的 Google 表格。您会在第一个单元格中看到“hello world”值。

6. 恭喜!

恭喜您完成此 Codelab!

后续操作

建议您探索以下文档,以扩展代理的功能:

所学内容

  • 如何为本地环境配置应用默认凭证 (ADC)
  • 如何创建可读取和写入 Google 表格的 ADK 智能体
  • 如何将代理部署到 Cloud Run

7. 清理

为避免产生意外费用(例如,如果此 Cloud Run function 被意外调用的次数超过了免费层级中每月 Cloud Run 调用次数的分配额度),您可以删除 Cloud Run 服务或删除项目。

如需删除 Cloud Run 服务,请前往 Cloud Console 中的 Cloud Run 页面 (https://console.cloud.google.com/run/),然后删除您在本 Codelab 中创建的服务 local-adk-sheets-codelab

如果您选择删除整个项目,可以前往 https://console.cloud.google.com/cloud-resource-manager,选择您在第 2 步中创建的项目,然后选择“删除”。如果您删除项目,则需要在 Cloud SDK 中更改项目。您可以运行 gcloud projects list 查看所有可用项目的列表。