1. Введение
Обзор
В этой лаборатории кода вы увидите, как создать базового чат-бота, написанного на узле, с помощью API Vertex AI Gemini и клиентской библиотеки Vertex AI. Это приложение использует хранилище экспресс-сессий, поддерживаемое Google Cloud Firestore .
Что вы узнаете
- Как использовать htmx, Tailwindcss и express.js для создания службы Cloud Run
- Как использовать клиентские библиотеки Vertex AI для аутентификации в API Google
- Как создать чат-бота для взаимодействия с моделью Gemini
- Как выполнить развертывание в облачной службе без файла Docker
- Как использовать хранилище экспресс-сессий, поддерживаемое Google Cloud Firestore
2. Настройка и требования
Предварительные условия
- Вы вошли в облачную консоль.
- Ранее вы развернули службу Cloud Run. Например, вы можете выполнить развертывание веб-службы из краткого руководства по исходному коду, чтобы начать работу.
Активировать Cloud Shell
- В Cloud Console нажмите «Активировать Cloud Shell». .
Если вы запускаете Cloud Shell впервые, вы увидите промежуточный экран с описанием того, что это такое. Если вам был представлен промежуточный экран, нажмите «Продолжить» .
Подготовка и подключение к Cloud Shell займет всего несколько минут.
Эта виртуальная машина загружена всеми необходимыми инструментами разработки. Он предлагает постоянный домашний каталог объемом 5 ГБ и работает в Google Cloud, что значительно повышает производительность сети и аутентификацию. Большую часть, если не всю, работу в этой лаборатории кода можно выполнить с помощью браузера.
После подключения к Cloud Shell вы увидите, что вы прошли аутентификацию и что для проекта установлен идентификатор вашего проекта.
- Выполните следующую команду в 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 нажмите «Добавить проект» .
- Введите <YOUR_PROJECT_ID>, чтобы добавить Firebase в один из существующих проектов Google Cloud.
- При появлении запроса прочтите и примите условия Firebase .
- Нажмите Продолжить .
- Нажмите «Подтвердить план» , чтобы подтвердить тарифный план Firebase.
- Включать Google Analytics для этой лаборатории кода необязательно.
- Нажмите «Добавить Firebase» .
- Когда проект будет создан, нажмите «Продолжить» .
- В меню «Создать» выберите «База данных Firestore» .
- Нажмите Создать базу данных .
- Выберите свой регион в раскрывающемся списке «Местоположение» , затем нажмите «Далее» .
- Используйте режим запуска по умолчанию в рабочем режиме , затем нажмите «Создать» .
5. Создайте учетную запись службы.
Эта учетная запись службы будет использоваться Cloud Run для вызова API Vertex AI Gemini. Эта учетная запись службы также будет иметь разрешения на чтение и запись в Firestore, а также на чтение секретов из Secret Manager.
Сначала создайте учетную запись службы, выполнив следующую команду:
gcloud iam service-accounts create $SERVICE_ACCOUNT \ --display-name="Cloud Run to access Vertex AI APIs"
Во-вторых, назначьте учетной записи службы роль пользователя Vertex AI.
gcloud projects add-iam-policy-binding $PROJECT_ID \ --member serviceAccount:$SERVICE_ACCOUNT_ADDRESS \ --role=roles/aiplatform.user
Теперь создайте секрет в Secret Manager. Служба Cloud Run будет получать доступ к этому секрету как к переменным среды, которые разрешаются во время запуска экземпляра. Вы можете узнать больше о секретах и 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'
Наконец, предоставьте сервисной учетной записи доступ на чтение и запись в Firestore.
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. Запустите службу локально
Во-первых, убедитесь, что вы находитесь в корневом каталоге chat-with-gemini
для вашей лаборатории кода.
cd .. && pwd
Затем установите зависимости, выполнив следующую команду:
npm install
Использование ADC при локальном запуске
Если вы работаете в Cloud Shell, вы уже работаете на виртуальной машине Google Compute Engine. Ваши учетные данные, связанные с этой виртуальной машиной (как показано при запуске gcloud auth list
), будут автоматически использоваться учетными данными приложения по умолчанию, поэтому нет необходимости использовать команду gcloud auth application-default login
. Вы можете перейти к разделу «Создание секрета локального сеанса».
Однако если вы работаете на локальном терминале (т. е. не в Cloud Shell), вам потребуется использовать учетные данные приложения по умолчанию для аутентификации в API Google. Вы можете либо 1) войти в систему, используя свои учетные данные (при условии, что у вас есть роли пользователя Vertex AI и пользователя хранилища данных), либо 2) вы можете войти в систему, выдав себя за учетную запись службы, используемую в этой лаборатории кода.
Вариант 1) Использование ваших учетных данных для ADC
Если вы хотите использовать свои учетные данные, вы можете сначала запустить gcloud auth list
чтобы проверить, как вы прошли аутентификацию в gcloud. Далее вам может потребоваться предоставить себе роль пользователя Vertex AI. Если ваша личность имеет роль владельца, у вас уже есть эта роль пользователя Vertex AI. Если нет, вы можете запустить эту команду, чтобы предоставить вам роль пользователя Vertex AI и роль пользователя хранилища данных.
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
Вы можете просмотреть веб-сайт, открыв кнопку «Просмотр в Интернете» и выбрав «Просмотр порта 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-адрес службы в веб-браузере. Затем задайте Близнецам вопрос, например: «Я занимаюсь игрой на гитаре, но я также инженер-программист. Когда я вижу «C#», должен ли я думать о нем как о языке программирования или музыкальной ноте? Какой из них мне выбрать?»
10. Поздравляем!
Поздравляем с завершением работы над кодом!
Рекомендуем ознакомиться с документацией Cloud Run и Vertex AI Gemini APIs .
Что мы рассмотрели
- Как использовать htmx, Tailwindcss и express.js для создания службы Cloud Run
- Как использовать клиентские библиотеки Vertex AI для аутентификации в API Google
- Как создать чат-бота для взаимодействия с моделью Gemini
- Как выполнить развертывание в облачной службе без файла Docker
- Как использовать хранилище экспресс-сессий, поддерживаемое Google Cloud Firestore
11. Очистка
Чтобы избежать непреднамеренных расходов (например, если службы Cloud Run по неосторожности вызываются больше раз, чем вам ежемесячно выделено количество вызовов Cloud Run на уровне бесплатного пользования ), вы можете либо удалить Cloud Run, либо удалить проект, созданный на шаге 2.
Чтобы удалить службу Cloud Run, перейдите в облачную консоль Cloud Run по адресу https://console.cloud.google.com/run и удалите службу chat-with-gemini
. Вы также можете удалить учетную запись службы vertex-ai-caller
или отозвать роль пользователя Vertex AI, чтобы избежать непреднамеренных вызовов Gemini.
Если вы решите удалить весь проект, вы можете перейти на https://console.cloud.google.com/cloud-resource-manager , выбрать проект, созданный на шаге 2, и нажать «Удалить». Если вы удалите проект, вам придется изменить проекты в Cloud SDK. Вы можете просмотреть список всех доступных проектов, запустив gcloud projects list
.