1. Giriş
Genel Bakış
Bu codelab'de, Vertex AI Gemini API'yi ve Vertex AI istemci kitaplığını kullanarak düğümde yazılmış temel bir chat bot'u nasıl oluşturacağınızı öğreneceksiniz. Bu uygulama, Google Cloud Firestore tarafından desteklenen bir ekspres oturum deposu kullanır.
Neler öğreneceksiniz?
- Cloud Run hizmeti derlemek için htmx, tailwindcss ve express.js'yi kullanma
- Google API'lerinde kimlik doğrulamak için Vertex AI istemci kitaplıklarını kullanma
- Gemini modeliyle etkileşimde bulunmak için chatbot oluşturma
- Docker dosyası olmadan Cloud Run hizmetine dağıtım yapma
- Google Cloud Firestore tarafından desteklenen ekspres oturum deposu nasıl kullanılır?
2. Kurulum ve Gereksinimler
Ön koşullar
- Cloud Console'a giriş yaptınız.
- Daha önce bir Cloud Run hizmeti dağıttınız. Örneğin, başlamak için kaynak kodundan web hizmeti dağıtma başlıklı makaledeki adımları 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. API'leri Etkinleştirin ve Ortam Değişkenlerini Ayarlayın
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 \ secretmanager.googleapis.com
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> SERVICE=chat-with-gemini SERVICE_ACCOUNT="vertex-ai-caller" SERVICE_ACCOUNT_ADDRESS=$SERVICE_ACCOUNT@$PROJECT_ID.iam.gserviceaccount.com SECRET_ID="SESSION_SECRET"
4. Firebase projesi oluşturma ve yapılandırma
- Firebase konsolunda Proje ekle'yi tıklayın.
- <YOUR_PROJECT_ID> değerini girin mevcut Google Cloud projelerinizden birine Firebase'i ekleyin
- İstenirse Firebase şartlarını inceleyip kabul edin.
- Devam'ı tıklayın.
- Firebase faturalandırma planını onaylamak için Planı Onayla'yı tıklayın.
- Bu codelab için Google Analytics'i etkinleştirmek isteğe bağlıdır.
- Firebase'i ekle'yi tıklayın.
- Proje oluşturulduktan sonra Continue (Devam) seçeneğini tıklayın.
- Build (Derleme) menüsünde, Firestore database'i (Firestore veritabanı) tıklayın.
- Create database'i (Veritabanı oluştur) tıklayın.
- Konum açılır menüsünden bölgenizi seçip İleri'yi tıklayın.
- Varsayılan Üretim modunda başlat seçeneğini kullanın, ardından Oluştur'u tıklayın.
5. Hizmet hesabı oluşturma
Bu hizmet hesabı, Cloud Run tarafından Vertex AI Gemini API'yi çağırmak için kullanılır. Bu hizmet hesabı ayrıca Firestore okuma, yazma ve Secret Manager'daki gizli anahtarları okuma iznine de sahip olur.
Ö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
Şimdi, Secret Manager'da gizli anahtar oluşturun. Cloud Run hizmeti, bu gizli anahtara bir ortam değişkeni olarak erişir ve bu değişken, örnek başlatma sırasında çözülür. Gizli anahtarlar ve Cloud Run hakkında daha fazla bilgi edinebilirsiniz.
gcloud secrets create $SECRET_ID --replication-policy="automatic" printf "keyboard-cat" | gcloud secrets versions add $SECRET_ID --data-file=-
Ayrıca hizmet hesabına, Secret Manager'da açık oturum gizli anahtarı için erişim izni verin.
gcloud secrets add-iam-policy-binding $SECRET_ID \ --member serviceAccount:$SERVICE_ACCOUNT_ADDRESS \ --role='roles/secretmanager.secretAccessor'
Son olarak, hizmet hesabına Firestore'a okuma ve yazma erişimi verin.
gcloud projects add-iam-policy-binding $PROJECT_ID \ --member serviceAccount:$SERVICE_ACCOUNT_ADDRESS \ --role=roles/datastore.user
6. 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 chat-with-gemini && cd chat-with-gemini
Ardından, aşağıdaki içeriğe sahip bir package.json
dosyası oluşturun:
{ "name": "chat-with-gemini", "version": "1.0.0", "description": "", "main": "app.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/connect-firestore": "^3.0.0", "@google-cloud/firestore": "^7.5.0", "@google-cloud/vertexai": "^0.4.0", "axios": "^1.6.8", "express": "^4.18.2", "express-session": "^1.18.0", "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"); // cloud run retrieves secret at instance startup time const secret = process.env.SESSION_SECRET; const { Firestore } = require("@google-cloud/firestore"); const { FirestoreStore } = require("@google-cloud/connect-firestore"); var session = require("express-session"); app.set("trust proxy", 1); // trust first proxy app.use( session({ store: new FirestoreStore({ dataset: new Firestore(), kind: "express-sessions" }), secret: secret, /* set secure to false for local dev session history testing */ /* see more at https://expressjs.com/en/resources/middleware/session.html */ cookie: { secure: true }, resave: false, saveUninitialized: true }) ); const expressWs = require("express-ws")(app); app.use(express.static("public")); // Vertex AI Section const { VertexAI } = require("@google-cloud/vertexai"); // instance of Vertex model let generativeModel; // on startup const port = parseInt(process.env.PORT) || 8080; app.listen(port, async () => { console.log(`demo1: listening on port ${port}`); // get project and location from metadata service const metadataService = require("./metadataService.js"); 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" }); }); app.ws("/sendMessage", async function (ws, req) { if (!req.session.chathistory || req.session.chathistory.length == 0) { req.session.chathistory = []; } let chatWithModel = generativeModel.startChat({ history: req.session.chathistory }); ws.on("message", async function (message) { console.log("req.sessionID: ", req.sessionID); // get session id 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); const answer = results.response.candidates[0].content.parts[0].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"> ${answer} </div>`); // save to current chat history let userHistory = { role: "user", parts: [{ text: questionToAsk }] }; let modelHistory = { role: "model", parts: [{ text: answer }] }; req.session.chathistory.push(userHistory); req.session.chathistory.push(modelHistory); // console.log( // "newly saved chat history: ", // util.inspect(req.session.chathistory, { // showHidden: false, // depth: null, // colors: true // }) // ); req.session.save(); }); ws.on("close", () => { console.log("WebSocket was closed"); }); }); function sleep(ms) { return new Promise((resolve) => { setTimeout(resolve, ms); }); } // gracefully close the web sockets process.on("SIGTERM", () => { server.close(); });
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 { // running on Clodu Run. 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 { // running on Clodu Run. 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>`;
Son olarak, tailwindCSS için bir input.css
dosyası oluşturun.
@tailwind base; @tailwind components; @tailwind utilities;
Şimdi yeni bir public
dizini oluşturun.
mkdir public cd public
Ayrıca bu herkese açık dizinde, 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 1</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 > Is C# a programming language or a musical note?</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. Hizmeti yerel olarak çalıştırma
Öncelikle, codelab'inizin kök dizininde (chat-with-gemini
) olduğunuzdan emin olun.
cd .. && pwd
Ardından, aşağıdaki 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. Yerel oturum gizli anahtarı oluşturma 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
Yerel oturum gizli anahtarı oluşturma
Şimdi, yerel geliştirme için bir yerel oturum gizli anahtarı oluşturun.
export SESSION_SECRET=local-secret
Uygulamayı yerel olarak çalıştırma
Son olarak, aşağıdaki komut dosyasını çalıştırarak uygulamayı başlatabilirsiniz. Bu komut dosyası, tailwindCSS'den çıkış.css dosyasını da oluşturur.
npm run dev
Web Önizlemesi düğmesini açıp Önizleme Bağlantı Noktası 8080'i seçerek web sitesini önizleyebilirsiniz.
8. Hizmeti dağıtma
Ö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 $SERVICE \ --service-account $SERVICE_ACCOUNT_ADDRESS \ --source . \ --region $REGION \ --allow-unauthenticated \ --set-secrets="SESSION_SECRET=$(echo $SECRET_ID):1"
"Kaynaktan dağıtım yapmak için derlenen container'ları depolamak için bir Artifact Registry Docker deposu gerekir. [us-central1] bölgesinde [cloud-run-source-deploy] adlı bir depo oluşturulacak.", "y"ye basın kabul edip devam edin.
9. Hizmeti test etme
Dağıtım tamamlandığında web tarayıcınızda hizmet URL'sini açın. Ardından Gemini'a bir soru sorun, ör. "Gitar çalıyorum. Aynı zamanda yazılım mühendisiyim. "C#" ifadesini gördüğümde bunu bir programlama dili mi yoksa müzik notası olarak mı düşünmeliyim? Hangisini seçmeliyim?"
10. Tebrikler!
Tebrikler, codelab'i tamamladınız.
Cloud Run ve Vertex AI Gemini API'leri belgelerini incelemenizi öneririz.
İşlediklerimiz
- Cloud Run hizmeti derlemek için htmx, tailwindcss ve express.js'yi kullanma
- Google API'lerinde kimlik doğrulamak için Vertex AI istemci kitaplıklarını kullanma
- Gemini modeliyle etkileşimde bulunmak için chat bot oluşturma
- Docker dosyası olmadan Cloud Run hizmetine dağıtım yapma
- Google Cloud Firestore tarafından desteklenen ekspres oturum deposu nasıl kullanılır?
11. Temizleme
Yanlışlıkla yapılan ücretleri önlemek için (örneğin, Cloud Run hizmetleri yanlışlıkla ücretsiz katmandaki aylık Cloud Run çağırma tahsisinizden daha fazla kez çağrıldıysa) Cloud Run'ı silebilir veya 2. adımda oluşturduğunuz projeyi silebilirsiniz.
Cloud Run hizmetini silmek için https://console.cloud.google.com/run adresinden Cloud Run Cloud Console'a gidip chat-with-gemini
hizmetini 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.