Cloud Run mit Gemini-Funktionsaufrufen verwenden

1. Einführung

Übersicht

In diesem Codelab erfahren Sie, wie Sie Gemini Zugriff auf Echtzeitdaten gewähren können, indem Sie eine neue Funktion namens Funktionsaufrufe verwenden. Um Echtzeitdaten zu simulieren, erstellen Sie einen Wetterdienst-Endpunkt, der das aktuelle Wetter für zwei Orte zurückgibt. Anschließend erstellen Sie eine Chat-App, die von Gemini unterstützt wird und Funktionsaufrufe verwendet, um das aktuelle Wetter abzurufen.

Sehen wir uns kurz an, wie Funktionsaufrufe funktionieren.

  • Der Prompt fragt nach dem aktuellen Wetter an einem bestimmten Ort.
  • Dieser Prompt und der Funktionsvertrag für „getWeather()“ werden an Gemini gesendet.
  • Gemini fordert die Chatbot-App auf, in seinem Namen „getWeather(Seattle)“ aufzurufen.
  • Die App sendet die Ergebnisse zurück (4 °C und regnerisch).
  • Gemini sendet die Ergebnisse an den Aufrufer zurück.

Zusammenfassend lässt sich sagen, dass Gemini die Funktion nicht aufruft. Sie als Entwickler müssen die Funktion aufrufen und die Ergebnisse an Gemini zurücksenden.

Diagramm des Ablaufs von Funktionsaufrufen

Lerninhalte

  • Funktionsweise von Funktionsaufrufen in Gemini
  • Bereitstellen einer Gemini-basierten Chatbot-App als Cloud Run-Dienst

2. Einrichtung und Anforderungen

Voraussetzungen

  • Sie sind in der Cloud Console angemeldet.
  • Sie haben bereits eine Funktion der 2. Generation bereitgestellt. Informationen zu den ersten Schritten finden Sie beispielsweise in der Kurzanleitung zum Bereitstellen einer Cloud Functions-Funktion der 2. Generation.

Cloud Shell aktivieren

  1. Klicken Sie in der Cloud Console auf Cloud Shell aktivieren d1264ca30785e435.png.

cb81e7c8e34bc8d.png

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

d95252b003979716.png

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

7833d5e1c5d18f54.png

Diese virtuelle Maschine enthält alle Entwicklungstools, die Sie benötigen. Sie bietet ein Basisverzeichnis mit 5 GB nichtflüchtigem Speicher und läuft in Google Cloud, was die Netzwerkleistung und Authentifizierung erheblich verbessert. 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.

  1. 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`
  1. 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. 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 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

4. Dienstkonto zum Aufrufen von Vertex AI erstellen

Dieses Dienstkonto wird von Cloud Run verwendet, um die Vertex AI Gemini API aufzurufen.

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-Nutzer“ zu.

gcloud projects add-iam-policy-binding $PROJECT_ID \
  --member serviceAccount:$SERVICE_ACCOUNT_ADDRESS \
  --role=roles/aiplatform.user

5. Back-End-Cloud Run-Dienst erstellen

Erstellen Sie zuerst ein Verzeichnis für den Quellcode und wechseln Sie in dieses Verzeichnis.

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 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.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 für die beiden Orte mit „curl“ prü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 sind es 4 °C und es regnet. In New Orleans sind es immer 37 °C und es ist feucht.

6. Frontend-Dienst erstellen

Wechseln Sie zuerst in das Frontend-Verzeichnis.

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 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");

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 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 {
                // 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;
    }
};

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 ein neues Verzeichnis public.

mkdir public
cd public

Erstellen Sie nun die Datei index.html für das Frontend, in der htmx verwendet wird.

<!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&apos;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. Frontend-Dienst lokal ausführen

Sie müssen sich im Verzeichnis frontend für Ihr Codelab befinden.

cd .. && pwd

Installieren Sie dann 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 Anwendung lokal ausführen fortfahren.

Wenn Sie jedoch in Ihrem lokalen Terminal arbeiten (also 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 sowohl die Rolle „Vertex AI-Nutzer“ als auch die Rolle „Datastore-Nutzer“) 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-Nutzer“ zuweisen. Wenn Ihre Identität die Rolle „Inhaber“ hat, haben Sie bereits diese Rolle „Vertex AI-Nutzer“. Andernfalls können Sie mit diesem Befehl Ihrer Identität die Rolle „Vertex AI-Nutzer“ und die Rolle „Datastore-Nutzer“ zuweisen.

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 „Dienstkonto-Tokenersteller“ haben. Sie können diese Rolle mit dem folgenden Befehl erhalten:

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

Zum Schluss können Sie die App mit dem folgenden Skript starten. Dieses Entwicklungsskript 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.

Webvorschau – Schaltfläche „Vorschau auf Port 8080“

8. Frontend-Dienst bereitstellen und testen

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 $FRONTEND \
  --service-account $SERVICE_ACCOUNT_ADDRESS \
  --source . \
  --region $REGION \
  --allow-unauthenticated

Öffnen Sie die Dienst-URL für das Frontend in Ihrem Browser. Stellen Sie eine Frage wie „Wie ist das aktuelle Wetter in Seattle?“ und Gemini sollte mit „Es sind derzeit 4 °C und es regnet“ antworten. Wenn Sie fragen „Wie ist das aktuelle Wetter in Boston?“, antwortet Gemini mit „Ich kann diese Anfrage nicht bearbeiten. Die verfügbare Wetter-API enthält keine Daten für Boston.“

9. Glückwunsch!

Herzlichen Glückwunsch zum Abschluss des Codelabs!

Wir empfehlen, die Dokumentation zu Cloud Run, Vertex AI Gemini APIs und Funktionsaufrufen zu lesen.

Behandelte Themen

  • Funktionsweise von Funktionsaufrufen in Gemini
  • Bereitstellen einer Gemini-basierten Chatbot-App als Cloud Run-Dienst

10. Bereinigen

Um unbeabsichtigte Kosten zu vermeiden (z. B. wenn dieser Cloud Run-Dienst versehentlich häufiger aufgerufen wird als Ihre monatliche Zuweisung für Cloud Run-Aufrufe im kostenlosen Kontingent), 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 in der Cloud Console https://console.cloud.google.com/functions/ auf und löschen Sie die Dienste $WEATHER_SERVICE und $FRONTEND, die Sie in diesem Codelab erstellt haben.

Sie können auch das Dienstkonto vertex-ai-caller löschen oder die Rolle „Vertex AI-Nutzer“ 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 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.