1. Introduction
Présentation
Dans cet atelier de programmation, vous allez apprendre à autoriser Gemini à accéder aux données en temps réel à l'aide d'une nouvelle fonctionnalité appelée Appels de fonctions. Pour simuler des données en temps réel, vous allez créer un point de terminaison de service météo qui renvoie la météo actuelle pour deux emplacements. Vous créerez ensuite une application de chat, fournie par Gemini, qui utilise l'appel de fonction pour obtenir la météo.
Utilisons un visuel rapide pour comprendre l'appel de fonction.
- L'invite demande des informations sur la météo actuelle d'un lieu donné
- Cette requête et le contrat de la fonction pour getWeather() sont envoyés à Gemini
- Gemini demande que l'application de chatbot appelle "getWeather(Seattle)" en son nom
- L'application renvoie les résultats (40 degress F et rainy).
- Gemini renvoie les résultats à l'appelant
Pour récapituler, Gemini n'appelle pas la fonction. En tant que développeur, vous devez appeler la fonction et renvoyer les résultats à Gemini.
Points abordés
- Fonctionnement de l'appel de fonction Gemini
- Déployer une application de chatbot fournie par Gemini en tant que service Cloud Run
2. Préparation
Prérequis
- Vous êtes connecté à la console Cloud.
- Vous avez déjà déployé une fonction de 2e génération. Par exemple, vous pouvez suivre le déploiement du guide de démarrage rapide de Cloud Functions 2nd gen pour commencer.
Activer Cloud Shell
- Dans Cloud Console, cliquez sur Activer Cloud Shell .
Si vous démarrez Cloud Shell pour la première fois, un écran intermédiaire vous explique de quoi il s'agit. Si un écran intermédiaire s'est affiché, cliquez sur Continuer.
Le provisionnement et la connexion à Cloud Shell ne devraient pas prendre plus de quelques minutes.
Cette machine virtuelle contient tous les outils de développement nécessaires. Elle comprend un répertoire d'accueil persistant de 5 Go et s'exécute dans Google Cloud, ce qui améliore considérablement les performances du réseau et l'authentification. Une grande partie, voire la totalité, de votre travail dans cet atelier de programmation peut être effectué dans un navigateur.
Une fois connecté à Cloud Shell, vous êtes authentifié et le projet est défini sur votre ID de projet.
- Exécutez la commande suivante dans Cloud Shell pour vérifier que vous êtes authentifié :
gcloud auth list
Résultat de la commande
Credentialed Accounts ACTIVE ACCOUNT * <my_account>@<my_domain.com> To set the active account, run: $ gcloud config set account `ACCOUNT`
- Exécutez la commande suivante dans Cloud Shell pour vérifier que la commande gcloud connaît votre projet:
gcloud config list project
Résultat de la commande
[core] project = <PROJECT_ID>
Si vous obtenez un résultat différent, exécutez cette commande :
gcloud config set project <PROJECT_ID>
Résultat de la commande
Updated property [core/project].
3. Configurer des variables d'environnement et activer les API
Configurer des variables d'environnement
Vous pouvez définir les variables d'environnement qui seront utilisées tout au long de cet atelier de programmation.
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
Activer les API
Avant de commencer à utiliser cet atelier de programmation, vous devez activer plusieurs API. Cet atelier de programmation nécessite l'utilisation des API suivantes. Vous pouvez activer ces API en exécutant la commande suivante:
gcloud services enable run.googleapis.com \ cloudbuild.googleapis.com \ aiplatform.googleapis.com
4. Créer un compte de service pour appeler Vertex AI
Ce compte de service permettra à Cloud Run d'appeler l'API Gemini Vertex AI.
Commencez par créer le compte de service en exécutant la commande suivante:
gcloud iam service-accounts create $SERVICE_ACCOUNT \ --display-name="Cloud Run to access Vertex AI APIs"
Ensuite, attribuez le rôle "Utilisateur Vertex AI" au compte de service.
gcloud projects add-iam-policy-binding $PROJECT_ID \ --member serviceAccount:$SERVICE_ACCOUNT_ADDRESS \ --role=roles/aiplatform.user
5. Créer le service Cloud Run de backend
Tout d'abord, créez un répertoire pour le code source et utilisez la commande cd pour y accéder.
mkdir -p gemini-function-calling/weatherservice gemini-function-calling/frontend && cd gemini-function-calling/weatherservice
Ensuite, créez un fichier package.json
avec le contenu suivant:
{ "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" } }
Ensuite, créez un fichier source app.js
avec le contenu ci-dessous. Ce fichier contient le point d'entrée du service et la logique principale de l'application.
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!"); });
Déployer le service Weather
Vous pouvez utiliser cette commande pour déployer le service météo.
gcloud run deploy $WEATHER_SERVICE \ --source . \ --region $REGION \ --allow-unauthenticated
Tester le service météo
Vous pouvez vérifier la météo de deux lieux à l'aide de la commande curl:
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
Seattle fait 12 degrés Celsius et il pleut, et La Nouvelle-Orléans fait 39 degrés l'humidité et toujours humide.
6. Créer le service d'interface
Tout d'abord, utilisez la commande cd pour accéder au répertoire "frontend".
cd gemini-function-calling/frontend
Ensuite, créez un fichier package.json
avec le contenu suivant:
{ "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" } }
Ensuite, créez un fichier source app.js
avec le contenu ci-dessous. Ce fichier contient le point d'entrée du service et la logique principale de l'application.
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); }); }
Créez un fichier input.css
pour tailwindCSS.
@tailwind base; @tailwind components; @tailwind utilities;
Créez le fichier tailwind.config.js
pour tailwindCSS.
/** @type {import('tailwindcss').Config} */ module.exports = { content: ["./**/*.{html,js}"], theme: { extend: {} }, plugins: [] };
Créez le fichier metadataService.js
pour obtenir l'ID du projet et la région du service Cloud Run déployé. Ces valeurs serviront à instancier une instance des bibliothèques clientes 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 { // 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; } };
Créer un fichier intitulé 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>`;
Créez un répertoire public
.
mkdir public cd public
Créez maintenant le fichier index.html
du frontal, qui utilisera 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 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. Exécuter le service d'interface en local
Tout d'abord, vérifiez que vous êtes bien dans le répertoire frontend
de votre atelier de programmation.
cd .. && pwd
Ensuite, installez les dépendances en exécutant la commande suivante:
npm install
Utiliser ADC en cas d'exécution locale
Si vous exécutez Cloud Shell, vous exécutez déjà le projet sur une machine virtuelle Google Compute Engine. Vos identifiants associés à cette machine virtuelle (comme indiqué en exécutant gcloud auth list
) seront automatiquement utilisés par les identifiants par défaut de l'application. Il n'est donc pas nécessaire d'exécuter la commande gcloud auth application-default login
. Vous pouvez passer à la section Exécuter l'application en local.
Toutefois, si vous exécutez l'application sur votre terminal local (c'est-à-dire sans passer par Cloud Shell), vous devez utiliser les identifiants par défaut de l'application pour vous authentifier auprès des API Google. Vous pouvez 1) vous connecter à l'aide de vos identifiants (à condition de disposer des rôles Utilisateur Vertex AI et Utilisateur Datastore) ou 2) vous connecter en usurpant l'identité du compte de service utilisé dans cet atelier de programmation.
Option 1 : Utiliser vos identifiants pour l'ADC
Si vous souhaitez utiliser vos identifiants, vous pouvez d'abord exécuter gcloud auth list
pour vérifier votre authentification dans gcloud. Ensuite, vous devrez peut-être attribuer à votre identité le rôle "Utilisateur Vertex AI". Si votre identité dispose du rôle Propriétaire, vous disposez déjà de ce rôle utilisateur Vertex AI. Si ce n'est pas le cas, vous pouvez exécuter cette commande pour attribuer à votre identité les rôles d'utilisateur Vertex AI et d'utilisateur 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
Exécutez ensuite la commande suivante :
gcloud auth application-default login
Option 2 : Emprunter l'identité d'un compte de service pour ADC
Si vous souhaitez utiliser le compte de service créé dans cet atelier de programmation, votre compte utilisateur doit disposer du rôle Créateur de jetons du compte de service. Vous pouvez obtenir ce rôle en exécutant la commande suivante:
gcloud projects add-iam-policy-binding $PROJECT_ID \ --member user:$USER \ --role=roles/iam.serviceAccountTokenCreator
Ensuite, vous exécuterez la commande suivante pour utiliser ADC avec le compte de service
gcloud auth application-default login --impersonate-service-account=$SERVICE_ACCOUNT_ADDRESS
Exécuter l'application en local
Enfin, vous pouvez démarrer l'application en exécutant le script suivant. Ce script de développement générera également le fichier output.css à partir de tailwindCSS.
npm run dev
Pour prévisualiser le site Web, ouvrez le bouton "Aperçu sur le Web", puis sélectionnez le port d'aperçu 8080
8. Déployer et tester le service d'interface
Commencez par exécuter cette commande pour démarrer le déploiement et spécifier le compte de service à utiliser. Si aucun compte de service n'est spécifié, le compte de service Compute par défaut est utilisé.
gcloud run deploy $FRONTEND \ --service-account $SERVICE_ACCOUNT_ADDRESS \ --source . \ --region $REGION \ --allow-unauthenticated
Ouvrez l'URL du service pour l'interface dans votre navigateur. Posez la question "Quel temps fait-il à Seattle ?" Gemini devrait répondre : "Il fait actuellement 10 degrés et il pleut." Si vous demandez "Quel temps fait-il à Boston ?", Gemini répondra : "Je ne peux pas traiter cette demande. L'API météo disponible ne contient pas de données pour Boston."
9. Félicitations !
Félicitations ! Vous avez terminé cet atelier de programmation.
Nous vous recommandons de consulter la documentation sur Cloud Run, les API Gemini de Vertex AI et les appels de fonction.
Points abordés
- Fonctionnement de l'appel de fonction Gemini
- Déployer une application de chatbot fournie par Gemini en tant que service Cloud Run
10. Effectuer un nettoyage
Pour éviter des frais accidentels (par exemple, si ce service Cloud Run est appelé par inadvertance plus de fois que l'allocation mensuelle des appels Cloud Run dans la version sans frais), vous pouvez supprimer le service Cloud Run ou le projet que vous avez créé à l'étape 2.
Pour supprimer les services Cloud Run, accédez à la console Cloud Run à l'adresse https://console.cloud.google.com/functions/, puis supprimez les services $WEATHE_SERVICE et $FRONTEND que vous avez créés dans cet atelier de programmation.
Vous pouvez également supprimer le compte de service vertex-ai-caller
ou révoquer le rôle "Utilisateur Vertex AI" pour éviter tout appel involontaire à Gemini.
Si vous choisissez de supprimer l'intégralité du projet, vous pouvez accéder à https://console.cloud.google.com/cloud-resource-manager, sélectionner le projet que vous avez créé à l'étape 2, puis cliquer sur "Supprimer". Si vous supprimez le projet, vous devrez le modifier dans Cloud SDK. Vous pouvez afficher la liste de tous les projets disponibles en exécutant gcloud projects list
.