1. Introduzione
Panoramica
In questo codelab, vedrai come concedere a Gemini l'accesso ai dati in tempo reale utilizzando una nuova funzionalità chiamata chiamata di funzione. Per simulare i dati in tempo reale, creerai un endpoint del servizio meteo che restituisce il meteo attuale per due località. Poi creerai un'app di chat, basata su Gemini, che utilizza le chiamate di funzione per recuperare il meteo attuale.
Utilizziamo un'immagine per capire rapidamente la chiamata di funzione.
- Il prompt richiede le posizioni meteo attuali in una determinata località
- Questo prompt e il contratto della funzione per getWeather() vengono inviati a Gemini
- Gemini chiede all'app chatbot di chiamare "getWeather(Seattle)" per suo conto
- L'app restituisce i risultati (40 gradi F e pioggia)
- Gemini restituisce i risultati al chiamante
Ricapitolando, Gemini non chiama la funzione. In qualità di sviluppatore, devi chiamare la funzione e inviare i risultati a Gemini.

Cosa imparerai a fare
- Come funziona la chiamata di funzione di Gemini
- Come eseguire il deployment di un'app chatbot basata su Gemini come servizio Cloud Run
2. Configurazione e requisiti
Prerequisiti
- Hai eseguito l'accesso a Cloud Console.
- In precedenza hai eseguito il deployment di una funzione di 2ª gen. Ad esempio, puoi seguire la guida rapida di Cloud Functions (2nd gen) per iniziare.
Attiva Cloud Shell
- Nella console Cloud, fai clic su Attiva Cloud Shell
.

Se è la prima volta che avvii Cloud Shell, viene visualizzata una schermata intermedia che ne descrive le funzionalità. Se è stata visualizzata una schermata intermedia, fai clic su Continua.

Bastano pochi istanti per eseguire il provisioning e connettersi a Cloud Shell.

