1. Pengantar
Ringkasan
Dalam codelab ini, Anda akan melihat cara membuat bot chat dasar yang ditulis dalam node menggunakan Vertex AI Gemini API dan library klien Vertex AI. Aplikasi ini menggunakan penyimpanan sesi ekspres yang didukung oleh Google Cloud Firestore.
Yang akan Anda pelajari
- Cara menggunakan htmx, tailwindcss, dan express.js untuk membangun layanan Cloud Run
- Cara menggunakan library klien Vertex AI untuk mengautentikasi ke Google API
- Cara membuat chatbot untuk berinteraksi dengan model Gemini
- Cara men-deploy ke layanan Cloud Run tanpa file Docker
- Cara menggunakan penyimpanan sesi ekspres yang didukung oleh Google Cloud Firestore
2. Penyiapan dan Persyaratan
Prasyarat
- Anda sudah login ke Konsol Cloud.
- Anda sebelumnya telah men-deploy layanan Cloud Run. Misalnya, Anda dapat mengikuti men-deploy layanan web dari panduan memulai kode sumber 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. Mengaktifkan API dan Menetapkan Variabel Lingkungan
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 \ secretmanager.googleapis.com
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> SERVICE=chat-with-gemini SERVICE_ACCOUNT="vertex-ai-caller" SERVICE_ACCOUNT_ADDRESS=$SERVICE_ACCOUNT@$PROJECT_ID.iam.gserviceaccount.com SECRET_ID="SESSION_SECRET"
4. Membuat dan mengonfigurasi project Firebase
- Di Firebase console, klik Add project.
- Masukkan <YOUR_PROJECT_ID> untuk menambahkan Firebase ke salah satu project Google Cloud Anda yang sudah ada
- Jika diminta, tinjau dan setujui persyaratan Firebase.
- Klik Lanjutkan.
- Klik Confirm Plan untuk mengonfirmasi paket penagihan Firebase.
- Mengaktifkan Google Analytics di codelab ini bersifat opsional.
- Klik Add Firebase.
- Setelah project dibuat, klik Continue.
- Dari menu Build, klik Firestore database.
- Klik Buat database.
- Pilih wilayah dari drop-down Lokasi, lalu klik Berikutnya.
- Gunakan Mulai dalam mode produksi default, lalu klik Buat.
5. Membuat akun layanan
Akun layanan ini akan digunakan oleh Cloud Run untuk memanggil Vertex AI Gemini API. Akun layanan ini juga akan memiliki izin untuk membaca dan menulis ke Firestore serta membaca secret dari Secret Manager.
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
Sekarang, buat secret di Secret Manager. Layanan Cloud Run akan mengakses rahasia ini sebagai variabel lingkungan, yang diselesaikan pada waktu startup instance. Anda dapat mempelajari rahasia dan Cloud Run lebih lanjut.
gcloud secrets create $SECRET_ID --replication-policy="automatic" printf "keyboard-cat" | gcloud secrets versions add $SECRET_ID --data-file=-
Berikan akses ke rahasia sesi ekspres ke akun layanan di Secret Manager.
gcloud secrets add-iam-policy-binding $SECRET_ID \ --member serviceAccount:$SERVICE_ACCOUNT_ADDRESS \ --role='roles/secretmanager.secretAccessor'
Terakhir, beri akun layanan akses baca dan tulis ke Firestore.
gcloud projects add-iam-policy-binding $PROJECT_ID \ --member serviceAccount:$SERVICE_ACCOUNT_ADDRESS \ --role=roles/datastore.user
6. Membuat layanan Cloud Run
Pertama, buat direktori untuk kode sumber dan {i>cd<i} ke direktori tersebut.
mkdir chat-with-gemini && cd chat-with-gemini
Lalu, buat file package.json
dengan konten berikut:
{ "name": "chat-with-gemini", "version": "1.0.0", "description": "", "main": "app.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/connect-firestore": "^3.0.0", "@google-cloud/firestore": "^7.5.0", "@google-cloud/vertexai": "^0.4.0", "axios": "^1.6.8", "express": "^4.18.2", "express-session": "^1.18.0", "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"); // cloud run retrieves secret at instance startup time const secret = process.env.SESSION_SECRET; const { Firestore } = require("@google-cloud/firestore"); const { FirestoreStore } = require("@google-cloud/connect-firestore"); var session = require("express-session"); app.set("trust proxy", 1); // trust first proxy app.use( session({ store: new FirestoreStore({ dataset: new Firestore(), kind: "express-sessions" }), secret: secret, /* set secure to false for local dev session history testing */ /* see more at https://expressjs.com/en/resources/middleware/session.html */ cookie: { secure: true }, resave: false, saveUninitialized: true }) ); const expressWs = require("express-ws")(app); app.use(express.static("public")); // Vertex AI Section const { VertexAI } = require("@google-cloud/vertexai"); // instance of Vertex model let generativeModel; // on startup const port = parseInt(process.env.PORT) || 8080; app.listen(port, async () => { console.log(`demo1: listening on port ${port}`); // get project and location from metadata service const metadataService = require("./metadataService.js"); 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" }); }); app.ws("/sendMessage", async function (ws, req) { if (!req.session.chathistory || req.session.chathistory.length == 0) { req.session.chathistory = []; } let chatWithModel = generativeModel.startChat({ history: req.session.chathistory }); ws.on("message", async function (message) { console.log("req.sessionID: ", req.sessionID); // get session id 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); const answer = results.response.candidates[0].content.parts[0].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"> ${answer} </div>`); // save to current chat history let userHistory = { role: "user", parts: [{ text: questionToAsk }] }; let modelHistory = { role: "model", parts: [{ text: answer }] }; req.session.chathistory.push(userHistory); req.session.chathistory.push(modelHistory); // console.log( // "newly saved chat history: ", // util.inspect(req.session.chathistory, { // showHidden: false, // depth: null, // colors: true // }) // ); req.session.save(); }); ws.on("close", () => { console.log("WebSocket was closed"); }); }); function sleep(ms) { return new Promise((resolve) => { setTimeout(resolve, ms); }); } // gracefully close the web sockets process.on("SIGTERM", () => { server.close(); });
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 { // running on Clodu Run. 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 { // running on Clodu Run. 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>`;
Terakhir, buat file input.css
untuk tailwindCSS.
@tailwind base; @tailwind components; @tailwind utilities;
Sekarang, buat direktori public
baru.
mkdir public cd public
Dan dalam direktori publik tersebut, 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 1</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 > Is C# a programming language or a musical note?</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 secara lokal
Pertama, pastikan Anda berada di direktori utama chat-with-gemini
untuk codelab Anda.
cd .. && pwd
Selanjutnya, 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 ke bagian Membuat rahasia sesi 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
Membuat rahasia sesi lokal
Sekarang, buat rahasia sesi lokal untuk pengembangan lokal.
export SESSION_SECRET=local-secret
Menjalankan aplikasi secara lokal
Terakhir, Anda dapat memulai aplikasi dengan menjalankan skrip berikut. Skrip 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 layanan
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 $SERVICE \ --service-account $SERVICE_ACCOUNT_ADDRESS \ --source . \ --region $REGION \ --allow-unauthenticated \ --set-secrets="SESSION_SECRET=$(echo $SECRET_ID):1"
Jika Anda melihat pesan "Men-deploy dari sumber memerlukan repositori Docker Artifact Registry untuk menyimpan container yang dibangun. Repositori bernama [cloud-run-source-deploy] di region [us-central1] akan dibuat.", hit ‘y' untuk menerima dan melanjutkan.
9. Menguji layanan
Setelah di-deploy, buka URL layanan di browser web Anda. Lalu ajukan pertanyaan kepada Gemini, mis. "Saya berlatih gitar, tetapi saya juga seorang teknisi perangkat lunak. Saat melihat "C#", haruskah saya menganggapnya sebagai bahasa pemrograman atau not musik? Mana yang harus saya pilih?"
10. Selamat!
Selamat, Anda telah menyelesaikan codelab!
Sebaiknya tinjau dokumentasi Cloud Run dan Vertex AI Gemini API.
Yang telah kita bahas
- Cara menggunakan htmx, tailwindcss, dan express.js untuk membangun layanan Cloud Run
- Cara menggunakan library klien Vertex AI untuk mengautentikasi ke Google API
- Cara membuat bot chat untuk berinteraksi dengan model Gemini
- Cara men-deploy ke layanan Cloud Run tanpa file Docker
- Cara menggunakan penyimpanan sesi ekspres yang didukung oleh Google Cloud Firestore
11. Pembersihan
Untuk menghindari tagihan yang tidak disengaja, (misalnya, jika layanan Cloud Run secara tidak sengaja dipanggil lebih sering daripada alokasi panggilan Cloud Run bulanan Anda di paket gratis), Anda dapat menghapus 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/run dan hapus layanan chat-with-gemini
. 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
.