1. บทนำ
ภาพรวม
ใน Codelab นี้ คุณจะได้ดูวิธีให้สิทธิ์ Gemini เข้าถึงข้อมูลแบบเรียลไทม์โดยใช้ฟีเจอร์ใหม่ชื่อการเรียกใช้ฟังก์ชัน หากต้องการจำลองข้อมูลแบบเรียลไทม์ คุณจะสร้างปลายทางบริการสภาพอากาศที่แสดงสภาพอากาศปัจจุบันสำหรับ 2 สถานที่ จากนั้นคุณจะสร้างแอปแชทที่ขับเคลื่อนโดย Gemini ซึ่งใช้การเรียกใช้ฟังก์ชันเพื่อดึงข้อมูลสภาพอากาศปัจจุบัน
เรามาลองใช้ภาพแบบสั้นๆ เพื่อทำความเข้าใจการเรียกใช้ฟังก์ชันกัน
- ข้อความแจ้งคำขอสำหรับสภาพอากาศปัจจุบันในสถานที่ที่ระบุ
- พรอมต์นี้+ สัญญาฟังก์ชันสำหรับ getWeather() จะส่งไปยัง Gemini แล้ว
- Gemini ขอให้แอปแชทบ็อตเรียกใช้ "getWeather(Seattle)" ในนามของ
- แอปส่งผลลัพธ์กลับมา (40 องศา F และฝนตก)
- Gemini ส่งผลลัพธ์กลับไปให้ผู้โทร
สรุปคือ Gemini ไม่ได้เรียกใช้ฟังก์ชัน คุณในฐานะนักพัฒนาแอปต้องเรียกใช้ฟังก์ชันและส่งผลลัพธ์กลับไปยัง Gemini
สิ่งที่คุณจะได้เรียนรู้
- วิธีการทํางานของการเรียกใช้ฟังก์ชันของ Gemini
- วิธีทำให้แอปแชทบ็อตที่ทำงานด้วยระบบ Gemini เป็นบริการ Cloud Run
2. การตั้งค่าและข้อกำหนด
ข้อกำหนดเบื้องต้น
- คุณเข้าสู่ระบบ Cloud Console แล้ว
- คุณได้ทำให้ฟังก์ชันรุ่นที่ 2 ใช้งานได้ก่อนหน้านี้ เช่น คุณสามารถทำตามการเริ่มต้นใช้งาน Cloud Function รุ่นที่ 2 อย่างรวดเร็วเพื่อเริ่มต้นใช้งาน
เปิดใช้งาน Cloud Shell
- คลิกเปิดใช้งาน Cloud Shell จาก Cloud Console
หากเริ่มต้นใช้งาน Cloud Shell เป็นครั้งแรก คุณจะเห็นหน้าจอตรงกลางที่อธิบายว่านี่คืออะไร หากระบบแสดงหน้าจอตรงกลาง ให้คลิกต่อไป
การจัดสรรและเชื่อมต่อกับ Cloud Shell ใช้เวลาเพียงไม่กี่นาที
เครื่องเสมือนนี้โหลดด้วยเครื่องมือการพัฒนาทั้งหมดที่จำเป็น โดยมีไดเรกทอรีหลักขนาด 5 GB ถาวรและทำงานใน Google Cloud ซึ่งช่วยเพิ่มประสิทธิภาพของเครือข่ายและการตรวจสอบสิทธิ์ได้อย่างมาก งานส่วนใหญ่ใน Codelab นี้สามารถทำได้โดยใช้เบราว์เซอร์
เมื่อเชื่อมต่อกับ Cloud Shell แล้ว คุณควรเห็นข้อความตรวจสอบสิทธิ์และโปรเจ็กต์ได้รับการตั้งค่าเป็นรหัสโปรเจ็กต์แล้ว
- เรียกใช้คำสั่งต่อไปนี้ใน Cloud Shell เพื่อยืนยันว่าคุณได้รับการตรวจสอบสิทธิ์แล้ว
gcloud auth list
เอาต์พุตจากคำสั่ง
Credentialed Accounts ACTIVE ACCOUNT * <my_account>@<my_domain.com> To set the active account, run: $ gcloud config set account `ACCOUNT`
- เรียกใช้คำสั่งต่อไปนี้ใน Cloud Shell เพื่อยืนยันว่าคำสั่ง gcloud รู้เกี่ยวกับโปรเจ็กต์ของคุณ
gcloud config list project
เอาต์พุตจากคำสั่ง
[core] project = <PROJECT_ID>
หากไม่ใช่ ให้ตั้งคำสั่งด้วยคำสั่งนี้
gcloud config set project <PROJECT_ID>
เอาต์พุตจากคำสั่ง
Updated property [core/project].
3. ตั้งค่าตัวแปรสภาพแวดล้อมและเปิดใช้ API
ตั้งค่าตัวแปรสภาพแวดล้อม
คุณกำหนดตัวแปรสภาพแวดล้อมที่จะใช้ตลอดทั้ง Codelab ได้
PROJECT_ID=<YOUR_PROJECT_ID> REGION=<YOUR_REGION, e.g. us-central1> WEATHER_SERVICE=weatherservice FRONTEND=frontend SERVICE_ACCOUNT="vertex-ai-caller" SERVICE_ACCOUNT_ADDRESS=$SERVICE_ACCOUNT@$PROJECT_ID.iam.gserviceaccount.com
เปิดใช้ API
ก่อนที่คุณจะเริ่มใช้ Codelab นี้ได้ คุณจะต้องเปิดใช้ API หลายรายการ Codelab นี้ต้องใช้ API ต่อไปนี้ คุณเปิดใช้ API เหล่านั้นได้โดยเรียกใช้คำสั่งต่อไปนี้
gcloud services enable run.googleapis.com \ cloudbuild.googleapis.com \ aiplatform.googleapis.com
4. สร้างบัญชีบริการเพื่อเรียกใช้ Vertex AI
Cloud Run จะใช้บัญชีบริการนี้เพื่อเรียกใช้ Vertex AI Gemini API
ก่อนอื่น ให้สร้างบัญชีบริการโดยเรียกใช้คำสั่งนี้
gcloud iam service-accounts create $SERVICE_ACCOUNT \ --display-name="Cloud Run to access Vertex AI APIs"
ประการที่ 2 ให้บทบาทผู้ใช้ Vertex AI แก่บัญชีบริการ
gcloud projects add-iam-policy-binding $PROJECT_ID \ --member serviceAccount:$SERVICE_ACCOUNT_ADDRESS \ --role=roles/aiplatform.user
5. สร้างบริการ Cloud Run แบ็กเอนด์
ขั้นแรก ให้สร้างไดเรกทอรีสำหรับซอร์สโค้ดและ cd ลงในไดเรกทอรีนั้น
mkdir -p gemini-function-calling/weatherservice gemini-function-calling/frontend && cd gemini-function-calling/weatherservice
จากนั้นสร้างไฟล์ package.json
ที่มีเนื้อหาต่อไปนี้
{ "name": "weatherservice", "version": "1.0.0", "description": "", "main": "app.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1" }, "keywords": [], "author": "", "license": "ISC", "dependencies": { "express": "^4.18.3" } }
จากนั้นสร้างไฟล์ต้นฉบับ app.js
ที่มีเนื้อหาด้านล่าง ไฟล์นี้ประกอบด้วยจุดแรกเข้าสำหรับบริการและประกอบด้วยตรรกะหลักสำหรับแอป
const express = require("express"); const app = express(); app.get("/getweather", (req, res) => { const location = req.query.location; let temp, conditions; if (location == "New Orleans") { temp = 99; conditions = "hot and humid"; } else if (location == "Seattle") { temp = 40; conditions = "rainy and overcast"; } else { res.status(400).send("there is no data for the requested location"); } res.json({ weather: temp, location: location, conditions: conditions }); }); const port = parseInt(process.env.PORT) || 8080; app.listen(port, () => { console.log(`weather service: listening on port ${port}`); }); app.get("/", (req, res) => { res.send("welcome to hard-coded weather!"); });
ทำให้บริการสภาพอากาศใช้งานได้
คุณสามารถใช้คำสั่งนี้เพื่อทำให้บริการสภาพอากาศใช้งานได้
gcloud run deploy $WEATHER_SERVICE \ --source . \ --region $REGION \ --allow-unauthenticated
ทดสอบบริการสภาพอากาศ
คุณยืนยันสภาพอากาศทั้ง 2 แห่งได้โดยใช้ curl ดังนี้
WEATHER_SERVICE_URL=$(gcloud run services describe $WEATHER_SERVICE \ --platform managed \ --region=$REGION \ --format='value(status.url)') curl $WEATHER_SERVICE_URL/getweather?location=Seattle curl $WEATHER_SERVICE_URL/getweather?location\=New%20Orleans
ซีแอตเทิลอุณหภูมิ 40 องศาฟาเรนไฮต์และมีฝนตก ส่วนนิวออร์ลีนส์จะมีอุณหภูมิ 99 องศาฟาเรนไฮต์และมีความชื้นตลอดเวลา
6. สร้างบริการฟรอนท์เอนด์
ขั้นแรก ใส่ดีวีดีลงในไดเรกทอรีฟรอนท์เอนด์
cd gemini-function-calling/frontend
จากนั้นสร้างไฟล์ package.json
ที่มีเนื้อหาต่อไปนี้
{ "name": "demo1", "version": "1.0.0", "description": "", "main": "index.js", "scripts": { "start": "node app.js", "nodemon": "nodemon app.js", "cssdev": "npx tailwindcss -i ./input.css -o ./public/output.css --watch", "tailwind": "npx tailwindcss -i ./input.css -o ./public/output.css", "dev": "npm run tailwind && npm run nodemon" }, "keywords": [], "author": "", "license": "ISC", "dependencies": { "@google-cloud/vertexai": "^0.4.0", "axios": "^1.6.7", "express": "^4.18.2", "express-ws": "^5.0.2", "htmx.org": "^1.9.10" }, "devDependencies": { "nodemon": "^3.1.0", "tailwindcss": "^3.4.1" } }
จากนั้นสร้างไฟล์ต้นฉบับ app.js
ที่มีเนื้อหาด้านล่าง ไฟล์นี้ประกอบด้วยจุดแรกเข้าสำหรับบริการและประกอบด้วยตรรกะหลักสำหรับแอป
const express = require("express"); const app = express(); app.use(express.urlencoded({ extended: true })); app.use(express.json()); const path = require("path"); const fs = require("fs"); const util = require("util"); const { spinnerSvg } = require("./spinnerSvg.js"); const expressWs = require("express-ws")(app); app.use(express.static("public")); const { VertexAI, FunctionDeclarationSchemaType } = require("@google-cloud/vertexai"); // get project and location from metadata service const metadataService = require("./metadataService.js"); // instance of Gemini model let generativeModel; // 1: define the function const functionDeclarations = [ { function_declarations: [ { name: "getweather", description: "get weather for a given location", parameters: { type: FunctionDeclarationSchemaType.OBJECT, properties: { location: { type: FunctionDeclarationSchemaType.STRING }, degrees: { type: FunctionDeclarationSchemaType.NUMBER, "description": "current temperature in fahrenheit" }, conditions: { type: FunctionDeclarationSchemaType.STRING, "description": "how the weather feels subjectively" } }, required: ["location"] } } ] } ]; // on instance startup const port = parseInt(process.env.PORT) || 8080; app.listen(port, async () => { console.log(`demo1: listening on port ${port}`); const project = await metadataService.getProjectId(); const location = await metadataService.getRegion(); // Vertex client library instance const vertex_ai = new VertexAI({ project: project, location: location }); // Instantiate models generativeModel = vertex_ai.getGenerativeModel({ model: "gemini-1.0-pro-001" }); }); const axios = require("axios"); const baseUrl = "https://weatherservice-k6msmyp47q-uc.a.run.app"; app.ws("/sendMessage", async function (ws, req) { // this chat history will be pinned to the current // Cloud Run instance. Consider using Firestore & // Firebase anonymous auth instead. // start ephemeral chat session with Gemini const chatWithModel = generativeModel.startChat({ tools: functionDeclarations }); ws.on("message", async function (message) { let questionToAsk = JSON.parse(message).message; console.log("WebSocket message: " + questionToAsk); ws.send(`<div hx-swap-oob="beforeend:#toupdate"><div id="questionToAsk" class="text-black m-2 text-right border p-2 rounded-lg ml-24"> ${questionToAsk} </div></div>`); // to simulate a natural pause in conversation await sleep(500); // get timestamp for div to replace const now = "fromGemini" + Date.now(); ws.send(`<div hx-swap-oob="beforeend:#toupdate"><div id=${now} class=" text-blue-400 m-2 text-left border p-2 rounded-lg mr-24"> ${spinnerSvg} </div></div>`); const results = await chatWithModel.sendMessage(questionToAsk); // Function calling demo let response1 = await results.response; let data = response1.candidates[0].content.parts[0]; let methodToCall = data.functionCall; if (methodToCall === undefined) { console.log("Gemini says: ", data.text); ws.send(`<div id=${now} hx-swap-oob="true" hx-swap="outerHTML" class="text-blue-400 m-2 text-left border p-2 rounded-lg mr-24"> ${data.text} </div>`); // bail out - Gemini doesn't want to return a function return; } // otherwise Gemini wants to call a function console.log( "Gemini wants to call: " + methodToCall.name + " with args: " + util.inspect(methodToCall.args, { showHidden: false, depth: null, colors: true }) ); // make the external call let jsonReturned; try { const responseFunctionCalling = await axios.get( baseUrl + "/" + methodToCall.name, { params: { location: methodToCall.args.location } } ); jsonReturned = responseFunctionCalling.data; } catch (ex) { // in case an invalid location was provided jsonReturned = ex.response.data; } console.log("jsonReturned: ", jsonReturned); // tell the model what function we just called const functionResponseParts = [ { functionResponse: { name: methodToCall.name, response: { name: methodToCall.name, content: { jsonReturned } } } } ]; // // Send a follow up message with a FunctionResponse const result2 = await chatWithModel.sendMessage( functionResponseParts ); // This should include a text response from the model using the response content // provided above const response2 = await result2.response; let answer = response2.candidates[0].content.parts[0].text; console.log("answer: ", answer); ws.send(`<div id=${now} hx-swap-oob="true" hx-swap="outerHTML" class="text-blue-400 m-2 text-left border p-2 rounded-lg mr-24"> ${answer} </div>`); }); ws.on("close", () => { console.log("WebSocket was closed"); }); }); function sleep(ms) { return new Promise((resolve) => { setTimeout(resolve, ms); }); }
สร้างไฟล์ input.css
สำหรับ tailwindCSS
@tailwind base; @tailwind components; @tailwind utilities;
สร้างไฟล์ tailwind.config.js
สำหรับ tailwindCSS
/** @type {import('tailwindcss').Config} */ module.exports = { content: ["./**/*.{html,js}"], theme: { extend: {} }, plugins: [] };
สร้างไฟล์ metadataService.js
เพื่อรับรหัสโปรเจ็กต์และภูมิภาคสำหรับบริการ Cloud Run ที่ทำให้ใช้งานได้แล้ว ระบบจะใช้ค่าเหล่านี้เพื่อสร้างอินสแตนซ์ของไลบรารีไคลเอ็นต์ Vertex AI
const your_project_id = "YOUR_PROJECT_ID"; const your_region = "YOUR_REGION"; const axios = require("axios"); module.exports = { getProjectId: async () => { let project = ""; try { // Fetch the token to make a GCF to GCF call const response = await axios.get( "http://metadata.google.internal/computeMetadata/v1/project/project-id", { headers: { "Metadata-Flavor": "Google" } } ); if (response.data == "") { // running locally on Cloud Shell project = your_project_id; } else { // use project id from metadata service project = response.data; } } catch (ex) { // running locally on local terminal project = your_project_id; } return project; }, getRegion: async () => { let region = ""; try { // Fetch the token to make a GCF to GCF call const response = await axios.get( "http://metadata.google.internal/computeMetadata/v1/instance/region", { headers: { "Metadata-Flavor": "Google" } } ); if (response.data == "") { // running locally on Cloud Shell region = your_region; } else { // use region from metadata service let regionFull = response.data; const index = regionFull.lastIndexOf("/"); region = regionFull.substring(index + 1); } } catch (ex) { // running locally on local terminal region = your_region; } return region; } };
สร้างไฟล์ชื่อ spinnerSvg.js
module.exports.spinnerSvg = `<svg class="animate-spin -ml-1 mr-3 h-5 w-5 text-blue-500" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" > <circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4" ></circle> <path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z" ></path></svg>`;
สร้างไดเรกทอรี public
ใหม่
mkdir public cd public
จากนั้นสร้างไฟล์ index.html
สำหรับส่วนหน้า ซึ่งจะใช้ htmx
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <script src="https://unpkg.com/htmx.org@1.9.10" integrity="sha384-D1Kt99CQMDuVetoL1lrYwg5t+9QdHe7NLX/SoJYkXDFfX37iInKRy5xLSi8nO7UC" crossorigin="anonymous" ></script> <link href="./output.css" rel="stylesheet" /> <script src="https://unpkg.com/htmx.org/dist/ext/ws.js"></script> <title>Demo 2</title> </head> <body> <div id="herewego" text-center> <!-- <div id="replaceme2" hx-swap-oob="true">Hello world</div> --> <div class="container mx-auto mt-8 text-center max-w-screen-lg" > <div class="overflow-y-scroll bg-white p-2 border h-[500px] space-y-4 rounded-lg m-auto" > <div id="toupdate"></div> </div> <form hx-trigger="submit, keyup[keyCode==13] from:body" hx-ext="ws" ws-connect="/sendMessage" ws-send="" hx-on="htmx:wsAfterSend: document.getElementById('message').value = ''" > <div class="mb-6 mt-6 flex gap-4"> <textarea rows="2" type="text" id="message" name="message" class="block grow rounded-lg border p-6 resize-none" required > What's is the current weather in Seattle?</textarea > <button type="submit" class="bg-blue-500 text-white px-4 py-2 rounded-lg text-center text-sm font-medium" > Send </button> </div> </form> </div> </div> </body> </html>
7. เรียกใช้บริการฟรอนท์เอนด์ในเครื่อง
ขั้นแรก ตรวจสอบให้แน่ใจว่าคุณอยู่ในไดเรกทอรี frontend
สำหรับ Codelab
cd .. && pwd
จากนั้นติดตั้งการอ้างอิงโดยเรียกใช้คำสั่งต่อไปนี้
npm install
การใช้ ADC เมื่อทำงานในเครื่อง
หากคุณเรียกใช้ใน Cloud Shell ก็หมายความว่าคุณกำลังทำงานบนเครื่องเสมือนของ Google Compute Engine อยู่แล้ว ข้อมูลเข้าสู่ระบบเริ่มต้นของแอปพลิเคชันจะใช้ข้อมูลเข้าสู่ระบบของคุณที่เชื่อมโยงกับเครื่องเสมือนนี้ (ตามที่แสดงโดยการเรียกใช้ gcloud auth list
) โดยอัตโนมัติ ดังนั้นจึงไม่จำเป็นต้องใช้คำสั่ง gcloud auth application-default login
คุณสามารถข้ามไปยังส่วนเรียกใช้แอปในเครื่อง
แต่หากคุณกำลังทำงานบนเทอร์มินัลในเครื่อง (ไม่ใช่ใน Cloud Shell) คุณจะต้องใช้ข้อมูลเข้าสู่ระบบเริ่มต้นของแอปพลิเคชันเพื่อตรวจสอบสิทธิ์ Google APIs 1) เข้าสู่ระบบโดยใช้ข้อมูลเข้าสู่ระบบ (ในกรณีที่คุณมีทั้งบทบาทผู้ใช้ Vertex AI และพื้นที่เก็บข้อมูล) หรือ 2) คุณเข้าสู่ระบบได้โดยแอบอ้างบัญชีบริการที่ใช้ใน Codelab นี้
ตัวเลือกที่ 1) การใช้ข้อมูลเข้าสู่ระบบสำหรับ ADC
หากต้องการใช้ข้อมูลเข้าสู่ระบบ ให้เรียกใช้ gcloud auth list
เพื่อยืนยันวิธีตรวจสอบสิทธิ์ใน gcloud ก่อน ถัดไป คุณอาจต้องมอบบทบาทผู้ใช้ Vertex AI ให้กับข้อมูลระบุตัวตนของคุณ หากข้อมูลประจำตัวของคุณมีบทบาทเจ้าของ แสดงว่าคุณมีบทบาทของผู้ใช้ Vertex AI นี้อยู่แล้ว หากไม่มี ให้เรียกใช้คำสั่งนี้เพื่อมอบบทบาทของผู้ใช้ Vertex AI และบทบาทผู้ใช้ Datastore
USER=<YOUR_PRINCIPAL_EMAIL> gcloud projects add-iam-policy-binding $PROJECT_ID \ --member user:$USER \ --role=roles/aiplatform.user gcloud projects add-iam-policy-binding $PROJECT_ID \ --member user:$USER \ --role=roles/datastore.user
จากนั้นเรียกใช้คำสั่งต่อไปนี้
gcloud auth application-default login
ตัวเลือกที่ 2) การแอบอ้างเป็นบัญชีบริการสำหรับ ADC
หากต้องการใช้บัญชีบริการที่สร้างใน Codelab นี้ บัญชีผู้ใช้ของคุณจะต้องมีบทบาทผู้สร้างโทเค็นบัญชีบริการ คุณสามารถรับบทบาทนี้ได้โดยการเรียกใช้คำสั่งต่อไปนี้:
gcloud projects add-iam-policy-binding $PROJECT_ID \ --member user:$USER \ --role=roles/iam.serviceAccountTokenCreator
ถัดไป คุณจะต้องเรียกใช้คำสั่งต่อไปนี้เพื่อใช้ ADC กับบัญชีบริการ
gcloud auth application-default login --impersonate-service-account=$SERVICE_ACCOUNT_ADDRESS
เรียกใช้แอปในเครื่อง
สุดท้าย คุณสามารถเริ่มแอปได้โดยเรียกใช้สคริปต์ต่อไปนี้ สคริปต์นักพัฒนาซอฟต์แวร์นี้จะสร้างไฟล์ ISRC.css จาก tailwindCSS ด้วย
npm run dev
คุณดูตัวอย่างเว็บไซต์ได้โดยเปิดปุ่ม "แสดงตัวอย่างเว็บ" แล้วเลือก "แสดงตัวอย่างพอร์ต 8080"
8. ทำให้ใช้งานได้และทดสอบบริการฟรอนท์เอนด์
ก่อนอื่นให้เรียกใช้คำสั่งนี้เพื่อเริ่มการทำให้ใช้งานได้และระบุบัญชีบริการที่จะใช้ หากไม่ได้ระบุบัญชีบริการ ระบบจะใช้บัญชีบริการประมวลผลเริ่มต้น
gcloud run deploy $FRONTEND \ --service-account $SERVICE_ACCOUNT_ADDRESS \ --source . \ --region $REGION \ --allow-unauthenticated
เปิด URL บริการของฟรอนท์เอนด์ในเบราว์เซอร์ ถามคำถาม "สภาพอากาศในซีแอตเทิลตอนนี้เป็นอย่างไร" และ Gemini ควรตอบว่า "ตอนนี้อุณหภูมิ 40 องศาและฝนตก" ถ้าคุณถามว่า "อากาศตอนนี้ในบอสตันเป็นอย่างไร" Gemini จะตอบกลับว่า "ฉันดำเนินการตามคำขอนี้ไม่ได้ Weather API ที่มีอยู่ไม่มีข้อมูลสำหรับบอสตัน"
9. ยินดีด้วย
ขอแสดงความยินดีที่เรียน Codelab จนจบ
เราขอแนะนำให้ดูเอกสารประกอบ Cloud Run, Vertex AI Gemini API และการเรียกใช้ฟังก์ชัน
หัวข้อที่ครอบคลุม
- วิธีการทํางานของการเรียกใช้ฟังก์ชันของ Gemini
- วิธีทำให้แอปแชทบ็อตที่ทำงานด้วยระบบ Gemini เป็นบริการ Cloud Run
10. ล้างข้อมูล
เพื่อหลีกเลี่ยงการเรียกเก็บเงินที่ไม่ตั้งใจ (เช่น หากมีการเรียกใช้บริการ Cloud Run นี้โดยไม่ได้ตั้งใจมากกว่าการจัดสรรการเรียกใช้ Cloud Run รายเดือนในระดับฟรี) คุณจะลบบริการ Cloud Run หรือลบโปรเจ็กต์ที่คุณสร้างในขั้นตอนที่ 2 ก็ได้
หากต้องการลบบริการ Cloud Run ให้ไปที่ Cloud Run บน Cloud Console ที่ https://console.cloud.google.com/functions/ และลบบริการ $WEATHER_SERVICE และ $FRONTEND ที่คุณสร้างไว้ใน Codelab นี้
นอกจากนี้ คุณอาจต้องลบบัญชีบริการ vertex-ai-caller
หรือเพิกถอนบทบาทผู้ใช้ Vertex AI เพื่อหลีกเลี่ยงการเรียกใช้ Gemini โดยไม่ตั้งใจ
หากเลือกลบทั้งโปรเจ็กต์ ให้ไปที่ https://console.cloud.google.com/cloud-resource-manager เลือกโปรเจ็กต์ที่คุณสร้างในขั้นตอนที่ 2 แล้วเลือกลบ หากลบโปรเจ็กต์ คุณจะต้องเปลี่ยนโปรเจ็กต์ใน Cloud SDK คุณสามารถดูรายการโปรเจ็กต์ที่ใช้ได้ทั้งหมดโดยเรียกใช้ gcloud projects list