1. Introduzione
Panoramica
In questo codelab, vedrai come creare un bot di chat di base scritto nel nodo utilizzando l'API Gemini di Vertex AI e la libreria client di Vertex AI. Questa app utilizza un archivio di sessioni Express supportato da Google Cloud Firestore.
Cosa imparerai a fare
- Utilizzare htmx, tailwindcss ed express.js per creare un servizio Cloud Run
- Come utilizzare le librerie client di Vertex AI per l'autenticazione nelle 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 alla console Cloud.
- Hai già eseguito il deployment di un servizio Cloud Run. Ad esempio, per iniziare, puoi seguire la guida rapida al deployment di un servizio web dal codice sorgente.
Attiva Cloud Shell
- Dalla console Cloud, fai clic su Attiva Cloud Shell .
Se è la prima volta che avvii Cloud Shell, ti verrà mostrata una schermata intermedia che descrive di cosa si tratta. Se ti è stata presentata una schermata intermedia, fai clic su Continua.
Il provisioning e la connessione a Cloud Shell dovrebbero richiedere solo qualche istante.
Questa macchina virtuale viene 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 di rete e l'autenticazione. Gran parte, se non tutto, del lavoro in questo codelab può essere svolto con un browser.
Una volta stabilita la connessione a Cloud Shell, dovresti vedere che hai eseguito l'autenticazione e che 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 confermare che il comando gcloud è a conoscenza del 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, ci sono diverse API che dovrai abilitare. Questo codelab richiede l'utilizzo delle API seguenti. Puoi abilitare queste API eseguendo questo 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.
- Attivare Google Analytics per questo codelab è facoltativo.
- Fai clic su Aggiungi Firebase.
- Una volta creato il progetto, fai clic su Continua.
- Dal menu Crea, fai clic su Database Firestore.
- Fai clic su Crea database.
- Scegli la tua regione dall'elenco a discesa Località, poi fai clic su Avanti.
- Utilizza l'impostazione predefinita Avvia in modalità di produzione, quindi fai clic su Crea.
5. Crea un account di servizio
Questo account di servizio verrà utilizzato da Cloud Run per chiamare l'API Gemini di Vertex AI. Questo account di servizio avrà anche le autorizzazioni per leggere e scrivere su Firestore e leggere i secret da Secret Manager.
Innanzitutto, crea l'account di servizio eseguendo questo comando:
gcloud iam service-accounts create $SERVICE_ACCOUNT \ --display-name="Cloud Run to access Vertex AI APIs"
Quindi, concedi il ruolo Utente Vertex AI all'account di servizio.
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 risolto al momento dell'avvio dell'istanza. Scopri di più su secret e Cloud Run.
gcloud secrets create $SECRET_ID --replication-policy="automatic" printf "keyboard-cat" | gcloud secrets versions add $SECRET_ID --data-file=-
Concedi all'account di servizio l'accesso al secret di sessione esplicito in Secret Manager.
gcloud secrets add-iam-policy-binding $SECRET_ID \ --member serviceAccount:$SERVICE_ACCOUNT_ADDRESS \ --role='roles/secretmanager.secretAccessor'
Infine, concedi all'account di servizio 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
Per prima cosa, crea una directory per il codice sorgente e accedi a quella directory.
mkdir chat-with-gemini && cd chat-with-gemini
Quindi, crea un file package.json
con i seguenti contenuti:
{ "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" } }
A questo punto, crea un file di origine app.js
con i contenuti seguenti. 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
E all'interno della directory pubblica, crea il file index.html
per il front-end, che utilizzerà il formato 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 in locale
Innanzitutto, assicurati di essere nella directory principale chat-with-gemini
del codelab.
cd .. && pwd
Quindi, installa le dipendenze eseguendo questo comando:
npm install
Utilizzo di ADC durante l'esecuzione in locale
Se l'esecuzione è in Cloud Shell, significa che è già in esecuzione su una macchina virtuale Google Compute Engine. Le tue 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 usare il comando gcloud auth application-default login
. Puoi passare direttamente alla sezione Creare un secret di sessione locale
Se invece è in esecuzione sul terminale locale (ovvero non in Cloud Shell), dovrai utilizzare le Credenziali predefinite dell'applicazione per autenticarti alle API di Google. Puoi 1) accedere utilizzando le tue credenziali (a condizione che tu disponga di entrambi i ruoli Utente Vertex AI e Utente Datastore) o 2) accedere impersonando l'account di servizio utilizzato in questo codelab.
Opzione 1) Utilizzare le tue credenziali per ADC
Se vuoi utilizzare le tue credenziali, puoi prima eseguire gcloud auth list
per verificare il modo in cui hai eseguito l'autenticazione in gcloud. Successivamente, potresti dover concedere alla tua identità il ruolo Vertex AI User. Se la tua identità ha il ruolo Proprietario, hai già 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
Poi esegui questo comando
gcloud auth application-default login
Opzione 2) Furto d'identità di un account di servizio per ADC
Se vuoi utilizzare l'account di servizio creato in questo codelab, il tuo account utente dovrà avere il ruolo Creatore token account di servizio. Puoi ottenere questo ruolo eseguendo questo comando:
gcloud projects add-iam-policy-binding $PROJECT_ID \ --member user:$USER \ --role=roles/iam.serviceAccountTokenCreator
Successivamente, eseguirai questo comando per utilizzare ADC con l'account di servizio
gcloud auth application-default login --impersonate-service-account=$SERVICE_ACCOUNT_ADDRESS
Crea 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 lo script riportato di seguito. 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 la porta di anteprima 8080
8. Esegui il deployment del servizio
Innanzitutto, esegui questo comando per avviare il deployment e specifica l'account di servizio da utilizzare. Se non viene specificato un account di servizio, viene utilizzato l'account di servizio 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 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. Prova il servizio
Una volta eseguito il deployment, apri l'URL del servizio nel browser web. Quindi fai una domanda a Gemini, ad esempio "Faccio pratica con la chitarra, ma sono anche ingegnere del software. Quando vedo "Do#", devo pensarlo a un linguaggio di programmazione o a una nota musicale? Quale dovrei scegliere?"
10. Complimenti!
Complimenti per aver completato il codelab.
Ti consigliamo di consultare la documentazione Cloud Run e le API Gemini di Vertex AI.
Argomenti trattati
- Utilizzare htmx, tailwindcss ed express.js per creare un servizio Cloud Run
- Come utilizzare le librerie client di Vertex AI per l'autenticazione nelle API di Google
- Come creare un bot di chat 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 inavvertitamente richiamati più volte rispetto all'allocazione mensile dei callout Cloud Run nel livello senza costi, puoi eliminare Cloud Run o eliminare il progetto che hai creato nel passaggio 2.
Per eliminare il servizio Cloud Run, vai alla console Cloud di Cloud Run all'indirizzo https://console.cloud.google.com/run ed elimina il servizio chat-with-gemini
. Potresti anche voler eliminare l'account di servizio vertex-ai-caller
o revocare il ruolo Vertex AI User per evitare chiamate involontarie a Gemini.
Se scegli di eliminare l'intero progetto, puoi andare all'indirizzo 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 modificarli in Cloud SDK. Puoi visualizzare l'elenco di tutti i progetti disponibili eseguendo gcloud projects list
.