1. Einführung
Übersicht
In diesem Codelab erfahren Sie, wie Sie Gemini mithilfe einer neuen Funktion namens Funktionsaufrufe Zugriff auf Echtzeitdaten geben. Um Echtzeitdaten zu simulieren, erstellen Sie einen Wetterdienst-Endpunkt, der das aktuelle Wetter für zwei Standorte zurückgibt. Anschließend erstellen Sie eine Chat-App auf Basis von Gemini, die mithilfe von Funktionsaufrufen das aktuelle Wetter abruft.
Sehen wir uns die Funktionsaufrufe kurz an.
- Der Prompt fragt nach aktuellen Wetterstandorten an einem bestimmten Standort.
- Dieser Prompt und der Funktionsvertrag für getWeather() werden an Gemini gesendet
- Gemini bittet darum, dass die Chatbot-App „getWeather(Seattle)“ aufruft in seinem Namen
- Die App sendet die Ergebnisse (40 Grad F und Regen)
- Gemini sendet die Ergebnisse an den Anrufer
Zur Erinnerung: Gemini ruft die Funktion nicht auf. Sie als Entwickler müssen die Funktion aufrufen und die Ergebnisse an Gemini zurücksenden.
Aufgaben in diesem Lab
- So funktionieren Gemini-Funktionsaufrufe
- Eine Gemini-basierte Chatbot-App als Cloud Run-Dienst bereitstellen
2. Einrichtung und Anforderungen
Voraussetzungen
- Sie sind in der Cloud Console angemeldet.
- Sie haben zuvor eine Funktion der 2. Generation bereitgestellt. Für den Einstieg können Sie beispielsweise der Kurzanleitung zur Bereitstellung von Cloud Functions (2nd gen) folgen.
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. Umgebungsvariablen einrichten und APIs aktivieren
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> WEATHER_SERVICE=weatherservice FRONTEND=frontend SERVICE_ACCOUNT="vertex-ai-caller" SERVICE_ACCOUNT_ADDRESS=$SERVICE_ACCOUNT@$PROJECT_ID.iam.gserviceaccount.com
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
4. Dienstkonto zum Aufrufen von Vertex AI erstellen
Dieses Dienstkonto wird von Cloud Run zum Aufrufen der Vertex AI Gemini API verwendet.
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
5. Cloud Run-Back-End-Dienst erstellen
Erstellen Sie zunächst ein Verzeichnis für den Quellcode und speichern Sie das Verzeichnis mit cd.
mkdir -p gemini-function-calling/weatherservice gemini-function-calling/frontend && cd gemini-function-calling/weatherservice
Erstellen Sie dann eine package.json
-Datei mit folgendem Inhalt:
{ "name": "weatherservice", "version": "1.0.0", "description": "", "main": "app.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1" }, "keywords": [], "author": "", "license": "ISC", "dependencies": { "express": "^4.18.3" } }
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.get("/getweather", (req, res) => { const location = req.query.location; let temp, conditions; if (location == "New Orleans") { temp = 99; conditions = "hot and humid"; } else if (location == "Seattle") { temp = 40; conditions = "rainy and overcast"; } else { res.status(400).send("there is no data for the requested location"); } res.json({ weather: temp, location: location, conditions: conditions }); }); const port = parseInt(process.env.PORT) || 8080; app.listen(port, () => { console.log(`weather service: listening on port ${port}`); }); app.get("/", (req, res) => { res.send("welcome to hard-coded weather!"); });
Wetterdienst bereitstellen
Mit diesem Befehl können Sie den Wetterdienst bereitstellen.
gcloud run deploy $WEATHER_SERVICE \ --source . \ --region $REGION \ --allow-unauthenticated
Wetterdienst testen
Sie können das Wetter an den beiden Standorten mit curl überprüfen:
WEATHER_SERVICE_URL=$(gcloud run services describe $WEATHER_SERVICE \ --platform managed \ --region=$REGION \ --format='value(status.url)') curl $WEATHER_SERVICE_URL/getweather?location=Seattle curl $WEATHER_SERVICE_URL/getweather?location\=New%20Orleans
In Seattle liegt die Temperatur bei 40 °C und es ist regnerisch, in New Orleans sind es 37 °C und es ist immer feucht.
6. Front-End-Dienst erstellen
Fügen Sie zuerst „cd“ in das Front-End-Verzeichnis ein.
cd gemini-function-calling/frontend
Erstellen Sie dann eine package.json
-Datei mit folgendem Inhalt:
{ "name": "demo1", "version": "1.0.0", "description": "", "main": "index.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/vertexai": "^0.4.0", "axios": "^1.6.7", "express": "^4.18.2", "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"); const expressWs = require("express-ws")(app); app.use(express.static("public")); const { VertexAI, FunctionDeclarationSchemaType } = require("@google-cloud/vertexai"); // get project and location from metadata service const metadataService = require("./metadataService.js"); // instance of Gemini model let generativeModel; // 1: define the function const functionDeclarations = [ { function_declarations: [ { name: "getweather", description: "get weather for a given location", parameters: { type: FunctionDeclarationSchemaType.OBJECT, properties: { location: { type: FunctionDeclarationSchemaType.STRING }, degrees: { type: FunctionDeclarationSchemaType.NUMBER, "description": "current temperature in fahrenheit" }, conditions: { type: FunctionDeclarationSchemaType.STRING, "description": "how the weather feels subjectively" } }, required: ["location"] } } ] } ]; // on instance startup const port = parseInt(process.env.PORT) || 8080; app.listen(port, async () => { console.log(`demo1: listening on port ${port}`); 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" }); }); const axios = require("axios"); const baseUrl = "https://weatherservice-k6msmyp47q-uc.a.run.app"; app.ws("/sendMessage", async function (ws, req) { // this chat history will be pinned to the current // Cloud Run instance. Consider using Firestore & // Firebase anonymous auth instead. // start ephemeral chat session with Gemini const chatWithModel = generativeModel.startChat({ tools: functionDeclarations }); ws.on("message", async function (message) { 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); // Function calling demo let response1 = await results.response; let data = response1.candidates[0].content.parts[0]; let methodToCall = data.functionCall; if (methodToCall === undefined) { console.log("Gemini says: ", data.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"> ${data.text} </div>`); // bail out - Gemini doesn't want to return a function return; } // otherwise Gemini wants to call a function console.log( "Gemini wants to call: " + methodToCall.name + " with args: " + util.inspect(methodToCall.args, { showHidden: false, depth: null, colors: true }) ); // make the external call let jsonReturned; try { const responseFunctionCalling = await axios.get( baseUrl + "/" + methodToCall.name, { params: { location: methodToCall.args.location } } ); jsonReturned = responseFunctionCalling.data; } catch (ex) { // in case an invalid location was provided jsonReturned = ex.response.data; } console.log("jsonReturned: ", jsonReturned); // tell the model what function we just called const functionResponseParts = [ { functionResponse: { name: methodToCall.name, response: { name: methodToCall.name, content: { jsonReturned } } } } ]; // // Send a follow up message with a FunctionResponse const result2 = await chatWithModel.sendMessage( functionResponseParts ); // This should include a text response from the model using the response content // provided above const response2 = await result2.response; let answer = response2.candidates[0].content.parts[0].text; console.log("answer: ", answer); 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>`); }); ws.on("close", () => { console.log("WebSocket was closed"); }); }); function sleep(ms) { return new Promise((resolve) => { setTimeout(resolve, ms); }); }
Erstellen Sie eine input.css
-Datei für tailwindCSS.
@tailwind base; @tailwind components; @tailwind utilities;
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 { // 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 { // 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 ein neues public
-Verzeichnis.
mkdir public cd public
Erstellen Sie nun 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 2</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 > What's is the current weather in Seattle?</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. Front-End-Dienst lokal ausführen
Prüfen Sie zuerst, ob Sie sich im Verzeichnis frontend
für Ihr Codelab befinden.
cd .. && pwd
Installieren Sie anschließend die Abhängigkeiten, indem Sie den folgenden Befehl ausführen:
npm install
ADC bei lokaler Ausführung verwenden
Wenn die Ausführung in Cloud Shell erfolgt, ist bereits eine virtuelle Maschine der Google Compute Engine vorhanden. 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 zum Abschnitt Anwendung lokal ausführen wechseln.
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
Anwendung lokal ausführen
Schließlich können Sie die Anwendung starten, indem Sie das folgende Skript ausführen. Das Entwicklerskript 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. Front-End-Dienst bereitstellen und testen
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 $FRONTEND \ --service-account $SERVICE_ACCOUNT_ADDRESS \ --source . \ --region $REGION \ --allow-unauthenticated
Öffnen Sie die Dienst-URL für das Front-End in Ihrem Browser. Stellen Sie die Frage: „Wie ist das aktuelle Wetter in München?“ und Gemini sollte antworten: „Es ist momentan 40 Grad und regnerisch.“ Wenn Sie fragen: „Wie ist das aktuelle Wetter in Boston?“ Gemini antwortet mit „Ich kann diese Anfrage nicht ausführen. Die verfügbare Wetter-API hat keine Daten für Boston."
9. Glückwunsch!
Herzlichen Glückwunsch zum Abschluss des Codelabs!
Weitere Informationen finden Sie in der Dokumentation zu Cloud Run, Gemini APIs in Vertex AI und Funktionsaufrufen.
Behandelte Themen
- So funktionieren Gemini-Funktionsaufrufe
- Eine Gemini-basierte Chatbot-App als Cloud Run-Dienst bereitstellen
10. Bereinigen
Um versehentliche Gebühren zu vermeiden, z. B. wenn diese Cloud Run-Dienstleistung versehentlich mehr Mal aufgerufen wird als Ihre monatliche Zuweisung von Cloud Run-Aufrufen in der kostenlosen Stufe, können Sie entweder den Cloud Run-Dienst oder das in Schritt 2 erstellte Projekt löschen.
Wenn Sie die Cloud Run-Dienste löschen möchten, rufen Sie die Cloud Run-Cloud Console unter https://console.cloud.google.com/functions/ auf und löschen Sie die $PROPERTY_SERVICE- und $FRONTEND-Dienste, die Sie in diesem Codelab erstellt haben.
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.