如何在部署至 Cloud Run 前,在本機測試會寫入 Google 試算表的 ADK 代理

1. 簡介

總覽

在這篇文章中,您將使用 Google Agent Development Kit (ADK) 和 FastAPI 建構 Gemini 代理。部署前,您會設定應用程式預設憑證 (ADC),在本機使用與 Cloud Run 服務身分相同的服務帳戶進行測試。

如要與 Google 試算表通訊,應用程式需要具有適當範圍的 OAuth 存取權杖,才能存取試算表。您將瞭解如何透過 ADC 在本機和 Cloud Run 上執行時取得這個存取權杖:

  • 本機:ADC 會尋找 gcloud auth application-default 指令產生的憑證。詳情請參閱這篇文章
  • 在 Cloud Run 上:ADC 會使用中繼資料伺服器取得憑證。詳情請參閱這篇文章

術語附註:

您可能比較熟悉「承擔身分」或「承擔相同權限」這類用語。在 Google Cloud 中,經過驗證的主體模擬服務帳戶後,就能存取該服務帳戶可存取的任何資源。只有經過驗證且具備適當權限的主體,才能模擬服務帳戶。詳情請參閱 https://docs.cloud.google.com/iam/docs/service-account-overview#impersonation

課程內容

  • 如何為本機環境設定應用程式預設憑證 (ADC)
  • 如何建立可讀取及寫入 Google 試算表的 ADK 代理
  • 如何將代理程式部署至 Cloud Run

2. 設定和需求條件

必要條件

  • 您已登入 Cloud Console。
  • 您先前已部署 Cloud Run 服務。舉例來說,您可以按照部署 Cloud Run 服務的說明開始操作。

設定環境變數

您可以設定本程式碼實驗室全程都會使用的環境變數。

複製共用連結時,您可以在 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 使用者角色授予服務帳戶 (這是使用 Vertex AI 上的 Gemini 模型時的必要步驟)

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

授予 gcloud 身分模擬服務帳戶的權限 (本機開發時需要)。您可以執行 gcloud auth list

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. 恭喜!

恭喜您完成本程式碼研究室!

後續步驟

建議您參考下列說明文件,擴充代理程式的功能:

涵蓋內容

  • 如何為本機環境設定應用程式預設憑證 (ADC)
  • 如何建立可讀取及寫入 Google 試算表的 ADK 代理
  • 如何將代理程式部署至 Cloud Run

7. 清理

為避免產生非預期費用 (例如,如果這個 Cloud Run 函式遭非預期呼叫的次數,超過免費層級每月 Cloud Run 呼叫次數配額),您可以刪除 Cloud Run 服務或專案。

如要刪除 Cloud Run 服務,請前往 Cloud 控制台的 Cloud Run 頁面 (https://console.cloud.google.com/run/),然後刪除您在本程式碼研究室中建立的服務 local-adk-sheets-codelab

如要刪除整個專案,請前往 https://console.cloud.google.com/cloud-resource-manager,選取您在步驟 2 中建立的專案,然後選擇「刪除」。刪除專案後,您必須在 Cloud SDK 中變更專案。如要查看所有可用專案的清單,請執行 gcloud projects list