1. Einführung
Übersicht
In diesem Codelab erfahren Sie, wie Sie einen einfachen Chatbot in Node.js mit der Vertex AI Gemini API und der Vertex AI-Clientbibliothek erstellen. Diese App verwendet einen Express-Sitzungsspeicher, der von Google Cloud Firestore unterstützt wird.
Lerninhalte
- Verwendung von htmx, tailwindcss und express.js zum Erstellen eines Cloud Run-Dienstes
- Verwendung der Vertex AI-Clientbibliotheken zur Authentifizierung bei Google APIs
- Erstellen eines Chatbots zur Interaktion mit dem Gemini-Modell
- Bereitstellung in einem Cloud Run-Dienst ohne Docker-Datei
- Verwendung eines Express-Sitzungsspeichers, der von Google Cloud Firestore unterstützt wird
2. Einrichtung und Anforderungen
Voraussetzungen
- Sie sind in der Cloud Console angemeldet.
- Sie haben bereits einen Cloud Run-Dienst bereitgestellt. Sie können beispielsweise der Kurzanleitung zum Bereitstellen eines Webdienstes aus Quellcode folgen, um loszulegen.
Cloud Shell aktivieren
- Klicken Sie in der Cloud Console auf Cloud Shell aktivieren
.

Wenn Sie die Cloud Shell zum ersten Mal starten, wird ein Fenster mit einer Beschreibung eingeblendet. Klicken Sie in diesem Fall einfach auf Weiter.

Das Herstellen der Verbindung mit der Cloud Shell sollte nur wenige Augenblicke dauern.

