1. Pengantar
Ringkasan
Dalam codelab ini, Anda akan melihat cara membuat chatbot dasar yang ditulis dalam node menggunakan Vertex AI Gemini API dan library klien Vertex AI. Aplikasi ini menggunakan penyimpanan sesi Express 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 melakukan autentikasi 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 Express yang didukung oleh Google Cloud Firestore
2. Penyiapan dan Persyaratan
Prasyarat
- Anda login ke Konsol Cloud.
- Anda telah men-deploy layanan Cloud Run sebelumnya. Misalnya, Anda dapat mengikuti panduan memulai deployment layanan web dari kode sumber untuk memulai.
Mengaktifkan Cloud Shell
- Dari Cloud Console, klik Aktifkan Cloud Shell
.

Jika ini adalah pertama kalinya Anda memulai Cloud Shell, Anda akan melihat layar perantara yang menjelaskan apa itu Cloud Shell. Jika Anda melihat layar perantara, klik Continue.

Perlu waktu beberapa saat untuk menyediakan dan terhubung ke Cloud Shell.

Virtual machine ini dilengkapi dengan semua alat pengembangan yang diperlukan. VM 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 telah 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 diaktifkan. 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 Tambahkan project.
- Masukkan <YOUR_PROJECT_ID> untuk menambahkan Firebase ke salah satu project Google Cloud yang ada
- Jika diminta, tinjau dan setujui persyaratan Firebase.
- Klik Lanjutkan.
- Klik Confirm Plan untuk mengonfirmasi paket penagihan Firebase.
- Anda dapat memilih untuk Mengaktifkan Google Analytics untuk codelab ini.
- Klik Add Firebase.
- Setelah project dibuat, klik Lanjutkan.
- Dari menu Build, klik Firestore database.
- Klik Create database.
- Pilih wilayah Anda 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 secret ini sebagai variabel lingkungan, yang di-resolve pada waktu startup instance. Anda dapat mempelajari lebih lanjut secrets dan Cloud Run.
gcloud secrets create $SECRET_ID --replication-policy="automatic" printf "keyboard-cat" | gcloud secrets versions add $SECRET_ID --data-file=-
Beri akun layanan akses ke secret sesi Express 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. Buat layanan Cloud Run
Pertama, buat direktori untuk kode sumber dan cd ke direktori tersebut.
mkdir chat-with-gemini && cd chat-with-gemini
Kemudian, 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. 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 ID 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 bernama 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
Di dalam direktori publik tersebut, buat file index.html untuk front-end, 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 root chat-with-gemini untuk codelab Anda.
cd .. && pwd
Selanjutnya, instal dependensi dengan menjalankan perintah berikut:
npm install
Menggunakan ADC saat berjalan secara lokal
Jika Anda menjalankan di Cloud Shell, Anda sudah menjalankan di virtual machine Google Compute Engine. Kredensial Anda yang terkait dengan mesin virtual ini (seperti yang ditunjukkan dengan menjalankan gcloud auth list) akan otomatis digunakan oleh Kredensial Default Aplikasi, sehingga Anda tidak perlu menggunakan perintah gcloud auth application-default login. Anda dapat langsung membuka bagian Buat secret sesi lokal
Namun, jika Anda menjalankan di terminal lokal (yaitu, bukan di Cloud Shell), Anda harus menggunakan Kredensial Default Aplikasi untuk melakukan autentikasi ke Google API. Anda dapat 1) login menggunakan kredensial Anda (asalkan Anda memiliki peran Pengguna Vertex AI dan Pengguna Datastore) 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 Anda diautentikasi di gcloud. Selanjutnya, Anda mungkin perlu memberikan peran Vertex AI User ke identitas Anda. Jika identitas Anda memiliki peran Pemilik, Anda sudah memiliki peran pengguna Vertex AI ini. Jika tidak, Anda dapat menjalankan perintah ini untuk memberikan peran pengguna Vertex AI dan peran Pengguna Datastore ke 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
Kemudian, jalankan perintah berikut
gcloud auth application-default login
Opsi 2) Meniru Identitas Akun Layanan untuk ADC
Jika Anda ingin menggunakan akun layanan yang dibuat dalam 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 secret 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 membuat file output.css dari tailwindCSS.
npm run dev
Anda dapat melihat pratinjau situs dengan membuka tombol Pratinjau Web dan memilih Pratinjau Port 8080

8. Men-deploy layanan
Pertama, jalankan perintah ini untuk memulai deployment dan tentukan 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 diminta untuk membuat repositori Docker Artifact Registry untuk menyimpan container yang di-build, berarti Anda belum membuatnya. Repositori bernama [cloud-run-source-deploy] di region [us-central1] akan dibuat.", tekan 'y' untuk menyetujui dan melanjutkan.
9. Uji layanan
Setelah di-deploy, buka URL layanan di browser web Anda. Kemudian, ajukan pertanyaan kepada Gemini, misalnya "Saya berlatih gitar, tetapi saya juga seorang software engineer. Saat saya melihat "C#", apakah saya harus menganggapnya sebagai bahasa pemrograman atau not balok? 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 melakukan autentikasi 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 Express yang didukung oleh Google Cloud Firestore
11. Pembersihan
Untuk menghindari biaya yang tidak disengaja (misalnya, jika layanan Cloud Run tidak sengaja dipanggil lebih banyak daripada alokasi pemanggilan Cloud Run bulanan Anda di tingkat gratis), Anda dapat menghapus Cloud Run atau menghapus project yang Anda buat di Langkah 2.
Untuk menghapus layanan Cloud Run, buka Konsol Cloud Run di https://console.cloud.google.com/run, lalu hapus layanan chat-with-gemini. Anda juga dapat menghapus akun layanan vertex-ai-caller atau mencabut peran Vertex AI User, untuk menghindari panggilan yang tidak disengaja ke Gemini.
Jika Anda memilih untuk menghapus seluruh project, Anda dapat membuka https://console.cloud.google.com/cloud-resource-manager, memilih project yang Anda buat di Langkah 2, lalu memilih Hapus. Jika menghapus project, Anda harus mengubah project di Cloud SDK. Anda dapat melihat daftar semua project yang tersedia dengan menjalankan gcloud projects list.