1. Introduzione
Panoramica
In questo codelab, vedrai come creare un chatbot di base scritto in Node utilizzando l'API Vertex AI Gemini e la libreria client Vertex AI. Questa app utilizza un archivio di sessioni express supportato da Google Cloud Firestore.
Cosa imparerai a fare
- Come utilizzare htmx, tailwindcss ed express.js per creare un servizio Cloud Run
- Come utilizzare le librerie client di Vertex AI per l'autenticazione alle API di Google
- Come creare un chatbot per interagire con il modello Gemini
- Come eseguire il deployment in un servizio Cloud Run senza un file Docker
- Come utilizzare un archivio di sessioni Express supportato da Google Cloud Firestore
2. Configurazione e requisiti
Prerequisiti
- Hai eseguito l'accesso a Cloud Console.
- Hai già eseguito il deployment di un servizio Cloud Run. Ad esempio, puoi seguire la guida rapida per il deployment di un servizio web dal codice sorgente per iniziare.
Attiva Cloud Shell
- Nella console Cloud, fai clic su Attiva Cloud Shell
.

Se è la prima volta che avvii Cloud Shell, viene visualizzata una schermata intermedia che ne descrive le funzionalità. Se è stata visualizzata una schermata intermedia, fai clic su Continua.

Bastano pochi istanti per eseguire il provisioning e connettersi a Cloud Shell.

