1. Einführung
Übersicht
In diesem Codelab erfahren Sie, wie Sie mithilfe der Gemini API von Vertex AI und der Vertex AI-Clientbibliothek einen einfachen Chatbot erstellen, der in Knoten geschrieben ist. Diese Anwendung verwendet einen Express-Sitzungsspeicher, der von Google Cloud Firestore unterstützt wird.
Aufgaben in diesem Lab
- Mit htmx, tailwindcss und express.js einen Cloud Run-Dienst erstellen
- Mit Vertex AI-Clientbibliotheken bei Google APIs authentifizieren
- Einen Chatbot für die Interaktion mit dem Gemini-Modell erstellen
- In einem Cloud Run-Dienst ohne Dockerfile bereitstellen
- So verwenden Sie einen Express-Sitzungsspeicher, der von Google Cloud Firestore unterstützt wird
2. Einrichtung und Anforderungen
Voraussetzungen
- Sie sind in der Cloud Console angemeldet.
- Sie haben zuvor einen Cloud Run-Dienst bereitgestellt. Weitere Informationen finden Sie beispielsweise in der Kurzanleitung: Webdienst aus dem Quellcode bereitstellen.
Cloud Shell aktivieren
- Klicken Sie in der Cloud Console auf Cloud Shell aktivieren .
Wenn Sie Cloud Shell zum ersten Mal starten, wird ein Zwischenbildschirm mit einer Beschreibung der Funktion angezeigt. Wenn ein Zwischenbildschirm angezeigt wird, klicken Sie auf Weiter.
Die Bereitstellung und Verbindung mit Cloud Shell dauert nur einen Moment.
Diese virtuelle Maschine verfügt über alle erforderlichen Entwicklertools. Es bietet ein Basisverzeichnis mit 5 GB nichtflüchtigem Speicher und wird in Google Cloud ausgeführt. Dadurch werden die Netzwerkleistung und die Authentifizierung erheblich verbessert. Viele, wenn nicht sogar alle Arbeiten in diesem Codelab können mit einem Browser erledigt werden.
Sobald Sie mit Cloud Shell verbunden sind, sollten Sie sehen, dass Sie authentifiziert sind und das Projekt auf Ihre Projekt-ID eingestellt ist.
- Führen Sie in 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 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 dieses Codelab verwenden können, müssen Sie mehrere APIs aktivieren. Für dieses Codelab müssen die folgenden APIs verwendet werden. Sie können diese APIs aktivieren, indem Sie den folgenden Befehl ausführen:
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.
- <YOUR_PROJECT_ID> eingeben um Firebase einem vorhandenen Google Cloud-Projekt hinzuzufügen
- Lesen und akzeptieren Sie die Nutzungsbedingungen von Firebase, wenn Sie dazu aufgefordert werden.
- Klicken Sie auf Weiter.
- Klicken Sie auf Tarif bestätigen, um den Firebase-Tarif zu bestätigen.
- Die Aktivierung von Google Analytics für dieses Codelab ist optional.
- Klicken Sie auf Firebase hinzufügen.
- Wenn das Projekt erstellt wurde, klicken Sie auf Weiter.
- Klicken Sie im Menü Build auf Firestore-Datenbank.
- Klicken Sie auf Datenbank erstellen.
- Wählen Sie Ihre Region aus dem Drop-down-Menü Standort aus und klicken Sie auf Weiter.
- Verwenden Sie die Standardeinstellung Im Produktionsmodus starten und klicken Sie dann auf Erstellen.
5. Dienstkonto erstellen
Dieses Dienstkonto wird von Cloud Run zum Aufrufen der Gemini API von Vertex AI verwendet. Dieses Dienstkonto hat außerdem Berechtigungen zum Lesen und Schreiben in Firestore sowie zum Lesen von Secrets aus Secret Manager.
Erstellen Sie zuerst das Dienstkonto, indem Sie den folgenden Befehl ausführen:
gcloud iam service-accounts create $SERVICE_ACCOUNT \ --display-name="Cloud Run to access Vertex AI APIs"
Weisen Sie dann dem Dienstkonto 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 jetzt ein Secret in Secret Manager. Der Cloud Run-Dienst greift als Umgebungsvariablen auf dieses Secret zu. Dies wird beim Starten der Instanz aufgelöst. 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 Express-Sitzungs-Secret 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 zuletzt 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 zunächst ein Verzeichnis für den Quellcode und speichern Sie das Verzeichnis mit cd.
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 folgendem Inhalt. Diese Datei enthält den Einstiegspunkt für den Dienst und die Hauptlogik für die Anwendung.
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 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; } };
Datei mit dem Namen „spinnerSvg.js
“ erstellen
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 zuletzt eine input.css
-Datei für tailwindCSS.
@tailwind base; @tailwind components; @tailwind utilities;
Erstellen Sie jetzt ein neues public
-Verzeichnis.
mkdir public cd public
Erstellen Sie in diesem öffentlichen Verzeichnis die Datei index.html
für das Front-End, die 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, indem Sie den folgenden Befehl ausführen:
npm install
ADC bei lokaler Ausführung verwenden
Wenn Sie Cloud Shell nutzen, nutzen Sie bereits eine virtuelle Google Compute Engine-Maschine. Ihre Anmeldedaten, die dieser virtuellen Maschine zugeordnet sind (wie beim Ausführen von gcloud auth list
gezeigt), werden automatisch von Standardanmeldedaten für Anwendungen verwendet, daher ist die Verwendung des Befehls gcloud auth application-default login
nicht erforderlich. Sie können direkt mit dem Abschnitt Lokales Sitzungs-Secret erstellen fortfahren.
Wenn Sie die Anwendung jedoch auf Ihrem lokalen Terminal ausführen (d.h. nicht in Cloud Shell), müssen Sie die Standardanmeldedaten für Anwendungen zur Authentifizierung bei Google APIs verwenden. Sie können sich entweder 1) mit Ihren Anmeldedaten anmelden (sofern Sie sowohl die Vertex AI-Nutzer- als auch die Datastore-Nutzerrolle haben) oder 2) sich als das in diesem Codelab verwendete Dienstkonto anmelden.
Option 1) 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 Vertex AI-Nutzerrolle bereits. Falls nicht, können Sie diesen Befehl ausführen, um die Vertex AI-Nutzerrolle und die Datastore-Nutzerrolle zu 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ät eines Dienstkontos für ADC übernehmen
Wenn Sie das in diesem Codelab erstellte Dienstkonto verwenden möchten, muss Ihr Nutzerkonto die Rolle Ersteller von Dienstkonto-Tokens 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 Anwendung starten, indem Sie das folgende Skript ausführen. Das Skript generiert außerdem die Datei „output.css“ aus tailwindCSS.
npm run dev
Sie können eine Vorschau der Website anzeigen, indem Sie die Schaltfläche „Webvorschau“ öffnen und „Vorschauport 8080“ auswählen
8. Dienst bereitstellen
Führen Sie zuerst diesen Befehl aus, um die Bereitstellung zu starten, und geben Sie das zu verwendende Dienstkonto an. Wenn kein Dienstkonto angegeben ist, wird das Compute-Standarddienstkonto verwendet.
gcloud run deploy $SERVICE \ --service-account $SERVICE_ACCOUNT_ADDRESS \ --source . \ --region $REGION \ --allow-unauthenticated \ --set-secrets="SESSION_SECRET=$(echo $SECRET_ID):1"
Wenn die Meldung „Für die Bereitstellung aus der Quelle ist ein Artifact Registry-Docker-Repository erforderlich, um erstellte Container zu speichern. Ein Repository mit dem Namen [cloud-run-source-deploy] in der Region [us-central1] wird erstellt.“, drücken Sie auf „y“. akzeptieren und fortfahren.
9. Dienst testen
Öffnen Sie nach der Bereitstellung die Dienst-URL in Ihrem Webbrowser. Stellen Sie Gemini dann eine Frage, z.B. „Ich praktiziere Gitarre, bin aber auch Softwareingenieurin. Wenn ich „C#“ sehe, sollte ich es mir dann als Programmiersprache oder als Musiknote vorstellen? Welche soll ich auswählen?“
10. Glückwunsch!
Herzlichen Glückwunsch zum Abschluss des Codelabs!
Weitere Informationen finden Sie in der Dokumentation zu Cloud Run und Gemini APIs in Vertex AI.
Behandelte Themen
- Mit htmx, tailwindcss und express.js einen Cloud Run-Dienst erstellen
- Mit Vertex AI-Clientbibliotheken bei Google APIs authentifizieren
- Einen Chatbot erstellen, um mit dem Gemini-Modell zu interagieren
- In einem Cloud Run-Dienst ohne Dockerfile bereitstellen
- So verwenden Sie einen Express-Sitzungsspeicher, der von Google Cloud Firestore unterstützt wird
11. Bereinigen
Um versehentliche Gebühren zu vermeiden, z. B. wenn die Cloud Run-Dienste versehentlich häufiger aufgerufen werden als Ihre monatliche Zuweisung von Cloud Run-Aufrufen in der kostenlosen Stufe, können Sie entweder Cloud Run 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 sollten auch das Dienstkonto „vertex-ai-caller
“ löschen oder die Rolle „Vertex AI User“ widerrufen, um unbeabsichtigte Aufrufe an 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 klicken Sie auf „Löschen“. 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.