1. مقدمه
نمای کلی
در این لبه کد، خواهید دید که چگونه می توانید با استفاده از Vertex AI Gemini API و کتابخانه کلاینت Vertex AI یک ربات چت پایه نوشته شده در گره ایجاد کنید. این برنامه از یک فروشگاه جلسه اکسپرس با پشتیبانی Google Cloud Firestore استفاده می کند.
چیزی که یاد خواهید گرفت
- نحوه استفاده از htmx، tailwindcss و express.js برای ساخت سرویس Cloud Run
- نحوه استفاده از کتابخانه های کلاینت AI Vertex برای احراز هویت در Google API
- نحوه ایجاد یک چت بات برای تعامل با مدل Gemini
- نحوه استقرار در سرویس اجرای ابری بدون فایل داکر
- نحوه استفاده از فروشگاه جلسه اکسپرس با پشتیبانی Google Cloud Firestore
2. راه اندازی و الزامات
پیش نیازها
- شما به کنسول Cloud وارد شده اید.
- شما قبلاً یک سرویس Cloud Run را مستقر کرده اید. برای مثال، میتوانید برای شروع ، استقرار یک سرویس وب را از کد منبع سریع دنبال کنید.
Cloud Shell را فعال کنید
- از Cloud Console، روی Activate Cloud Shell کلیک کنید .
اگر این اولین باری است که Cloud Shell را راه اندازی می کنید، با یک صفحه میانی روبرو می شوید که آن را توصیف می کند. اگر با یک صفحه میانی مواجه شدید، روی Continue کلیک کنید.
تهیه و اتصال به Cloud Shell فقط باید چند لحظه طول بکشد.
این ماشین مجازی با تمام ابزارهای توسعه مورد نیاز بارگذاری شده است. این یک فهرست اصلی 5 گیگابایتی دائمی ارائه میکند و در Google Cloud اجرا میشود، که عملکرد و احراز هویت شبکه را بسیار افزایش میدهد. بسیاری از کارهای شما، اگر نه همه، در این کد لبه با مرورگر قابل انجام است.
پس از اتصال به Cloud Shell، باید ببینید که احراز هویت شده اید و پروژه به ID پروژه شما تنظیم شده است.
- برای تایید احراز هویت، دستور زیر را در Cloud Shell اجرا کنید:
gcloud auth list
خروجی فرمان
Credentialed Accounts ACTIVE ACCOUNT * <my_account>@<my_domain.com> To set the active account, run: $ gcloud config set account `ACCOUNT`
- دستور زیر را در Cloud Shell اجرا کنید تا تأیید کنید که دستور gcloud از پروژه شما اطلاع دارد:
gcloud config list project
خروجی فرمان
[core] project = <PROJECT_ID>
اگر اینطور نیست، می توانید آن را با این دستور تنظیم کنید:
gcloud config set project <PROJECT_ID>
خروجی فرمان
Updated property [core/project].
3. API ها را فعال کنید و متغیرهای محیط را تنظیم کنید
API ها را فعال کنید
قبل از اینکه بتوانید از این کد لبه استفاده کنید، چندین API وجود دارد که باید آنها را فعال کنید. این آزمایشگاه کد نیاز به استفاده از API های زیر دارد. با اجرای دستور زیر می توانید آن API ها را فعال کنید:
gcloud services enable run.googleapis.com \ cloudbuild.googleapis.com \ aiplatform.googleapis.com \ secretmanager.googleapis.com
تنظیم متغیرهای محیطی
می توانید متغیرهای محیطی را تنظیم کنید که در سراسر این کد لبه مورد استفاده قرار می گیرند.
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 ایجاد و پیکربندی کنید
- در کنسول Firebase ، روی افزودن پروژه کلیک کنید.
- برای افزودن Firebase به یکی از پروژههای Google Cloud موجود، <YOUR_PROJECT_ID> را وارد کنید
- در صورت درخواست، شرایط Firebase را بررسی کرده و بپذیرید.
- روی Continue کلیک کنید.
- برای تأیید طرح صورتحساب Firebase روی تأیید طرح کلیک کنید.
- این اختیاری است که Google Analytics را برای این کد لبه فعال کنید.
- روی Add Firebase کلیک کنید.
- پس از ایجاد پروژه، روی Continue کلیک کنید.
- از منوی Build ، روی پایگاه داده Firestore کلیک کنید.
- روی ایجاد پایگاه داده کلیک کنید.
- منطقه خود را از منوی کشویی Location انتخاب کنید، سپس روی Next کلیک کنید.
- از Start پیش فرض در حالت تولید استفاده کنید، سپس روی Create کلیک کنید.
5. یک حساب کاربری ایجاد کنید
این حساب سرویس توسط Cloud Run برای فراخوانی Vertex AI Gemini API استفاده خواهد شد. این حساب سرویس همچنین دارای مجوز خواندن و نوشتن در Firestor و خواندن اسرار از Secret Manager است.
ابتدا با اجرای این دستور اکانت سرویس ایجاد کنید:
gcloud iam service-accounts create $SERVICE_ACCOUNT \ --display-name="Cloud Run to access Vertex AI APIs"
دوم، نقش Vertex AI User را به حساب سرویس اختصاص دهید.
gcloud projects add-iam-policy-binding $PROJECT_ID \ --member serviceAccount:$SERVICE_ACCOUNT_ADDRESS \ --role=roles/aiplatform.user
اکنون یک راز در Secret Manager ایجاد کنید. سرویس Cloud Run به عنوان متغیرهای محیطی به این راز دسترسی خواهد داشت که در زمان راهاندازی نمونه حل میشود. میتوانید درباره Secrets و Cloud Run اطلاعات بیشتری کسب کنید.
gcloud secrets create $SECRET_ID --replication-policy="automatic" printf "keyboard-cat" | gcloud secrets versions add $SECRET_ID --data-file=-
و به حساب سرویس دسترسی به راز جلسه اکسپرس در Secret Manager بدهید.
gcloud secrets add-iam-policy-binding $SECRET_ID \ --member serviceAccount:$SERVICE_ACCOUNT_ADDRESS \ --role='roles/secretmanager.secretAccessor'
در نهایت، به حساب سرویس دسترسی خواندن و نوشتن به Firestor اجازه دهید.
gcloud projects add-iam-policy-binding $PROJECT_ID \ --member serviceAccount:$SERVICE_ACCOUNT_ADDRESS \ --role=roles/datastore.user
6. سرویس Cloud Run را ایجاد کنید
ابتدا یک دایرکتوری برای کد منبع و سی دی در آن دایرکتوری ایجاد کنید.
mkdir chat-with-gemini && cd chat-with-gemini
سپس یک فایل package.json
با محتوای زیر ایجاد کنید:
{ "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" } }
سپس یک فایل منبع app.js
با محتوای زیر ایجاد کنید. این فایل حاوی نقطه ورود سرویس و حاوی منطق اصلی برنامه است.
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(); });
فایل tailwind.config.js
را برای tailwindCSS ایجاد کنید.
/** @type {import('tailwindcss').Config} */ module.exports = { content: ["./**/*.{html,js}"], theme: { extend: {} }, plugins: [] };
فایل metadataService.js
را برای دریافت شناسه پروژه و منطقه برای سرویس Cloud Run ایجاد کنید. این مقادیر برای نمونهسازی نمونهای از کتابخانههای کلاینت 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 { // 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
ایجاد کنید
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>`;
در نهایت، یک فایل input.css
برای tailwindCSS ایجاد کنید.
@tailwind base; @tailwind components; @tailwind utilities;
اکنون یک فهرست public
جدید ایجاد کنید.
mkdir public cd public
و در آن دایرکتوری عمومی، فایل index.html
را برای قسمت جلویی ایجاد کنید که از 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 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. سرویس را به صورت محلی اجرا کنید
ابتدا، مطمئن شوید که در دایرکتوری root chat-with-gemini
برای Codelab خود هستید.
cd .. && pwd
سپس با اجرای دستور زیر، وابستگی ها را نصب کنید:
npm install
استفاده از ADC هنگام اجرای محلی
اگر در Cloud Shell در حال اجرا هستید، در حال حاضر روی یک ماشین مجازی موتور محاسباتی گوگل کار می کنید. اعتبارنامه شما مرتبط با این ماشین مجازی (همانطور که با اجرای gcloud auth list
نشان داده شده است) به طور خودکار توسط Application Default Credentials استفاده می شود، بنابراین لازم نیست از دستور gcloud auth application-default login
استفاده کنید. می توانید به بخش Create a local session secret بروید
با این حال، اگر در ترمینال محلی خود در حال اجرا هستید (یعنی نه در Cloud Shell)، باید از اعتبارنامه پیش فرض برنامه برای احراز هویت در API های Google استفاده کنید. شما می توانید 1) با استفاده از اطلاعات کاربری خود وارد شوید (به شرطی که هر دو نقش کاربر Vertex AI و کاربر Datastore را داشته باشید) یا 2) می توانید با جعل هویت حساب سرویس مورد استفاده در این Codelab وارد سیستم شوید.
گزینه 1) استفاده از اعتبارنامه خود برای ADC
اگر میخواهید از اعتبارنامههای خود استفاده کنید، میتوانید ابتدا gcloud auth list
را اجرا کنید تا نحوه احراز هویت خود را در gcloud تأیید کنید. در مرحله بعد، ممکن است لازم باشد به هویت خود نقش Vertex AI User را بدهید. اگر هویت شما دارای نقش مالک است، از قبل این نقش کاربری Vertex AI را دارید. اگر نه، میتوانید این دستور را اجرا کنید تا به هویت خود، نقش کاربر Vertex AI و نقش کاربر 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
سپس دستور زیر را اجرا کنید
gcloud auth application-default login
گزینه 2) جعل هویت یک حساب سرویس برای ADC
اگر میخواهید از حساب سرویس ایجاد شده در این کد لبه استفاده کنید، حساب کاربری شما باید نقش ایجاد کننده رمز حساب سرویس را داشته باشد. با اجرای دستور زیر می توانید این نقش را بدست آورید:
gcloud projects add-iam-policy-binding $PROJECT_ID \ --member user:$USER \ --role=roles/iam.serviceAccountTokenCreator
در مرحله بعد، دستور زیر را برای استفاده از ADC با حساب سرویس اجرا می کنید
gcloud auth application-default login --impersonate-service-account=$SERVICE_ACCOUNT_ADDRESS
یک راز جلسه محلی ایجاد کنید
اکنون، یک راز جلسه محلی برای توسعه محلی ایجاد کنید.
export SESSION_SECRET=local-secret
برنامه را به صورت محلی اجرا کنید
در نهایت، می توانید برنامه را با اجرای اسکریپت زیر راه اندازی کنید. این اسکریپت همچنین فایل output.css را از tailwindCSS تولید می کند.
npm run dev
با باز کردن دکمه Web Preview و انتخاب Preview Port 8080 می توانید پیش نمایش وب سایت را مشاهده کنید
8. سرویس را مستقر کنید
ابتدا این دستور را اجرا کنید تا استقرار شروع شود و حساب سرویس مورد استفاده را مشخص کنید. اگر یک حساب سرویس مشخص نشده باشد، از حساب پیش فرض سرویس محاسباتی استفاده می شود.
gcloud run deploy $SERVICE \ --service-account $SERVICE_ACCOUNT_ADDRESS \ --source . \ --region $REGION \ --allow-unauthenticated \ --set-secrets="SESSION_SECRET=$(echo $SECRET_ID):1"
اگر از شما خواسته شد که "استقرار از منبع به یک مخزن Artifact Registry Docker برای ذخیره کانتینرهای ساخته شده نیاز دارد. مخزنی به نام [cloud-run-source-deploy] در منطقه [us-central1] ایجاد می شود."، "y" را بزنید تا قبول کنید و ادامه دهید
9. سرویس را تست کنید
پس از استقرار، URL سرویس را در مرورگر وب خود باز کنید. سپس یک سوال از Gemini بپرسید، به عنوان مثال "من گیتار تمرین می کنم اما مهندس نرم افزار هم هستم. وقتی "C#" را می بینم، آیا باید آن را به عنوان یک زبان برنامه نویسی یا یک نت موسیقی در نظر بگیرم؟ کدام یک را انتخاب کنم؟
10. تبریک می گویم!
برای تکمیل کد لبه تبریک می گویم!
توصیه میکنیم اسناد Cloud Run و Vertex AI Gemini را مرور کنید.
آنچه را پوشش داده ایم
- نحوه استفاده از htmx، tailwindcss و express.js برای ساخت سرویس Cloud Run
- نحوه استفاده از کتابخانه های کلاینت AI Vertex برای احراز هویت در Google API
- نحوه ایجاد یک ربات چت برای تعامل با مدل Gemini
- نحوه استقرار در سرویس اجرای ابری بدون فایل داکر
- نحوه استفاده از فروشگاه جلسه اکسپرس با پشتیبانی Google Cloud Firestore
11. پاکسازی کنید
برای جلوگیری از هزینههای غیرعمدی، (به عنوان مثال، اگر سرویسهای Cloud Run به طور ناخواسته بیشتر از تخصیص فراخوانی ماهانه Cloud Run در ردیف رایگان فراخوانی میشوند)، میتوانید Cloud Run را حذف کنید یا پروژهای را که در مرحله 2 ایجاد کردهاید حذف کنید.
برای حذف سرویس Cloud Run، به کنسول Cloud Run Cloud در https://console.cloud.google.com/run بروید و سرویس chat-with-gemini
را حذف کنید. همچنین ممکن است بخواهید حساب سرویس vertex-ai-caller
حذف کنید یا نقش Vertex AI User را لغو کنید تا از تماسهای غیرعمدی با Gemini جلوگیری کنید.
اگر تصمیم به حذف کل پروژه دارید، میتوانید به https://console.cloud.google.com/cloud-resource-manager بروید، پروژهای را که در مرحله ۲ ایجاد کردهاید انتخاب کنید و حذف را انتخاب کنید. اگر پروژه را حذف کنید، باید پروژه ها را در Cloud SDK خود تغییر دهید. با اجرای gcloud projects list
می توانید لیست تمام پروژه های موجود را مشاهده کنید.