Questa macchina virtuale è caricata con tutti gli strumenti di sviluppo necessari. Offre una home directory permanente da 5 GB e viene eseguita in Google Cloud, migliorando notevolmente le prestazioni e l'autenticazione della rete. Gran parte del lavoro per questo codelab, se non tutto, può essere svolto con un browser.
Una volta eseguita la connessione a Cloud Shell, dovresti vedere che il tuo account è autenticato e il progetto è impostato sul tuo ID progetto.
- Esegui questo comando in Cloud Shell per verificare che l'account sia autenticato:
gcloud auth list
Output comando
Credentialed Accounts
ACTIVE ACCOUNT
* <my_account>@<my_domain.com>
To set the active account, run:
$ gcloud config set account `ACCOUNT`
- Esegui questo comando in Cloud Shell per verificare che il comando gcloud conosca il tuo progetto:
gcloud config list project
Output comando
[core] project = <PROJECT_ID>
In caso contrario, puoi impostarlo con questo comando:
gcloud config set project <PROJECT_ID>
Output comando
Updated property [core/project].
3. Configura le variabili di ambiente e abilita le API
Configura le variabili di ambiente
Puoi impostare le variabili di ambiente che verranno utilizzate in questo codelab.
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
Abilita API
Prima di poter iniziare a utilizzare questo codelab, devi abilitare diverse API. Questo codelab richiede l'utilizzo delle seguenti API. Puoi abilitare queste API eseguendo il seguente comando:
gcloud services enable run.googleapis.com \
cloudbuild.googleapis.com \
aiplatform.googleapis.com
4. Crea un service account per chiamare Vertex AI
Questo service account verrà utilizzato da Cloud Run per chiamare l'API Gemini di Vertex AI.
Innanzitutto, crea il service account eseguendo questo comando:
gcloud iam service-accounts create $SERVICE_ACCOUNT \ --display-name="Cloud Run to access Vertex AI APIs"
Secondo, concedi il ruolo Utente Vertex AI al service account.
gcloud projects add-iam-policy-binding $PROJECT_ID \ --member serviceAccount:$SERVICE_ACCOUNT_ADDRESS \ --role=roles/aiplatform.user
5. Crea il servizio Cloud Run di backend
Innanzitutto, crea una directory per il codice sorgente e accedi tramite cd.
mkdir -p gemini-function-calling/weatherservice gemini-function-calling/frontend && cd gemini-function-calling/weatherservice
Quindi, crea un file package.json con il seguente contenuto:
{
"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"
}
}
Successivamente, crea un file di origine app.js con il seguente contenuto. Questo file contiene il punto di ingresso del servizio e la logica principale dell'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!");
});
Esegui il deployment del servizio meteo
Puoi utilizzare questo comando per eseguire il deployment del servizio meteo.
gcloud run deploy $WEATHER_SERVICE \ --source . \ --region $REGION \ --allow-unauthenticated
Testare il servizio meteo
Puoi verificare il meteo per le due località utilizzando 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
A Seattle ci sono 4 °C e piove, mentre a New Orleans ci sono 37 °C e un tasso di umidità elevato, sempre.
6. Crea il servizio frontend
Innanzitutto, accedi alla directory frontend.
cd gemini-function-calling/frontend
Quindi, crea un file package.json con il seguente contenuto:
{
"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"
}
}
Successivamente, crea un file di origine app.js con il seguente contenuto. Questo file contiene il punto di ingresso del servizio e la logica principale dell'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);
});
}
Crea un file input.css per tailwindCSS.
@tailwind base; @tailwind components; @tailwind utilities;
Crea il file tailwind.config.js per tailwindCSS.
/** @type {import('tailwindcss').Config} */
module.exports = {
content: ["./**/*.{html,js}"],
theme: {
extend: {}
},
plugins: []
};
Crea il file metadataService.js per ottenere l'ID progetto e la regione per il servizio Cloud Run di cui è stato eseguito il deployment. Questi valori verranno utilizzati per creare un'istanza delle librerie client di 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;
}
};
Crea un file denominato 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>`;
Crea una nuova directory public.
mkdir public cd public
Ora crea il file index.html per il front-end, che utilizzerà 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. Esegui il servizio Frontend in locale
Innanzitutto, assicurati di trovarti nella directory frontend del tuo codelab.
cd .. && pwd
Quindi, installa le dipendenze eseguendo il seguente comando:
npm install
Utilizzo di ADC durante l'esecuzione locale
Se esegui Cloud Shell, la macchina virtuale Google Compute Engine è già in esecuzione. Le credenziali associate a questa macchina virtuale (come mostrato dall'esecuzione di gcloud auth list) verranno utilizzate automaticamente dalle credenziali predefinite dell'applicazione, quindi non è necessario utilizzare il comando gcloud auth application-default login. Puoi passare direttamente alla sezione Esegui l'app localmente.
Tuttavia, se esegui l'operazione sul terminale locale (ovvero non in Cloud Shell), devi utilizzare le Credenziali predefinite dell'applicazione per l'autenticazione alle API di Google. Puoi 1) accedere utilizzando le tue credenziali (a condizione che tu disponga dei ruoli Utente Vertex AI e Utente Datastore) oppure 2) accedere rappresentando il service account utilizzato in questo codelab.
Opzione 1: utilizzo delle credenziali per le Credenziali predefinite dell'applicazione
Se vuoi utilizzare le tue credenziali, puoi prima eseguire gcloud auth list per verificare la tua autenticazione in gcloud. Successivamente, potrebbe essere necessario concedere all'identità il ruolo Utente Vertex AI. Se la tua identità ha il ruolo Proprietario, disponi già di questo ruolo utente Vertex AI. In caso contrario, puoi eseguire questo comando per concedere alla tua identità il ruolo Utente Vertex AI e il ruolo Utente 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
Quindi esegui questo comando
gcloud auth application-default login
Opzione 2: simulare l'identità di un service account per ADC
Se vuoi utilizzare il service account creato in questo codelab, il tuo account utente deve disporre del ruolo Creatore token service account. Puoi ottenere questo ruolo eseguendo il seguente comando:
gcloud projects add-iam-policy-binding $PROJECT_ID \ --member user:$USER \ --role=roles/iam.serviceAccountTokenCreator
Successivamente, esegui questo comando per utilizzare ADC con l'account di servizio
gcloud auth application-default login --impersonate-service-account=$SERVICE_ACCOUNT_ADDRESS
Esegui l'app localmente
Infine, puoi avviare l'app eseguendo il seguente script. Questo script di sviluppo genererà anche il file output.css da tailwindCSS.
npm run dev
Puoi visualizzare l'anteprima del sito web aprendo il pulsante Anteprima web e selezionando Anteprima porta 8080.

8. Eseguire il deployment e testare il servizio Frontend
Innanzitutto, esegui questo comando per avviare il deployment e specificare il service account da utilizzare. Se non viene specificato un service account, viene utilizzato il service account di Compute predefinito.
gcloud run deploy $FRONTEND \ --service-account $SERVICE_ACCOUNT_ADDRESS \ --source . \ --region $REGION \ --allow-unauthenticated
Apri l'URL del servizio per il frontend nel browser. Poni la domanda "Che tempo fa a Seattle?" e Gemini dovrebbe rispondere "Attualmente ci sono 4 gradi e piove". Se chiedi "Che tempo fa a Boston?" Gemini risponderà con "Non posso soddisfare questa richiesta. L'API meteo disponibile non dispone di dati per Boston."
9. Complimenti!
Congratulazioni per aver completato il codelab.
Ti consigliamo di consultare la documentazione di Cloud Run, delle API Gemini di Vertex AI e della chiamata di funzione.
Argomenti trattati
- Come funziona la chiamata di funzione di Gemini
- Come eseguire il deployment di un'app chatbot basata su Gemini come servizio Cloud Run
10. Esegui la pulizia
Per evitare addebiti involontari (ad esempio, se questo servizio Cloud Run viene richiamato inavvertitamente più volte rispetto all'allocazione mensile di chiamate a Cloud Run nel livello senza costi), puoi eliminare il servizio Cloud Run o il progetto che hai creato nel passaggio 2.
Per eliminare i servizi Cloud Run, vai alla console Google Cloud di Cloud Run all'indirizzo https://console.cloud.google.com/functions/ ed elimina i servizi $WEATHER_SERVICE e $FRONTEND che hai creato in questo codelab.
Potresti anche voler eliminare il service account vertex-ai-caller o revocare il ruolo Utente Vertex AI per evitare chiamate involontarie a Gemini.
Se scegli di eliminare l'intero progetto, puoi andare alla pagina https://console.cloud.google.com/cloud-resource-manager, selezionare il progetto che hai creato nel passaggio 2 e scegliere Elimina. Se elimini il progetto, dovrai cambiare progetto in Cloud SDK. Puoi visualizzare l'elenco di tutti i progetti disponibili eseguendo gcloud projects list.