Questa macchina virtuale è caricata con tutti gli strumenti di sviluppo necessari. Offre una home directory permanente da 5 GB e viene eseguita in Google Cloud, migliorando notevolmente le prestazioni e l'autenticazione della rete. Gran parte del lavoro per questo codelab, se non tutto, può essere svolto con un browser.
Una volta eseguita la connessione a Cloud Shell, dovresti vedere che il tuo account è autenticato e il progetto è impostato sul tuo ID progetto.
- Esegui questo comando in Cloud Shell per verificare che l'account sia autenticato:
gcloud auth list
Output comando
Credentialed Accounts
ACTIVE ACCOUNT
* <my_account>@<my_domain.com>
To set the active account, run:
$ gcloud config set account `ACCOUNT`
- Esegui questo comando in Cloud Shell per verificare che il comando gcloud conosca il tuo progetto:
gcloud config list project
Output comando
[core] project = <PROJECT_ID>
In caso contrario, puoi impostarlo con questo comando:
gcloud config set project <PROJECT_ID>
Output comando
Updated property [core/project].
3. Abilita le API e imposta le variabili di ambiente
Abilita API
Prima di poter iniziare a utilizzare questo codelab, devi abilitare diverse API. Questo codelab richiede l'utilizzo delle seguenti API. Puoi abilitare queste API eseguendo il seguente comando:
gcloud services enable run.googleapis.com \
cloudbuild.googleapis.com \
aiplatform.googleapis.com \
secretmanager.googleapis.com
Configura le variabili di ambiente
Puoi impostare le variabili di ambiente che verranno utilizzate in questo codelab.
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. Crea e configura un progetto Firebase
- Nella console Firebase, fai clic su Aggiungi progetto.
- Inserisci <YOUR_PROJECT_ID> per aggiungere Firebase a uno dei tuoi progetti Google Cloud esistenti
- Se richiesto, leggi e accetta i Termini di Firebase.
- Fai clic su Continua.
- Fai clic su Conferma piano per confermare il piano di fatturazione Firebase.
- L'abilitazione di Google Analytics per questo codelab è facoltativa.
- Fai clic su Aggiungi Firebase.
- Una volta creato il progetto, fai clic su Continua.
- Nel menu Build, fai clic su Database Firestore.
- Fai clic su Crea database.
- Scegli la tua regione dal menu a discesa Località, poi fai clic su Avanti.
- Utilizza l'opzione predefinita Avvia in modalità di produzione, quindi fai clic su Crea.
5. Crea un account di servizio
Questo service account verrà utilizzato da Cloud Run per chiamare l'API Gemini di Vertex AI. Questo service account avrà anche le autorizzazioni per leggere e scrivere in Firestore e leggere i secret da Secret Manager.
Innanzitutto, crea il service account eseguendo questo comando:
gcloud iam service-accounts create $SERVICE_ACCOUNT \ --display-name="Cloud Run to access Vertex AI APIs"
Secondo, concedi il ruolo Utente Vertex AI al service account.
gcloud projects add-iam-policy-binding $PROJECT_ID \ --member serviceAccount:$SERVICE_ACCOUNT_ADDRESS \ --role=roles/aiplatform.user
Ora crea un secret in Secret Manager. Il servizio Cloud Run accederà a questo secret come variabile di ambiente, che viene risolta all'avvio dell'istanza. Puoi scoprire di più sui secret e su Cloud Run.
gcloud secrets create $SECRET_ID --replication-policy="automatic" printf "keyboard-cat" | gcloud secrets versions add $SECRET_ID --data-file=-
e concedi al service account l'accesso al secret della sessione rapida in Secret Manager.
gcloud secrets add-iam-policy-binding $SECRET_ID \
--member serviceAccount:$SERVICE_ACCOUNT_ADDRESS \
--role='roles/secretmanager.secretAccessor'
Infine, concedi al service account l'accesso in lettura e scrittura a Firestore.
gcloud projects add-iam-policy-binding $PROJECT_ID \ --member serviceAccount:$SERVICE_ACCOUNT_ADDRESS \ --role=roles/datastore.user
6. Crea il servizio Cloud Run
Innanzitutto, crea una directory per il codice sorgente e accedi tramite cd.
mkdir chat-with-gemini && cd chat-with-gemini
Quindi, crea un file package.json con il seguente contenuto:
{
"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"
}
}
Successivamente, crea un file di origine app.js con il seguente contenuto. Questo file contiene il punto di ingresso del servizio e la logica principale dell'app.
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();
});
Crea il file tailwind.config.js per tailwindCSS.
/** @type {import('tailwindcss').Config} */
module.exports = {
content: ["./**/*.{html,js}"],
theme: {
extend: {}
},
plugins: []
};
Crea il file metadataService.js per ottenere l'ID progetto e la regione per il servizio Cloud Run di cui è stato eseguito il deployment. Questi valori verranno utilizzati per creare un'istanza delle librerie client di 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;
}
};
Crea un file denominato 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>`;
Infine, crea un file input.css per tailwindCSS.
@tailwind base; @tailwind components; @tailwind utilities;
Ora crea una nuova directory public.
mkdir public cd public
All'interno di questa directory pubblica, crea il file index.html per il front-end, che utilizzerà 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. Esegui il servizio localmente
Innanzitutto, assicurati di trovarti nella directory principale chat-with-gemini del codelab.
cd .. && pwd
Successivamente, installa le dipendenze eseguendo il seguente comando:
npm install
Utilizzo di ADC durante l'esecuzione locale
Se esegui Cloud Shell, la macchina virtuale Google Compute Engine è già in esecuzione. Le credenziali associate a questa macchina virtuale (come mostrato dall'esecuzione di gcloud auth list) verranno utilizzate automaticamente dalle credenziali predefinite dell'applicazione, quindi non è necessario utilizzare il comando gcloud auth application-default login. Puoi passare direttamente alla sezione Creare un secret di sessione locale.
Tuttavia, se esegui l'operazione sul terminale locale (ovvero non in Cloud Shell), devi utilizzare le Credenziali predefinite dell'applicazione per l'autenticazione alle API di Google. Puoi 1) accedere utilizzando le tue credenziali (a condizione che tu disponga dei ruoli Utente Vertex AI e Utente Datastore) oppure 2) accedere rappresentando il service account utilizzato in questo codelab.
Opzione 1: utilizzo delle credenziali per le Credenziali predefinite dell'applicazione
Se vuoi utilizzare le tue credenziali, puoi prima eseguire gcloud auth list per verificare la tua autenticazione in gcloud. Successivamente, potrebbe essere necessario concedere all'identità il ruolo Utente Vertex AI. Se la tua identità ha il ruolo Proprietario, disponi già di questo ruolo utente Vertex AI. In caso contrario, puoi eseguire questo comando per concedere alla tua identità il ruolo Utente Vertex AI e il ruolo Utente 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
Quindi esegui questo comando
gcloud auth application-default login
Opzione 2: simulare l'identità di un service account per ADC
Se vuoi utilizzare il service account creato in questo codelab, il tuo account utente deve disporre del ruolo Creatore token service account. Puoi ottenere questo ruolo eseguendo il seguente comando:
gcloud projects add-iam-policy-binding $PROJECT_ID \ --member user:$USER \ --role=roles/iam.serviceAccountTokenCreator
Successivamente, esegui questo comando per utilizzare ADC con l'account di servizio
gcloud auth application-default login --impersonate-service-account=$SERVICE_ACCOUNT_ADDRESS
Creare un secret di sessione locale
Ora crea un secret di sessione locale per lo sviluppo locale.
export SESSION_SECRET=local-secret
Esegui l'app localmente
Infine, puoi avviare l'app eseguendo il seguente script. Questo script genererà anche il file output.css da tailwindCSS.
npm run dev
Puoi visualizzare l'anteprima del sito web aprendo il pulsante Anteprima web e selezionando Anteprima porta 8080.

8. Esegui il deployment del servizio
Innanzitutto, esegui questo comando per avviare il deployment e specificare il service account da utilizzare. Se non viene specificato un service account, viene utilizzato il service account di Compute predefinito.
gcloud run deploy $SERVICE \ --service-account $SERVICE_ACCOUNT_ADDRESS \ --source . \ --region $REGION \ --allow-unauthenticated \ --set-secrets="SESSION_SECRET=$(echo $SECRET_ID):1"
Se viene visualizzato il messaggio "Il deployment dall'origine richiede un repository Docker di Artifact Registry per archiviare i container creati. Verrà creato un repository denominato [cloud-run-source-deploy] nella regione [us-central1].", premi "y" per accettare e continuare.
9. Testare il servizio
Una volta eseguito il deployment, apri l'URL del servizio nel browser web. Poi, chiedi a Gemini una domanda, ad esempio "Suono la chitarra, ma sono anche un ingegnere informatico. Quando vedo "Do diesis", devo considerarlo un linguaggio di programmazione o una nota musicale? Quale devo scegliere?"
10. Complimenti!
Congratulazioni per aver completato il codelab.
Ti consigliamo di consultare la documentazione Cloud Run e API Gemini di Vertex AI.
Argomenti trattati
- Come utilizzare htmx, tailwindcss ed express.js per creare un servizio Cloud Run
- Come utilizzare le librerie client di Vertex AI per l'autenticazione alle API di Google
- Come creare un chatbot per interagire con il modello Gemini
- Come eseguire il deployment in un servizio Cloud Run senza un file Docker
- Come utilizzare un archivio di sessioni Express supportato da Google Cloud Firestore
11. Esegui la pulizia
Per evitare addebiti involontari (ad esempio, se i servizi Cloud Run vengono richiamati inavvertitamente più volte rispetto all'allocazione mensile di chiamate di Cloud Run nel livello senza costi), puoi eliminare Cloud Run o il progetto che hai creato nel passaggio 2.
Per eliminare il servizio Cloud Run, vai alla console Cloud Run all'indirizzo https://console.cloud.google.com/run ed elimina il servizio chat-with-gemini. Potresti anche voler eliminare il service account vertex-ai-caller o revocare il ruolo Utente Vertex AI per evitare chiamate involontarie a Gemini.
Se scegli di eliminare l'intero progetto, puoi andare alla pagina https://console.cloud.google.com/cloud-resource-manager, selezionare il progetto che hai creato nel passaggio 2 e scegliere Elimina. Se elimini il progetto, dovrai cambiare progetto in Cloud SDK. Puoi visualizzare l'elenco di tutti i progetti disponibili eseguendo gcloud projects list.