1. Pengantar
Ringkasan
Dalam codelab ini, Anda akan melihat cara memberi Gemini akses ke data real time menggunakan fitur baru yang disebut Panggilan Fungsi. Untuk menyimulasikan data real time, Anda akan membangun endpoint layanan cuaca yang menampilkan cuaca saat ini untuk 2 lokasi. Kemudian, Anda akan membangun aplikasi percakapan, dengan teknologi Gemini, yang menggunakan panggilan fungsi untuk mengetahui cuaca saat ini.
Mari kita gunakan visualisasi singkat untuk memahami Panggilan Fungsi.
- Permintaan untuk lokasi cuaca saat ini di lokasi tertentu
- Perintah ini + kontrak fungsi untuk getWeather() akan dikirim ke Gemini
- Gemini meminta agar aplikasi bot chat memanggil "getWeather(Seattle)" atas namanya
- Aplikasi mengirimkan kembali hasil (40 derajat F dan hujan)
- Gemini mengirimkan kembali hasilnya kepada pemanggil
Singkatnya, Gemini tidak memanggil Fungsi. Anda sebagai developer harus memanggil fungsi dan mengirimkan kembali hasilnya ke Gemini.
Yang akan Anda pelajari
- Cara kerja panggilan fungsi Gemini
- Cara men-deploy aplikasi chatbot yang didukung Gemini sebagai layanan Cloud Run
2. Penyiapan dan Persyaratan
Prasyarat
- Anda sudah login ke Konsol Cloud.
- Anda sebelumnya telah men-deploy fungsi generasi ke-2. Misalnya, Anda dapat mengikuti men-deploy panduan memulai Cloud Function generasi ke-2 untuk memulai.
Mengaktifkan Cloud Shell
- Dari Cloud Console, klik Aktifkan Cloud Shell .
Jika ini pertama kalinya Anda memulai Cloud Shell, Anda akan melihat layar perantara yang menjelaskan apa itu Cloud Shell. Jika Anda melihat layar perantara, klik Lanjutkan.
Perlu waktu beberapa saat untuk penyediaan dan terhubung ke Cloud Shell.
Mesin virtual ini dimuat dengan semua alat pengembangan yang diperlukan. Layanan ini menawarkan direktori beranda tetap sebesar 5 GB dan beroperasi di Google Cloud, sehingga sangat meningkatkan performa dan autentikasi jaringan. Sebagian besar pekerjaan Anda dalam codelab ini dapat dilakukan dengan browser.
Setelah terhubung ke Cloud Shell, Anda akan melihat bahwa Anda telah diautentikasi dan project sudah ditetapkan ke project ID Anda.
- Jalankan perintah berikut di Cloud Shell untuk mengonfirmasi bahwa Anda telah diautentikasi:
gcloud auth list
Output perintah
Credentialed Accounts ACTIVE ACCOUNT * <my_account>@<my_domain.com> To set the active account, run: $ gcloud config set account `ACCOUNT`
- Jalankan perintah berikut di Cloud Shell untuk mengonfirmasi bahwa perintah gcloud mengetahui project Anda:
gcloud config list project
Output perintah
[core] project = <PROJECT_ID>
Jika tidak, Anda dapat menyetelnya dengan perintah ini:
gcloud config set project <PROJECT_ID>
Output perintah
Updated property [core/project].
3. Menyiapkan variabel lingkungan dan mengaktifkan API
Menyiapkan variabel lingkungan
Anda dapat menetapkan variabel lingkungan yang akan digunakan di seluruh codelab ini.
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
Mengaktifkan API
Sebelum Anda dapat mulai menggunakan codelab ini, ada beberapa API yang perlu Anda aktifkan. Codelab ini memerlukan penggunaan API berikut. Anda dapat mengaktifkan API tersebut dengan menjalankan perintah berikut:
gcloud services enable run.googleapis.com \ cloudbuild.googleapis.com \ aiplatform.googleapis.com
4. Buat akun layanan untuk memanggil Vertex AI
Akun layanan ini akan digunakan oleh Cloud Run untuk memanggil Vertex AI Gemini API.
Pertama, buat akun layanan dengan menjalankan perintah ini:
gcloud iam service-accounts create $SERVICE_ACCOUNT \ --display-name="Cloud Run to access Vertex AI APIs"
Kedua, berikan peran Vertex AI User ke akun layanan.
gcloud projects add-iam-policy-binding $PROJECT_ID \ --member serviceAccount:$SERVICE_ACCOUNT_ADDRESS \ --role=roles/aiplatform.user
5. Membuat layanan Cloud Run backend
Pertama, buat direktori untuk kode sumber dan {i>cd<i} ke direktori tersebut.
mkdir -p gemini-function-calling/weatherservice gemini-function-calling/frontend && cd gemini-function-calling/weatherservice
Lalu, buat file package.json
dengan konten berikut:
{ "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" } }
Selanjutnya, buat file sumber app.js
dengan konten di bawah ini. File ini berisi titik entri untuk layanan dan berisi logika utama untuk aplikasi.
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!"); });
Men-deploy Layanan Cuaca
Anda dapat menggunakan perintah ini untuk men-deploy layanan cuaca.
gcloud run deploy $WEATHER_SERVICE \ --source . \ --region $REGION \ --allow-unauthenticated
Menguji Layanan Cuaca
Anda dapat memverifikasi cuaca untuk 2 lokasi tersebut menggunakan 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
Suhu di Seattle 40 derajat F dan hujan, sedangkan suhu di New Orleans selalu lembap.
6. Membuat layanan Frontend
Pertama, lakukan cd ke direktori frontend.
cd gemini-function-calling/frontend
Lalu, buat file package.json
dengan konten berikut:
{ "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" } }
Selanjutnya, buat file sumber app.js
dengan konten di bawah ini. File ini berisi titik entri untuk layanan dan berisi logika utama untuk aplikasi.
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); }); }
Buat file input.css
untuk tailwindCSS.
@tailwind base; @tailwind components; @tailwind utilities;
Buat file tailwind.config.js
untuk tailwindCSS.
/** @type {import('tailwindcss').Config} */ module.exports = { content: ["./**/*.{html,js}"], theme: { extend: {} }, plugins: [] };
Buat file metadataService.js
untuk mendapatkan project dan region untuk layanan Cloud Run yang di-deploy. Nilai ini akan digunakan untuk membuat instance library klien 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; } };
Buat file dengan nama 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>`;
Buat direktori public
baru.
mkdir public cd public
Sekarang buat file index.html
untuk frontend, yang akan menggunakan 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. Menjalankan layanan Frontend secara lokal
Pertama, pastikan Anda berada di direktori frontend
untuk codelab Anda.
cd .. && pwd
Kemudian, instal dependensi dengan menjalankan perintah berikut:
npm install
Menggunakan ADC saat berjalan secara lokal
Jika menjalankan Cloud Shell, berarti Anda sudah menjalankannya di virtual machine Google Compute Engine. Kredensial Anda yang terkait dengan virtual machine ini (seperti yang ditunjukkan dengan menjalankan gcloud auth list
) akan otomatis digunakan oleh Application Default Credentials, sehingga tidak perlu menggunakan perintah gcloud auth application-default login
. Anda dapat langsung melanjutkan ke bagian Menjalankan aplikasi secara lokal
Namun, jika menjalankan di terminal lokal (bukan di Cloud Shell), Anda harus menggunakan Kredensial Default Aplikasi untuk melakukan autentikasi ke Google API. Anda dapat 1) login menggunakan kredensial (asalkan Anda memiliki peran Vertex AI User dan Datastore User) atau 2) Anda dapat login dengan meniru akun layanan yang digunakan dalam codelab ini.
Opsi 1) Menggunakan kredensial Anda untuk ADC
Jika ingin menggunakan kredensial, Anda dapat menjalankan gcloud auth list
terlebih dahulu untuk memverifikasi cara autentikasi Anda di gcloud. Selanjutnya, Anda mungkin perlu memberikan peran Vertex AI User ke identitas Anda. Jika identitas Anda memiliki peran Pemilik, berarti Anda sudah memiliki peran pengguna Vertex AI ini. Jika tidak, Anda dapat menjalankan perintah ini untuk memberikan peran pengguna Vertex AI dan peran Datastore User kepada identitas Anda.
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
Lalu jalankan perintah berikut
gcloud auth application-default login
Opsi 2) Meniru Identitas Akun Layanan untuk ADC
Jika Anda ingin menggunakan akun layanan yang dibuat di codelab ini, akun pengguna Anda harus memiliki peran Service Account Token Creator. Anda dapat memperoleh peran ini dengan menjalankan perintah berikut:
gcloud projects add-iam-policy-binding $PROJECT_ID \ --member user:$USER \ --role=roles/iam.serviceAccountTokenCreator
Selanjutnya, Anda akan menjalankan perintah berikut untuk menggunakan ADC dengan akun layanan
gcloud auth application-default login --impersonate-service-account=$SERVICE_ACCOUNT_ADDRESS
Menjalankan aplikasi secara lokal
Terakhir, Anda dapat memulai aplikasi dengan menjalankan skrip berikut. Skrip dev ini juga akan menghasilkan file output.css dari tailwindCSS.
npm run dev
Anda dapat melihat pratinjau situs dengan membuka tombol Web Preview dan memilih Preview Port 8080
8. Men-deploy dan menguji layanan Frontend
Pertama-tama, jalankan perintah ini untuk memulai deployment dan menentukan akun layanan yang akan digunakan. Jika akun layanan tidak ditentukan, akun layanan komputasi default akan digunakan.
gcloud run deploy $FRONTEND \ --service-account $SERVICE_ACCOUNT_ADDRESS \ --source . \ --region $REGION \ --allow-unauthenticated
Buka URL layanan untuk frontend di browser Anda. Ajukan pertanyaan "Bagaimana cuaca saat ini di Jakarta?" dan Gemini harus merespons "Saat ini suhunya 40 derajat dan hujan." Jika Anda bertanya "Bagaimana cuaca saat ini di Jakarta?", Gemini akan merespons dengan pesan "Saya tidak dapat memenuhi permintaan ini. API cuaca yang tersedia tidak memiliki data untuk Boston."
9. Selamat!
Selamat, Anda telah menyelesaikan codelab!
Sebaiknya tinjau dokumentasi Cloud Run, Vertex AI Gemini API, dan panggilan fungsi.
Yang telah kita bahas
- Cara kerja panggilan fungsi Gemini
- Cara men-deploy aplikasi chatbot yang didukung Gemini sebagai layanan Cloud Run
10. Pembersihan
Untuk menghindari tagihan yang tidak disengaja, (misalnya, jika layanan Cloud Run ini secara tidak sengaja dipanggil lebih sering daripada alokasi pemanggilan Cloud Run bulanan Anda dalam paket gratis), Anda dapat menghapus layanan Cloud Run atau menghapus project yang Anda buat di Langkah 2.
Untuk menghapus layanan Cloud Run, buka Konsol Cloud Cloud Run di https://console.cloud.google.com/functions/ dan hapus layanan $WEATHER_SERVICE dan $FRONTEND yang Anda buat di codelab ini.
Anda juga dapat menghapus akun layanan vertex-ai-caller
atau mencabut peran Pengguna Vertex AI, untuk menghindari panggilan yang tidak disengaja ke Gemini.
Jika memilih untuk menghapus seluruh project, Anda dapat membuka https://console.cloud.google.com/cloud-resource-manager, pilih project yang dibuat pada Langkah 2, lalu pilih Hapus. Jika project dihapus, Anda harus mengubah project di Cloud SDK. Anda dapat melihat daftar semua project yang tersedia dengan menjalankan gcloud projects list
.