1. Giriş
Genel Bakış
Bu codelab'de, İşlev Çağrısı adlı yeni özelliği kullanarak Gemini'ın gerçek zamanlı verilere erişmesine nasıl izin vereceğinizi öğreneceksiniz. Gerçek zamanlı verileri simüle etmek amacıyla, 2 konum için mevcut hava durumunu döndüren bir hava durumu hizmeti uç noktası derleyeceksiniz. Ardından, güncel hava durumunu almak için işlev çağrısından yararlanan, Gemini tarafından desteklenen bir sohbet uygulaması geliştireceksiniz.
İşlev Çağrısı'nı anlamak için kısa bir görsel kullanalım.
- İstem, belirli bir konumdaki mevcut hava durumu konumları için istekte bulunur
- Bu istem ve getWeather() işlev sözleşmesi Gemini'a gönderilir
- Gemini, sohbet botu uygulamasının "getWeather(Seattle)" demesini ister kendi adına
- Uygulama sonuçları geri gönderir (40 F derece ve yağmurlu)
- Gemini, sonuçları arayana geri gönderir
Özetlemek gerekirse Gemini, İşlev'i çağırmaz. Geliştirici olarak sizin, işlevi çağırmanız ve sonuçları Gemini'a geri göndermeniz gerekir.
Neler öğreneceksiniz?
- Gemini işlev çağrısının işleyiş şekli
- Gemini destekli chatbot uygulamasını Cloud Run hizmeti olarak dağıtma
2. Kurulum ve Gereksinimler
Ön koşullar
- Cloud Console'a giriş yaptınız.
- Daha önce 2. nesil işlevi dağıttınız. Örneğin, hizmeti kullanmaya başlamak için 2. nesil Cloud Functions işlevi hızlı başlangıç kılavuzunu dağıtma işlemini uygulayabilirsiniz.
Cloud Shell'i etkinleştirme
- Cloud Console'da, Cloud Shell'i etkinleştir simgesini tıklayın.
Cloud Shell'i ilk kez başlatıyorsanız ne olduğunu açıklayan bir ara ekran gösterilir. Ara bir ekran görüntülendiyse Devam'ı tıklayın.
Temel hazırlık ve Cloud Shell'e bağlanmak yalnızca birkaç dakika sürer.
Gereken tüm geliştirme araçları bu sanal makinede yüklüdür. 5 GB boyutunda kalıcı bir ana dizin sunar ve Google Cloud'da çalışarak ağ performansını ve kimlik doğrulamasını büyük ölçüde iyileştirir. Bu codelab'deki çalışmalarınızın tamamı olmasa bile büyük bir kısmı tarayıcıyla yapılabilir.
Cloud Shell'e bağlandıktan sonra kimliğinizin doğrulandığını ve projenin proje kimliğinize ayarlandığını göreceksiniz.
- Kimlik doğrulamanızın tamamlandığını onaylamak için Cloud Shell'de aşağıdaki komutu çalıştırın:
gcloud auth list
Komut çıkışı
Credentialed Accounts ACTIVE ACCOUNT * <my_account>@<my_domain.com> To set the active account, run: $ gcloud config set account `ACCOUNT`
- gcloud komutunun projenizi bildiğini onaylamak için Cloud Shell'de aşağıdaki komutu çalıştırın:
gcloud config list project
Komut çıkışı
[core] project = <PROJECT_ID>
Doğru değilse aşağıdaki komutla ayarlayabilirsiniz:
gcloud config set project <PROJECT_ID>
Komut çıkışı
Updated property [core/project].
3. Ortam değişkenlerini ayarlayın ve API'leri etkinleştirin
Ortam değişkenlerini ayarlama
Bu codelab'de kullanılacak ortam değişkenlerini ayarlayabilirsiniz.
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
API'leri etkinleştir
Bu codelab'i kullanmaya başlamadan önce etkinleştirmeniz gereken birkaç API vardır. Bu codelab'de aşağıdaki API'lerin kullanılması gerekir. Bu API'leri şu komutu çalıştırarak etkinleştirebilirsiniz:
gcloud services enable run.googleapis.com \ cloudbuild.googleapis.com \ aiplatform.googleapis.com
4. Vertex AI'ı çağırmak için hizmet hesabı oluşturma
Bu hizmet hesabı, Cloud Run tarafından Vertex AI Gemini API'yi çağırmak için kullanılır.
Öncelikle şu komutu çalıştırarak hizmet hesabını oluşturun:
gcloud iam service-accounts create $SERVICE_ACCOUNT \ --display-name="Cloud Run to access Vertex AI APIs"
Ardından, hizmet hesabına Vertex AI Kullanıcısı rolünü verin.
gcloud projects add-iam-policy-binding $PROJECT_ID \ --member serviceAccount:$SERVICE_ACCOUNT_ADDRESS \ --role=roles/aiplatform.user
5. Arka uç Cloud Run hizmetini oluşturma
İlk olarak, kaynak kodu için bir dizin oluşturun ve bu dizin için cd'yi kullanın.
mkdir -p gemini-function-calling/weatherservice gemini-function-calling/frontend && cd gemini-function-calling/weatherservice
Ardından, aşağıdaki içeriğe sahip bir package.json
dosyası oluşturun:
{ "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" } }
Sonra, aşağıdaki içerikle bir app.js
kaynak dosyası oluşturun. Bu dosya, hizmetin giriş noktasını ve uygulamanın ana mantığını içerir.
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!"); });
Hava Durumu Hizmeti'ni dağıtma
Hava durumu hizmetini dağıtmak için bu komutu kullanabilirsiniz.
gcloud run deploy $WEATHER_SERVICE \ --source . \ --region $REGION \ --allow-unauthenticated
Hava Durumu Hizmeti'ni test etme
curl'ü kullanarak 2 konumun hava durumunu doğrulayabilirsiniz:
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'da hava 14 derece, New Orleans ise 40 derece ve nemli.
6. Ön uç hizmetini oluşturma
İlk olarak, ön uç dizinine cd yazın.
cd gemini-function-calling/frontend
Ardından, aşağıdaki içeriğe sahip bir package.json
dosyası oluşturun:
{ "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" } }
Sonra, aşağıdaki içerikle bir app.js
kaynak dosyası oluşturun. Bu dosya, hizmetin giriş noktasını ve uygulamanın ana mantığını içerir.
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); }); }
tailwindCSS için bir input.css
dosyası oluşturun.
@tailwind base; @tailwind components; @tailwind utilities;
tailwindCSS için tailwind.config.js
dosyasını oluşturun.
/** @type {import('tailwindcss').Config} */ module.exports = { content: ["./**/*.{html,js}"], theme: { extend: {} }, plugins: [] };
Dağıtılan Cloud Run hizmetinin proje kimliğini ve bölgesini almak için metadataService.js
dosyasını oluşturun. Bu değerler, Vertex AI istemci kitaplıklarının örneğini oluşturmak için kullanılır.
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; } };
spinnerSvg.js
adlı bir dosya oluştur
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>`;
Yeni bir public
dizini oluşturun.
mkdir public cd public
Şimdi, kullanıcı arabirimi için htmx kullanacak olan index.html
dosyasını oluşturun.
<!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. Ön uç hizmetini yerel olarak çalıştırın
Öncelikle, codelab'iniz için frontend
dizininde olduğunuzdan emin olun.
cd .. && pwd
Ardından şu komutu çalıştırarak bağımlıları yükleyin:
npm install
Yerel olarak çalıştırırken ADC kullanma
Cloud Shell'de çalışıyorsanız zaten bir Google Compute Engine sanal makinesinde çalıştırıyorsunuz demektir. Bu sanal makineyle ilişkili kimlik bilgileriniz (gcloud auth list
çalıştırıldığında gösterildiği şekilde), otomatik olarak Uygulama Varsayılan Kimlik Bilgileri tarafından kullanılacağından gcloud auth application-default login
komutunun kullanılması gerekmez. Dilerseniz Uygulamayı yerel olarak çalıştırma bölümüne geçebilirsiniz
Ancak yerel terminalinizde (ör. Cloud Shell'de değil) çalışıyorsanız Google API'lerinde kimlik doğrulamak için Uygulama Varsayılan Kimlik Bilgilerini kullanmanız gerekir. 1) Kimlik bilgilerinizi kullanarak giriş yapabilirsiniz (hem Vertex AI Kullanıcısı hem de Datastore Kullanıcısı rollerine sahipseniz) veya 2) bu codelab'de kullanılan hizmet hesabının kimliğine bürünerek giriş yapabilirsiniz.
1. Seçenek) ADC için kimlik bilgilerinizi kullanma
Kimlik bilgilerinizi kullanmak istiyorsanız gcloud'da kimlik doğrulamanın nasıl yapıldığını doğrulamak için önce gcloud auth list
komutunu çalıştırabilirsiniz. Ardından, kimliğinize Vertex AI Kullanıcısı rolünü vermeniz gerekebilir. Kimliğiniz Sahip rolündeyse Vertex AI kullanıcı rolüne zaten sahipsiniz demektir. Aksi takdirde, kimliğinize Vertex AI kullanıcı rolünü ve Datastore Kullanıcısı rolünü vermek için bu komutu çalıştırabilirsiniz.
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
Ardından aşağıdaki komutu çalıştırın
gcloud auth application-default login
2. Seçenek) ADC için Bir Hizmet Hesabının Kimliğine Bürünme
Bu codelab'de oluşturulan hizmet hesabını kullanmak istiyorsanız kullanıcı hesabınızın Hizmet Hesabı Jetonu Oluşturucu rolüne sahip olması gerekir. Bu rolü almak için şu komutu çalıştırın:
gcloud projects add-iam-policy-binding $PROJECT_ID \ --member user:$USER \ --role=roles/iam.serviceAccountTokenCreator
Ardından, ADC'yi hizmet hesabıyla kullanmak için aşağıdaki komutu çalıştıracaksınız
gcloud auth application-default login --impersonate-service-account=$SERVICE_ACCOUNT_ADDRESS
Uygulamayı yerel olarak çalıştırma
Son olarak, aşağıdaki komut dosyasını çalıştırarak uygulamayı başlatabilirsiniz. Bu geliştirici komut dosyası, tailwindCSS'den çıkış.css dosyası da oluşturur.
npm run dev
Web Önizlemesi düğmesini açıp Önizleme Bağlantı Noktası 8080'i seçerek web sitesini önizleyebilirsiniz.
8. Ön uç hizmetini dağıtma ve test etme
Öncelikle bu komutu çalıştırarak dağıtımı başlatın ve kullanılacak hizmet hesabını belirtin. Bir hizmet hesabı belirtilmemişse varsayılan Compute hizmet hesabı kullanılır.
gcloud run deploy $FRONTEND \ --service-account $SERVICE_ACCOUNT_ADDRESS \ --source . \ --region $REGION \ --allow-unauthenticated
Ön uç için hizmet URL'sini tarayıcınızda açın. "Seattle'da şu anda hava nasıl?" sorusu sorun. Gemini, "Şu anda sıcaklık 40 derece ve yağmurlu" şeklinde yanıt vermelidir. "İstanbul'da şu anda hava nasıl?" diye sorarsanız Gemini, "Bu isteği yerine getiremiyorum. Kullanılabilir hava durumu API'sinde Boston ile ilgili veri yok."
9. Tebrikler!
Tebrikler, codelab'i tamamladınız.
Cloud Run, Vertex AI Gemini API'leri ve işlev çağrısı belgelerini incelemenizi öneririz.
İşlediklerimiz
- Gemini işlev çağrısının işleyiş şekli
- Gemini destekli chatbot uygulamasını Cloud Run hizmeti olarak dağıtma
10. Temizleme
Yanlışlıkla yapılan ücretleri önlemek için (örneğin, bu Cloud Run hizmeti yanlışlıkla ücretsiz katmandaki aylık Cloud Run çağırma hizmetinden daha fazla kez çağrıldıysa) Cloud Run hizmetini veya 2. adımda oluşturduğunuz projeyi silebilirsiniz.
Cloud Run hizmetlerini silmek için https://console.cloud.google.com/functions/ adresinden Cloud Run Cloud Console'a gidin ve bu codelab'de oluşturduğunuz $WEEK_SERVICE ve $FRONTEND hizmetlerini silin.
Dilerseniz Gemini'a yanlışlıkla yapılan çağrıları önlemek için vertex-ai-caller
hizmet hesabını silebilir veya Vertex AI Kullanıcısı rolünü iptal edebilirsiniz.
Projenin tamamını silmeyi tercih ederseniz https://console.cloud.google.com/cloud-resource-manager adresine gidip 2. adımda oluşturduğunuz projeyi, ardından Sil'i seçebilirsiniz. Projeyi silerseniz Cloud SDK'nızdaki projeleri değiştirmeniz gerekir. gcloud projects list
komutunu çalıştırarak mevcut tüm projelerin listesini görüntüleyebilirsiniz.