Diese virtuelle Maschine ist mit allen erforderlichen Entwicklungstools ausgestattet. Sie bietet ein persistentes 5-GB-Homeverzeichnis und wird in Google Cloud ausgeführt, wodurch die Netzwerkleistung und die Authentifizierung erheblich verbessert werden. Die meisten, wenn nicht sogar alle Aufgaben in diesem Codelab können mit einem Browser erledigt werden.
Sobald die Verbindung mit der Cloud Shell hergestellt ist, sehen Sie, dass Sie authentifiziert sind und für das Projekt schon Ihre Projekt-ID eingestellt ist.
- Führen Sie in der Cloud Shell den folgenden Befehl aus, um zu prüfen, ob Sie authentifiziert sind:
gcloud auth list
Befehlsausgabe
Credentialed Accounts
ACTIVE ACCOUNT
* <my_account>@<my_domain.com>
To set the active account, run:
$ gcloud config set account `ACCOUNT`
- Führen Sie in der Cloud Shell den folgenden Befehl aus, um zu prüfen, ob der gcloud-Befehl Ihr Projekt kennt:
gcloud config list project
Befehlsausgabe
[core] project = <PROJECT_ID>
Ist dies nicht der Fall, können Sie die Einstellung mit diesem Befehl vornehmen:
gcloud config set project <PROJECT_ID>
Befehlsausgabe
Updated property [core/project].
3. APIs aktivieren und Umgebungsvariablen festlegen
APIs aktivieren
Bevor Sie mit diesem Codelab beginnen können, müssen Sie mehrere APIs aktivieren. Für dieses Codelab müssen Sie die folgenden APIs verwenden. Sie können diese APIs mit dem folgenden Befehl aktivieren:
gcloud services enable run.googleapis.com \
cloudbuild.googleapis.com \
aiplatform.googleapis.com \
secretmanager.googleapis.com
Umgebungsvariablen einrichten
Sie können Umgebungsvariablen festlegen, die in diesem Codelab verwendet werden.
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. Firebase-Projekt erstellen und konfigurieren
- Klicken Sie in der Firebase Console auf Projekt hinzufügen.
- Geben Sie <YOUR_PROJECT_ID> ein, um Firebase zu einem Ihrer vorhandenen Google Cloud-Projekte hinzuzufügen.
- Lesen und akzeptieren Sie gegebenenfalls die Firebase-Nutzungsbedingungen.
- Klicken Sie auf Weiter.
- Klicken Sie auf Tarif bestätigen , um den Firebase-Abrechnungstarif zu bestätigen.
- Sie können Google Analytics für dieses Codelab optional aktivieren.
- Klicken Sie auf Firebase hinzufügen.
- Wenn das Projekt erstellt wurde, klicken Sie auf Weiter.
- Klicken Sie im Menü Erstellen auf Firestore-Datenbank.
- Klicken Sie auf Datenbank erstellen.
- Wählen Sie im Drop-down-Menü Standort Ihre Region aus und klicken Sie dann auf Weiter.
- Verwenden Sie den Standardmodus Im Produktionsmodus starten und klicken Sie dann auf Erstellen.
5. Dienstkonto erstellen
Dieses Dienstkonto wird von Cloud Run verwendet, um die Vertex AI Gemini API aufzurufen. Dieses Dienstkonto hat auch Berechtigungen zum Lesen und Schreiben in Firestore und zum Lesen von Secrets aus Secret Manager.
Erstellen Sie zuerst das Dienstkonto mit diesem Befehl:
gcloud iam service-accounts create $SERVICE_ACCOUNT \ --display-name="Cloud Run to access Vertex AI APIs"
Weisen Sie dem Dienstkonto als Nächstes die Rolle „Vertex AI User“ zu.
gcloud projects add-iam-policy-binding $PROJECT_ID \ --member serviceAccount:$SERVICE_ACCOUNT_ADDRESS \ --role=roles/aiplatform.user
Erstellen Sie nun ein Secret in Secret Manager. Der Cloud Run-Dienst greift auf dieses Secret als Umgebungsvariable zu, die beim Start der Instanz aufgelöst wird. Weitere Informationen zu Secrets und Cloud Run
gcloud secrets create $SECRET_ID --replication-policy="automatic" printf "keyboard-cat" | gcloud secrets versions add $SECRET_ID --data-file=-
Gewähren Sie dem Dienstkonto Zugriff auf das Secret für die Express-Sitzung in Secret Manager.
gcloud secrets add-iam-policy-binding $SECRET_ID \
--member serviceAccount:$SERVICE_ACCOUNT_ADDRESS \
--role='roles/secretmanager.secretAccessor'
Gewähren Sie dem Dienstkonto schließlich Lese- und Schreibzugriff auf Firestore.
gcloud projects add-iam-policy-binding $PROJECT_ID \ --member serviceAccount:$SERVICE_ACCOUNT_ADDRESS \ --role=roles/datastore.user
6. Cloud Run-Dienst erstellen
Erstellen Sie zuerst ein Verzeichnis für den Quellcode und wechseln Sie in dieses Verzeichnis.
mkdir chat-with-gemini && cd chat-with-gemini
Erstellen Sie dann eine package.json-Datei mit folgendem Inhalt:
{
"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"
}
}
Erstellen Sie als Nächstes eine app.js-Quelldatei mit dem folgenden Inhalt. Diese Datei enthält den Einstiegspunkt für den Dienst und die Hauptlogik für die 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();
});
Erstellen Sie die Datei tailwind.config.js für tailwindCSS.
/** @type {import('tailwindcss').Config} */
module.exports = {
content: ["./**/*.{html,js}"],
theme: {
extend: {}
},
plugins: []
};
Erstellen Sie die Datei metadataService.js, um die Projekt-ID und die Region für den bereitgestellten Cloud Run-Dienst abzurufen. Diese Werte werden verwendet, um eine Instanz der Vertex AI-Clientbibliotheken zu instanziieren.
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;
}
};
Erstellen Sie eine Datei mit dem Namen 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>`;
Erstellen Sie schließlich eine input.css-Datei für tailwindCSS.
@tailwind base; @tailwind components; @tailwind utilities;
Erstellen Sie nun ein neues Verzeichnis public.
mkdir public cd public
Erstellen Sie in diesem öffentlichen Verzeichnis die Datei index.html für das Front-End, das htmx verwendet.
<!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. Dienst lokal ausführen
Prüfen Sie zuerst, ob Sie sich im Stammverzeichnis chat-with-gemini für Ihr Codelab befinden.
cd .. && pwd
Installieren Sie als Nächstes die Abhängigkeiten mit dem folgenden Befehl:
npm install
ADC bei lokaler Ausführung verwenden
Wenn Sie in der Cloud Shell arbeiten, arbeiten Sie bereits auf einer virtuellen Google Compute Engine-Maschine. Ihre Anmeldedaten, die dieser virtuellen Maschine zugeordnet sind (wie durch Ausführen von gcloud auth list angezeigt), werden automatisch von den Standardanmeldedaten für Anwendungen verwendet. Daher ist es nicht erforderlich, den Befehl gcloud auth application-default login zu verwenden. Sie können mit dem Abschnitt Lokales Sitzungs-Secret erstellen fortfahren.
Wenn Sie jedoch in Ihrem lokalen Terminal arbeiten (d.h. nicht in der Cloud Shell), müssen Sie die Standardanmeldedaten für Anwendungen verwenden, um sich bei Google APIs zu authentifizieren. Sie können sich entweder 1) mit Ihren Anmeldedaten anmelden (vorausgesetzt, Sie haben die Rollen „Vertex AI User“ und „Datastore User“) oder 2) sich anmelden, indem Sie die Identität des in diesem Codelab verwendeten Dienstkontos übernehmen.
Option 1: Ihre Anmeldedaten für ADC verwenden
Wenn Sie Ihre Anmeldedaten verwenden möchten, können Sie zuerst gcloud auth list ausführen, um zu prüfen, wie Sie in gcloud authentifiziert sind. Als Nächstes müssen Sie Ihrer Identität möglicherweise die Rolle „Vertex AI User“ gewähren. Wenn Ihre Identität die Rolle „Inhaber“ hat, haben Sie diese Rolle „Vertex AI User“ bereits. Andernfalls können Sie mit diesem Befehl Ihrer Identität die Rolle „Vertex AI User“ und die Rolle „Datastore User“ gewähren.
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
Führen Sie dann den folgenden Befehl aus:
gcloud auth application-default login
Option 2: Identitätswechsel für ein Dienstkonto für ADC
Wenn Sie das in diesem Codelab erstellte Dienstkonto verwenden möchten, muss Ihr Nutzerkonto die Rolle „Dienstkonto-Tokenersteller“ haben. Sie können diese Rolle mit dem folgenden Befehl abrufen:
gcloud projects add-iam-policy-binding $PROJECT_ID \ --member user:$USER \ --role=roles/iam.serviceAccountTokenCreator
Führen Sie als Nächstes den folgenden Befehl aus, um ADC mit dem Dienstkonto zu verwenden:
gcloud auth application-default login --impersonate-service-account=$SERVICE_ACCOUNT_ADDRESS
Lokales Sitzungs-Secret erstellen
Erstellen Sie nun ein lokales Sitzungs-Secret für die lokale Entwicklung.
export SESSION_SECRET=local-secret
Anwendung lokal ausführen
Schließlich können Sie die App mit dem folgenden Skript starten. Dieses Skript generiert auch die Datei „output.css“ aus tailwindCSS.
npm run dev
Sie können eine Vorschau der Website aufrufen, indem Sie auf die Schaltfläche „Webvorschau“ klicken und „Vorschau auf Port 8080“ auswählen.

8. Dienst bereitstellen
Führen Sie zuerst diesen Befehl aus, um die Bereitstellung zu starten und das zu verwendende Dienstkonto anzugeben. Wenn kein Dienstkonto angegeben ist, wird das Standarddienstkonto für Compute verwendet.
gcloud run deploy $SERVICE \ --service-account $SERVICE_ACCOUNT_ADDRESS \ --source . \ --region $REGION \ --allow-unauthenticated \ --set-secrets="SESSION_SECRET=$(echo $SECRET_ID):1"
Wenn Sie aufgefordert werden, dass für die Bereitstellung aus der Quelle ein Artifact Registry Docker-Repository zum Speichern der erstellten Container erforderlich ist, wird ein Repository mit dem Namen [cloud-run-source-deploy] in der Region [us-central1] erstellt. Drücken Sie „y“, um zu akzeptieren und fortzufahren.
9. Dienst testen
Öffnen Sie nach der Bereitstellung die Dienst-URL in Ihrem Webbrowser. Stellen Sie Gemini dann eine Frage, z.B.: „Ich spiele Gitarre, bin aber auch Softwareentwickler. Wenn ich „C#“ sehe, sollte ich es als Programmiersprache oder als Musiknote betrachten? Was sollte ich wählen?“
10. Glückwunsch!
Herzlichen Glückwunsch zum Abschluss des Codelabs!
Wir empfehlen, die Dokumentation zu Cloud Run und den Vertex AI Gemini APIs zu lesen.
Behandelte Themen
- Verwendung von htmx, tailwindcss und express.js zum Erstellen eines Cloud Run-Dienstes
- Verwendung der Vertex AI-Clientbibliotheken zur Authentifizierung bei Google APIs
- Erstellen eines Chatbots zur Interaktion mit dem Gemini-Modell
- Bereitstellung in einem Cloud Run-Dienst ohne Docker-Datei
- Verwendung eines Express-Sitzungsspeichers, der von Google Cloud Firestore unterstützt wird
11. Bereinigen
Um unbeabsichtigte Kosten zu vermeiden (z. B. wenn die Cloud Run-Dienste versehentlich häufiger aufgerufen werden als Ihre monatliche Cloud Run-Aufrufzuweisung im kostenlosen Kontingent), können Sie entweder den Cloud Run-Dienst oder das in Schritt 2 erstellte Projekt löschen.
Wenn Sie den Cloud Run-Dienst löschen möchten, rufen Sie die Cloud Run Cloud Console unter https://console.cloud.google.com/run auf und löschen Sie den chat-with-gemini Dienst. Sie können auch das Dienstkonto vertex-ai-caller löschen oder die Rolle „Vertex AI User“ widerrufen, um unbeabsichtigte Aufrufe von Gemini zu vermeiden.
Wenn Sie das gesamte Projekt löschen möchten, rufen Sie https://console.cloud.google.com/cloud-resource-manager auf, wählen Sie das in Schritt 2 erstellte Projekt aus und wählen Sie „Löschen“ aus. Wenn Sie das Projekt löschen, müssen Sie die Projekte in Ihrem Cloud SDK ändern. Sie können die Liste aller verfügbaren Projekte mit dem Befehl gcloud projects list aufrufen.