{"id":377,"date":"2026-03-20T20:14:34","date_gmt":"2026-03-20T12:14:34","guid":{"rendered":"https:\/\/flycloud.io\/?p=377"},"modified":"2026-10-07T20:15:05","modified_gmt":"2026-10-07T12:15:05","slug":"%e5%9f%ba%e4%ba%8e-gemini-%e4%b8%8e-vertex-ai-%e6%9e%84%e5%bb%ba%e9%9b%b6%e5%94%ae%e9%94%80%e5%94%ae%e8%ae%a2%e5%8d%95-ai-agent","status":"publish","type":"post","link":"https:\/\/flycloud.io\/en\/2026\/03\/20\/%e5%9f%ba%e4%ba%8e-gemini-%e4%b8%8e-vertex-ai-%e6%9e%84%e5%bb%ba%e9%9b%b6%e5%94%ae%e9%94%80%e5%94%ae%e8%ae%a2%e5%8d%95-ai-agent\/","title":{"rendered":"\u57fa\u4e8e Gemini \u4e0e Vertex AI \u6784\u5efa\u96f6\u552e\u9500\u552e\u8ba2\u5355 AI Agent"},"content":{"rendered":"<h2 class=\"wp-block-heading\">1. \u6982\u89c8<\/h2>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"what-youll-build\"><strong>\u6784\u5efa\u5185\u5bb9<\/strong><\/h2>\n\n\n\n<p>\u60a8\u7684\u5e94\u7528\u5c06\uff1a<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>\u5b83\u9002\u7528\u4e8e\u79fb\u52a8\u8bbe\u5907\u6216\u684c\u9762\u8bbe\u5907\u3002<\/li>\n\n\n\n<li>\u60a8\u53ef\u4ee5\u62cd\u6444\u5546\u54c1\u7167\u7247\uff0c\u7136\u540e\u901a\u8fc7\u8bed\u97f3\u804a\u5929\u8ba2\u8d2d\u5546\u54c1\u3002<\/li>\n\n\n\n<li>\u8be5\u5e94\u7528\u7684\u8fd0\u4f5c\u65b9\u5f0f\u5982\u4e0b\uff1a\u60a8\u53ea\u9700\u62cd\u6444\u5546\u54c1\u7684\u7167\u7247\uff0c\u7136\u540e\u8bf4\u51fa\u201c<strong>\u6211\u8981\u8ba2\u8d2d\u6b64\u5546\u54c1\uff0c3 \u7bb1\u3002\u6211\u662f\u6c83\u5c14\u739b\u6a80\u9999\u5c71\u5206\u5e97\u7684\u7ecf\u7406\u3002<\/strong>\u201d\u5e94\u7528\u4f1a\u5c06\u7167\u7247\u4e0a\u4f20\u5230 Cloud Storage\uff0c\u5e76\u8f6c\u5199\u60a8\u7684\u5f55\u97f3\u3002\u7136\u540e\uff0c\u6b64\u4fe1\u606f\u4f1a\u53d1\u9001\u5230 Vertex AI \u4e0a\u7684 Gemini \u6a21\u578b\uff0c\u8be5\u6a21\u578b\u4f1a\u8bc6\u522b\u5546\u54c1\u548c\u60a8\u7684\u5546\u5e97\uff08\u6a80\u9999\u5c71\u6c83\u5c14\u739b\uff09\u3002\u5982\u679c\u8bf7\u6c42\u7b26\u5408\u9500\u552e\u8ba2\u5355\u6761\u4ef6\uff0c\u7cfb\u7edf\u4f1a\u751f\u6210\u4e00\u4e2a\u5177\u6709\u552f\u4e00 ID \u7684\u9500\u552e\u8ba2\u5355\u3002<\/li>\n<\/ul>\n\n\n\n<figure class=\"wp-block-image\"><img decoding=\"async\" src=\"https:\/\/codelabs.developers.google.com\/static\/codelabs\/GenAI-Agent-Retail\/img\/c8333d8139d8764c.png?hl=zh-cn\" alt=\"c8333d8139d8764c.png\"\/><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\">2. \u5b66\u4e60\u5185\u5bb9<\/h2>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"what-youll-learn\"><strong>\u5b66\u4e60\u5185\u5bb9<\/strong><\/h2>\n\n\n\n<ul class=\"wp-block-list\">\n<li>\u5982\u4f55\u4f7f\u7528 Vertex AI \u521b\u5efa AI \u667a\u80fd\u4f53<\/li>\n\n\n\n<li>\u5982\u4f55\u5411 Speech-to-Text API \u670d\u52a1\u53d1\u9001\u97f3\u9891\uff0c\u5e76\u63a5\u6536\u6587\u5b57\u8f6c\u5199\u7ed3\u679c<\/li>\n\n\n\n<li>\u5982\u4f55\u5728 Cloud Run \u4e0a\u90e8\u7f72 AI \u667a\u80fd\u4f53<\/li>\n\n\n\n<li><\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"what-youll-need\"><strong>\u6240\u9700\u6761\u4ef6<\/strong><\/h2>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Google Cloud \u8d26\u53f7<\/li>\n\n\n\n<li>\u719f\u6089 Python\u3001JavaScript \u548c Google Cloud<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"architecture\"><strong>\u67b6\u6784<\/strong><\/h2>\n\n\n\n<figure class=\"wp-block-image\"><img decoding=\"async\" src=\"https:\/\/codelabs.developers.google.com\/static\/codelabs\/GenAI-Agent-Retail\/img\/b21e2a3deedb60ec.png?hl=zh-cn\" alt=\"b21e2a3deedb60ec.png\"\/><\/figure>\n\n\n\n<p>\u6b64\u4ee3\u7406\u53ef\u8ba9\u60a8\u901a\u8fc7\u56fe\u7247\u548c\u6587\u672c\u63d0\u793a\uff0c\u5229\u7528 Gemini \u7684\u591a\u6a21\u6001\u529f\u80fd\u7b80\u5316\u8ba2\u8d2d\u6d41\u7a0b\u3002\u5982\u679c\u8ba2\u5355\u662f\u901a\u8fc7\u8bed\u97f3\u4e0b\u8fbe\u7684\uff0cGoogle Speech \u7684 Chirp 2 \u6a21\u578b\u4f1a\u5c06\u5176\u8f6c\u5199\u4e3a\u6587\u672c\uff0c\u7136\u540e\u5c06\u8be5\u6587\u672c\u4e0e\u63d0\u4f9b\u7684\u56fe\u7247\u4e00\u8d77\u7528\u4e8e\u67e5\u8be2 Vertex AI \u7684 Gemini \u6a21\u578b\u3002<\/p>\n\n\n\n<p>\u6211\u4eec\u5c06\u6784\u5efa\uff1a<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li>\u521b\u5efa\u5f00\u53d1\u73af\u5883<\/li>\n\n\n\n<li>\u4f9b\u7528\u6237\u901a\u8fc7\u79fb\u52a8\u8bbe\u5907\u6216 PC \u8c03\u7528\u7684 Flask \u5e94\u7528\u3002\u5e94\u7528\u5c06\u5728 Cloud Run \u4e0a\u8fd0\u884c\u3002<\/li>\n<\/ol>\n\n\n\n<h2 class=\"wp-block-heading\">3. \u51c6\u5907\u5de5\u4f5c<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"enable-apis\">\u542f\u7528 API<\/h3>\n\n\n\n<p>\u542f\u7528\u5b9e\u9a8c\u6240\u9700\u7684 API\u3002\u8fd9\u9700\u8981\u51e0\u5206\u949f\u65f6\u95f4\u3002<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">gcloud services enable \\\n  run.googleapis.com \\\n  cloudbuild.googleapis.com \\\n  aiplatform.googleapis.com \\\n  speech.googleapis.com \\\n  sqladmin.googleapis.com \\\n  logging.googleapis.com \\\n  compute.googleapis.com \\\n  servicenetworking.googleapis.com \\\n  monitoring.googleapis.com\n<\/pre>\n\n\n\n<p>\u9884\u671f\u63a7\u5236\u53f0\u8f93\u51fa\uff1a<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">Operation \"operations\/acf.p2-639929424533-ffa3a09b-7663-4b31-8f78-5872bf4ad778\" finished successfully.\n<\/pre>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"set-up-the-environments\">\u8bbe\u7f6e\u73af\u5883<\/h3>\n\n\n\n<p>\u5728 CLI \u547d\u4ee4\u8bbe\u7f6e Google Cloud \u73af\u5883\u7684\u53c2\u6570\u4e4b\u524d\u3002<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">export PROJECT_ID=\"&lt;YOUR_PROJECT_ID&gt;\"\nexport VPC_NAME=\"&lt;YOUR_VPC_NAME&gt;\" e.g : demonetwork\nexport SUBNET_NAME=\"&lt;YOUR_SUBNET_NAME&gt;\" e.g : genai-subnet\nexport REGION=\"&lt;YOUR_REGION&gt;\" e.g : us-central1\nexport GENAI_BUCKET=\"&lt;YOUR BUCKET FOR AGENT&gt;\" # eg&gt; genai-${PROJECT_ID}\n<\/pre>\n\n\n\n<p>For example :<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">export PROJECT_ID=$(gcloud config get-value project)\nexport VPC_NAME=\"demonetwork\" \nexport SUBNET_NAME=\"genai-subnet\" \nexport REGION=\"us-central1\" \nexport GENAI_BUCKET=\"genai-${PROJECT_ID}\" \n<\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">4. \u6784\u5efa\u57fa\u7840\u67b6\u6784<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"create-the-network-for-your-app\">\u4e3a\u60a8\u7684\u5e94\u7528\u521b\u5efa\u7f51\u7edc<\/h3>\n\n\n\n<p>\u4e3a\u5e94\u7528\u521b\u5efa VPC\u3002\u5982\u9700\u521b\u5efa\u540d\u4e3a\u201cdemonetwork\u201d\u7684 VPC\uff0c\u8bf7\u8fd0\u884c\u4ee5\u4e0b\u547d\u4ee4\uff1a<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">gcloud compute networks create demonetwork \\\n    --subnet-mode custom\n<\/pre>\n\n\n\n<p>\u5982\u9700\u5728\u201cdemonetwork\u201d\u7f51\u7edc\u4e2d\u521b\u5efa\u5730\u5740\u8303\u56f4\u4e3a 10.10.0.0\/24 \u7684\u5b50\u7f51\u201cgenai-subnet\u201d\uff0c\u8bf7\u8fd0\u884c\u4ee5\u4e0b\u547d\u4ee4\uff1a<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">gcloud compute networks subnets create genai-subnet \\\n    --network demonetwork \\\n    --region us-central1 \\\n    --range 10.10.0.0\/24\n<\/pre>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"create-a-cloud-sql-for-postgresql\">\u521b\u5efa Cloud SQL for PostgreSQL<\/h3>\n\n\n\n<p>\u4e3a\u4e13\u7528\u670d\u52a1\u8bbf\u95ee\u901a\u9053\u5206\u914d\u7684 IP \u5730\u5740\u8303\u56f4\u3002<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">gcloud compute addresses create google-managed-services-my-network \\\n    --global \\\n    --purpose=VPC_PEERING \\\n    --prefix-length=16 \\\n    --description=\"peering range for Google\" \\\n    --network=demonetwork\n<\/pre>\n\n\n\n<p>\u521b\u5efa\u4e13\u7528\u8fde\u63a5\u3002<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">gcloud services vpc-peerings connect \\\n    --service=servicenetworking.googleapis.com \\\n    --ranges=google-managed-services-my-network \\\n    --network=demonetwork\n<\/pre>\n\n\n\n<p>\u8fd0\u884c <strong>gcloud sql instances create<\/strong> \u547d\u4ee4\u4ee5\u521b\u5efa Cloud SQL \u5b9e\u4f8b\u3002<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">gcloud sql instances create sql-retail-genai \\\n  --database-version POSTGRES_14 \\\n  --tier db-f1-micro \\\n  --region=$REGION \\\n  --project=$PROJECT_ID \\\n  --network=projects\/${PROJECT_ID}\/global\/networks\/${VPC_NAME} \\\n  --no-assign-ip \\\n  --enable-google-private-path\n<\/pre>\n\n\n\n<p>\u6b64\u547d\u4ee4\u53ef\u80fd\u9700\u8981\u51e0\u5206\u949f\u65f6\u95f4\u624d\u80fd\u5b8c\u6210\u3002<\/p>\n\n\n\n<p>\u9884\u671f\u63a7\u5236\u53f0\u8f93\u51fa\uff1a<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">Created [https:\/\/sqladmin.googleapis.com\/sql\/v1beta4\/projects\/evident-trees-438609-q3\/instances\/sql-retail-genai].\nNAME: sql-retail-genai\nDATABASE_VERSION: POSTGRES_14\nLOCATION: us-central1-c\nTIER: db-f1-micro\nPRIMARY_ADDRESS: -\nPRIVATE_ADDRESS: 10.66.0.3\nSTATUS: RUNNABLE\n<\/pre>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"create-a-database-for-your-app-and-the-user\">\u4e3a\u5e94\u7528\u548c\u7528\u6237\u521b\u5efa\u6570\u636e\u5e93<\/h3>\n\n\n\n<p>\u8fd0\u884c <strong>gcloud sql databases create<\/strong> \u547d\u4ee4\uff0c\u5728 sql-retail-genai \u4e2d\u521b\u5efa Cloud SQL \u6570\u636e\u5e93\u3002<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">gcloud sql databases create retail-orders \\\n  --instance sql-retail-genai\n<\/pre>\n\n\n\n<p>\u521b\u5efa PostgreSQL \u6570\u636e\u5e93\u7528\u6237\u540e\uff0c\u6700\u597d\u66f4\u6539\u5bc6\u7801\u3002<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">gcloud sql users create aiagent --instance sql-retail-genai --password \"genaiaigent2@\"\n<\/pre>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"create-a-bucket-for-storing-images\"><strong>\u521b\u5efa\u7528\u4e8e\u5b58\u50a8\u56fe\u7247\u7684\u5b58\u50a8\u6876<\/strong><\/h3>\n\n\n\n<p>\u4e3a\u4ee3\u7406\u521b\u5efa\u79c1\u5bc6\u5b58\u50a8\u6876<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">gsutil mb -l $REGION gs:\/\/$GENAI_BUCKET\n<\/pre>\n\n\n\n<p>\u66f4\u65b0\u5b58\u50a8\u6876\u6743\u9650<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">gsutil iam ch serviceAccount:&lt;your service account&gt;: roles\/storage.objectUser gs:\/\/$GENAI_BUCKET\n<\/pre>\n\n\n\n<p>\u5982\u679c\u5047\u8bbe\u4f7f\u7528\u9ed8\u8ba4\u8ba1\u7b97\u670d\u52a1\u8d26\u53f7\uff1a<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">gsutil iam ch serviceAccount:$(gcloud projects describe $PROJECT_ID --format=\"value(projectNumber)\")-compute@developer.gserviceaccount.com:roles\/storage.objectUser gs:\/\/$GENAI_BUCKET<br><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">5. \u4e3a\u5e94\u7528\u51c6\u5907\u4ee3\u7801<\/h2>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"prepare-the-codes\">\u51c6\u5907\u4ee3\u7801<\/h2>\n\n\n\n<p>\u7528\u4e8e\u4e0b\u5355\u7684 Web \u5e94\u7528\u662f\u4f7f\u7528 Flask \u6784\u5efa\u7684\uff0c\u53ef\u4ee5\u5728\u79fb\u52a8\u8bbe\u5907\u6216 PC \u4e0a\u7684 Web \u6d4f\u89c8\u5668\u4e2d\u8fd0\u884c\u3002\u5b83\u4f1a\u8bbf\u95ee\u6240\u8fde\u63a5\u8bbe\u5907\u7684\u9ea6\u514b\u98ce\u548c\u6444\u50cf\u5934\uff0c\u5e76\u4f7f\u7528 Google Speech \u7684 Chirp 2 \u6a21\u578b\u548c Vertex AI \u7684 Gemini Pro 1.5 \u6a21\u578b\u3002\u8ba2\u5355\u7ed3\u679c\u5b58\u50a8\u5728 Cloud SQL \u6570\u636e\u5e93\u4e2d\u3002<\/p>\n\n\n\n<p>\u5982\u679c\u60a8\u4f7f\u7528\u4e86\u4e0a\u4e00\u9875\u4e2d\u63d0\u4f9b\u7684\u793a\u4f8b\u73af\u5883\u53d8\u91cf\u540d\u79f0\uff0c\u5219\u53ef\u4ee5\u76f4\u63a5\u4f7f\u7528\u4ee5\u4e0b\u4ee3\u7801\uff0c\u65e0\u9700\u8fdb\u884c\u4fee\u6539\u3002\u5982\u679c\u60a8\u81ea\u5b9a\u4e49\u4e86\u73af\u5883\u53d8\u91cf\u540d\u79f0\uff0c\u5219\u9700\u8981\u76f8\u5e94\u5730\u66f4\u6539\u4ee3\u7801\u4e2d\u7684\u4e00\u4e9b\u53d8\u91cf\u503c\u3002<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"create-two-directories-as-follows.\"><strong>\u6309\u5982\u4e0b\u65b9\u5f0f\u521b\u5efa\u4e24\u4e2a\u76ee\u5f55\u3002<\/strong><\/h3>\n\n\n\n<pre class=\"wp-block-preformatted\">mkdir -p genai-agent\/templates\n<\/pre>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"create-a-requirements.txt\">\u521b\u5efa requirements.txt<\/h3>\n\n\n\n<pre class=\"wp-block-preformatted\">vi ~\/genai-agent\/requirements.txt\n<\/pre>\n\n\n\n<p>\u5728\u6587\u672c\u6587\u4ef6\u4e2d\u8f93\u5165\u8f6f\u4ef6\u5305\u5217\u8868\u3002<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>aiofiles==24.1.0<br>aiohappyeyeballs==2.4.3<br>aiohttp==3.10.9<br>aiosignal==1.3.1<br>annotated-types==0.7.0<br>asn1crypto==1.5.1<br>attrs==24.2.0<br>blinker==1.8.2<br>cachetools==5.5.0<br>certifi==2024.8.30<br>cffi==1.17.1<br>charset-normalizer==3.3.2<br>click==8.1.7<br>cloud-sql-python-connector==1.12.1<br>cryptography==43.0.1<br>docstring_parser==0.16<br>Flask==3.0.3<br>frozenlist==1.4.1<br>google-api-core==2.20.0<br>google-auth==2.35.0<br>google-cloud-aiplatform==1.69.0<br>google-cloud-bigquery==3.26.0<br>google-cloud-core==2.4.1<br>google-cloud-resource-manager==1.12.5<br>google-cloud-speech==2.27.0<br>google-cloud-storage==2.18.2<br>google-crc32c==1.6.0<br>google-resumable-media==2.7.2<br>googleapis-common-protos==1.65.0<br>greenlet==3.1.1<br>grpc-google-iam-v1==0.13.1<br>grpcio==1.66.2<br>grpcio-status==1.66.2<br>idna==3.10<br>itsdangerous==2.2.0<br>Jinja2==3.1.4<br>MarkupSafe==3.0.0<br>multidict==6.1.0<br>numpy==2.1.2<br>packaging==24.1<br>pg8000==1.31.2<br>pgvector==0.3.5<br>proto-plus==1.24.0<br>protobuf==5.28.2<br>pyasn1==0.6.1<br>pyasn1_modules==0.4.1<br>pycparser==2.22<br>pydantic==2.9.2<br>pydantic_core==2.23.4<br>python-dateutil==2.9.0.post0<br>requests==2.32.3<br>rsa==4.9<br>scramp==1.4.5<br>shapely==2.0.6<br>six==1.16.0<br>SQLAlchemy==2.0.35<br>typing_extensions==4.12.2<br>urllib3==2.2.3<br>Werkzeug==3.0.4<br>yarl==1.13.1<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"create-a-main.py\">\u521b\u5efa main.py<\/h3>\n\n\n\n<pre class=\"wp-block-preformatted\">vi ~\/genai-agent\/main.py\n<\/pre>\n\n\n\n<p>\u5728 main.py \u6587\u4ef6\u4e2d\u8f93\u5165 Python \u4ee3\u7801\u3002<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>from flask import Flask, render_template, request, jsonify, Response<br>import os<br>import base64<br>from google.api_core.client_options import ClientOptions<br>from google.cloud.speech_v2 import SpeechClient<br>from google.cloud.speech_v2.types import cloud_speech<br><br>import vertexai<br>from vertexai.generative_models import GenerativeModel, Part, SafetySetting<br>from google.cloud import storage<br>import uuid &nbsp;# Import the uuid module<br>from typing import Dict &nbsp;# Add this import<br>import datetime<br>import json<br>import re<br><br>import os<br>from google.cloud.sql.connector import Connector<br>import pg8000<br>import sqlalchemy<br>from sqlalchemy import create_engine, text<br><br>app = Flask(__name__)<br><br># Replace with your actual project ID<br>project_id = os.environ.get(\"PROJECT_ID\")<br><br># Use a connection pool to reuse connections and improve performance<br># This also handles connection lifecycle management automatically<br>engine = None<br><br># Configure Google Cloud Storage<br>storage_client = storage.Client()<br>bucket_name = os.environ.get(\"GENAI_BUCKET\") &nbsp;<br>client = SpeechClient(<br>&nbsp; &nbsp; client_options=ClientOptions(<br>&nbsp; &nbsp; &nbsp; &nbsp; api_endpoint=\"us-central1-speech.googleapis.com\",<br>&nbsp; &nbsp; ),<br>)<br><br>def get_engine():<br>&nbsp; &nbsp; global engine &nbsp;# Use global to access\/modify the global engine variable<br>&nbsp; &nbsp; if engine is None: &nbsp;# Create the engine only once<br>&nbsp; &nbsp; &nbsp; &nbsp; connector = Connector()<br><br>&nbsp; &nbsp; &nbsp; &nbsp; def getconn() -&gt; pg8000.dbapi.Connection:<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; conn: pg8000.dbapi.Connection = connector.connect(<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; os.environ&#91;\"INSTANCE_CONNECTION_NAME\"], &nbsp;# Cloud SQL instance connection name<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; \"pg8000\",<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; user=os.environ&#91;\"DB_USER\"],<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; password=os.environ&#91;\"DB_PASS\"],<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; db=os.environ&#91;\"DB_NAME\"],<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; ip_type=\"PRIVATE\",<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; )<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return conn<br><br>&nbsp; &nbsp; &nbsp; &nbsp; engine = create_engine(<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; \"postgresql+pg8000:\/\/\",<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; creator=getconn,<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; pool_pre_ping=True, &nbsp;# Check connection validity before use<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; pool_size=5, &nbsp;# Adjust pool size as needed<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; max_overflow=2, # &nbsp;Allow some overflow for bursts<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; pool_recycle=300, # &nbsp;Recycle connections after 5 minutes<br>&nbsp; &nbsp; &nbsp; &nbsp; )<br>&nbsp; &nbsp; return engine<br><br>def migrate_db() -&gt; None:<br>&nbsp; &nbsp; engine = get_engine() &nbsp;# Get the engine (creates it if necessary)<br>&nbsp; &nbsp; with engine.begin() as conn:<br>&nbsp; &nbsp; &nbsp; &nbsp; sql = \"\"\"<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; CREATE TABLE IF NOT EXISTS image_sales_orders (<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; order_id SERIAL PRIMARY KEY,<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; vendor_name VARCHAR(80) NOT NULL,<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; order_item VARCHAR(100) NOT NULL,<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; order_boxes INT NOT NULL, &nbsp;<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; time_cast TIMESTAMP NOT NULL<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; );<br>&nbsp; &nbsp; &nbsp; &nbsp; \"\"\"<br>&nbsp; &nbsp; &nbsp; &nbsp; conn.execute(text(sql))<br><br><br>@app.before_request<br>def init_db():<br>&nbsp; &nbsp; migrate_db()<br>&nbsp; &nbsp; #print(\"Migration complete.\")<br><br>@app.route('\/')<br>def index():<br>&nbsp; &nbsp; return render_template('index.html')<br><br>@app.route('\/orderlist')<br>def orderlist():<br>&nbsp; &nbsp; engine = get_engine()<br>&nbsp; &nbsp; with engine.connect() as conn:<br>&nbsp; &nbsp; &nbsp; &nbsp; sql = text(\"\"\"<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; SELECT order_id, vendor_name, order_item, order_boxes, time_cast<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; FROM image_sales_orders<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; ORDER BY time_cast DESC<br>&nbsp; &nbsp; &nbsp; &nbsp; \"\"\")<br>&nbsp; &nbsp; &nbsp; &nbsp; result = conn.execute(sql).mappings() &nbsp;# Use .mappings() for dict-like access<br>&nbsp; &nbsp; &nbsp; &nbsp; orders = &#91;]<br>&nbsp; &nbsp; &nbsp; &nbsp; for row in result:<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; order = {<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 'OrderId': row&#91;'order_id'],<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 'VendorName': row&#91;'vendor_name'],<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 'OrderItem': row&#91;'order_item'],<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 'OrderBoxes': row&#91;'order_boxes'],<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 'OrderDate': row&#91;'time_cast'].strftime('%Y-%m-%d'),<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 'OrderTime': row&#91;'time_cast'].strftime('%H:%M:%S'),<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; orders.append(order)<br>&nbsp; &nbsp; return render_template('orderlist.html', orders=orders)<br><br>@app.route(\"\/upload_photo\", methods=&#91;\"POST\"])<br>def upload_photo():<br>&nbsp; &nbsp; # Get the uploaded file<br>&nbsp; &nbsp; file = request.files&#91;\"photo\"]<br><br>&nbsp; &nbsp; # Generate a unique filename<br>&nbsp; &nbsp; filename = f\"{uuid.uuid4()}--{file.filename}\"<br><br>&nbsp; &nbsp; # Upload the file to Google Cloud Storage<br>&nbsp; &nbsp; bucket = storage_client.get_bucket(bucket_name)<br>&nbsp; &nbsp; blob = bucket.blob(filename)<br>&nbsp; &nbsp; generation_match_precondition = 0<br>&nbsp; &nbsp; blob.upload_from_file(file, if_generation_match=generation_match_precondition)<br><br>&nbsp; &nbsp; # Return the destination filename<br>&nbsp; &nbsp; image_url = f\"gs:\/\/{bucket_name}\/{filename}\"<br><br>&nbsp; &nbsp; # Return the destination filename<br>&nbsp; &nbsp; return image_url<br><br>@app.route('\/upload', methods=&#91;'POST'])<br>def upload():<br>&nbsp; &nbsp; audio_data = request.form&#91;'audio_data']<br>&nbsp; &nbsp; audio_data = base64.b64decode(audio_data.split(',')&#91;1])<br><br>&nbsp; &nbsp; audio_path = f\"{uuid.uuid4()}--audio.wav\" <br><br>&nbsp; &nbsp; with open(audio_path, 'wb') as f:<br>&nbsp; &nbsp; &nbsp; &nbsp; f.write(audio_data)<br><br>&nbsp; &nbsp; transcript = transcribe_speech(audio_path)<br>&nbsp; &nbsp; os.remove(audio_path)<br>&nbsp; &nbsp; return jsonify({'transcript': transcript})<br><br>@app.route(\"\/orders\", methods=&#91;\"POST\"])<br>def cast_order() -&gt; Response:<br>&nbsp; &nbsp; prompt = request.form&#91;'transcript']<br>&nbsp; &nbsp; image_url = request.form&#91;'image_url']<br>&nbsp; &nbsp; print(f\"Prompt: {prompt}\")<br>&nbsp; &nbsp; print(f\"Image URL: {image_url}\")<br><br>&nbsp; &nbsp; model_response = generate(image_url=image_url, prompt=prompt)<br>&nbsp; &nbsp; # Extract the text content from the model response<br>&nbsp; &nbsp; response_text = model_response.text if hasattr(model_response, 'text') else str(model_response)<br><br>&nbsp; &nbsp; #print(f\"Response from Model !!!!!!: {response_text}\")<br><br>&nbsp; &nbsp; try:<br>&nbsp; &nbsp; &nbsp; &nbsp; response_json = json.loads(response_text)<br>&nbsp; &nbsp; &nbsp; &nbsp; function_name = response_json.get(\"function\")<br>&nbsp; &nbsp; &nbsp; &nbsp; parameters = response_json.get(\"parameters\")<br><br>&nbsp; &nbsp; except json.JSONDecodeError as e:<br>&nbsp; &nbsp; &nbsp; &nbsp; logging.error(f\"JSON decoding error: {e}\")<br>&nbsp; &nbsp; &nbsp; &nbsp; return Response(<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; \"I cannot fulfill your request because I cannot find the &#91;Product Name], &#91;Quantity (Box)], and &#91;Retail Store Name] in the provided image and prompt.\",<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; status=500<br>&nbsp; &nbsp; &nbsp; &nbsp; )<br><br>&nbsp; &nbsp; if function_name == 'Z_SALES_ORDER_SRV\/orderlistSet':<br>&nbsp; &nbsp; &nbsp; &nbsp; engine = get_engine()<br>&nbsp; &nbsp; &nbsp; &nbsp; with engine.connect() as conn:<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; try:<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; # Explicitly convert order_boxes to integer<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; order_boxes = int(parameters&#91;\"order_boxes\"])<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; vendor_name = parameters&#91;\"vendor_name\"]<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; order_item = parameters&#91;\"order_item\"]<br><br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; # Prepare the SQL statement<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; sql = text(\"\"\"<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; INSERT INTO image_sales_orders (vendor_name, order_item, order_boxes, time_cast)<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; VALUES (:vendor_name, :order_item, :order_boxes, NOW())<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; \"\"\")<br><br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; # Prepare parameters<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; params = {<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; \"vendor_name\": vendor_name,<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; \"order_item\": order_item,<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; \"order_boxes\": order_boxes,<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }<br><br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; # Execute the SQL statement with parameters<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; conn.execute(sql, params)<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; conn.commit()<br><br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; response_message = f\"Dear &#91;{vendor_name}],\\n\\nYour order has been completed as follows. \\n\\nItem Name : {order_item}\\nQTY(Boxes) : {order_boxes}\\n\\nThanks.\"<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return Response(response_message, status=200)<br><br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; except (KeyError, ValueError) as e:<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; logging.error(f\"Error inserting into database: {e}\")<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; response_message = \"Error processing your order. Please check the input data.\"<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return Response(response_message, status=500)<br><br>&nbsp; &nbsp; else:<br>&nbsp; &nbsp; &nbsp; &nbsp; # Handle other function names if necessary<br>&nbsp; &nbsp; &nbsp; &nbsp; return Response(\"Unknown function.\", status=400)<br><br><br>def transcribe_speech(audio_file):<br>&nbsp; &nbsp; with open(audio_file, \"rb\") as f:<br>&nbsp; &nbsp; &nbsp; &nbsp; content = f.read()<br><br>&nbsp; &nbsp; config = cloud_speech.RecognitionConfig(<br>&nbsp; &nbsp; &nbsp; &nbsp; auto_decoding_config=cloud_speech.AutoDetectDecodingConfig(),<br>&nbsp; &nbsp; &nbsp; &nbsp; language_codes=&#91;\"auto\"],<br>&nbsp; &nbsp; &nbsp; &nbsp; #language_codes=&#91;\"ko-KR\"], &nbsp; &nbsp;-- In case that needs to choose specific language <br>&nbsp; &nbsp; &nbsp; &nbsp; model=\"chirp_2\",<br>&nbsp; &nbsp; )<br><br>&nbsp; &nbsp; request = cloud_speech.RecognizeRequest(<br>&nbsp; &nbsp; &nbsp; &nbsp; recognizer=f\"projects\/{project_id}\/locations\/us-central1\/recognizers\/_\",<br>&nbsp; &nbsp; &nbsp; &nbsp; config=config,<br>&nbsp; &nbsp; &nbsp; &nbsp; content=content,<br>&nbsp; &nbsp; )<br><br>&nbsp; &nbsp; response = client.recognize(request=request)<br><br>&nbsp; &nbsp; transcript = \"\"<br>&nbsp; &nbsp; for result in response.results:<br>&nbsp; &nbsp; &nbsp; &nbsp; transcript += result.alternatives&#91;0].transcript<br><br>&nbsp; &nbsp; return transcript<br><br>if __name__ == '__main__':<br>&nbsp; &nbsp; app.run(debug=True, host=\"0.0.0.0\", port=int(os.environ.get(\"PORT\", 8080)))<br>&nbsp; &nbsp; #app.run(debug=True)<br><br>def generate(image_url,prompt):<br>&nbsp; &nbsp; vertexai.init(project=project_id, location=\"us-central1\")<br>&nbsp; &nbsp; model = GenerativeModel(\"gemini-1.5-pro-002\")<br>&nbsp; &nbsp; image1 = Part.from_uri(uri=image_url, mime_type=\"image\/jpeg\")<br><br>&nbsp; &nbsp; prompt_default = \"\"\"A retail store will give you an image with order details as an Input. You will identify the order details and provide an output as the following json format. You should not add any comment on it. The Box quantity should be arabic number. You can extract the item name from a given image or prompt. However, you should extract the retail store name or the quantity from only the text prompt but not the given image. All parameter values are strings. Don't assume any parameters. Do not wrap the json codes in JSON markers.<br><br>{\\\"function\\\":\\\"Z_SALES_ORDER_SRV\/orderlistSet\\\",\\\"parameters\\\":{\\\"vendor_name\\\":Retail store name,\\\"order_item\\\":Item name,\\\"order_boxes\\\":Box quantity}}<br><br>If you are not clear on any parameter, provide the output as follows.<br>{\\\"function\\\":\\\"None\\\"}<br><br>You should not use the json markdown for the result.<br><br>Input :\"\"\"<br><br>&nbsp; &nbsp; generation_config = {<br>&nbsp; &nbsp; &nbsp; &nbsp; \"max_output_tokens\": 8192,<br>&nbsp; &nbsp; &nbsp; &nbsp; \"temperature\": 0,<br>&nbsp; &nbsp; &nbsp; &nbsp; \"top_p\": 0.95,<br>&nbsp; &nbsp; }<br><br>&nbsp; &nbsp; safety_settings = &#91;<br>&nbsp; &nbsp; &nbsp; &nbsp; SafetySetting(<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; category=SafetySetting.HarmCategory.HARM_CATEGORY_HATE_SPEECH,<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; threshold=SafetySetting.HarmBlockThreshold.OFF<br>&nbsp; &nbsp; &nbsp; &nbsp; ),<br>&nbsp; &nbsp; &nbsp; &nbsp; SafetySetting(<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; category=SafetySetting.HarmCategory.HARM_CATEGORY_DANGEROUS_CONTENT,<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; threshold=SafetySetting.HarmBlockThreshold.OFF<br>&nbsp; &nbsp; &nbsp; &nbsp; ),<br>&nbsp; &nbsp; &nbsp; &nbsp; SafetySetting(<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; category=SafetySetting.HarmCategory.HARM_CATEGORY_SEXUALLY_EXPLICIT,<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; threshold=SafetySetting.HarmBlockThreshold.OFF<br>&nbsp; &nbsp; &nbsp; &nbsp; ),<br>&nbsp; &nbsp; &nbsp; &nbsp; SafetySetting(<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; category=SafetySetting.HarmCategory.HARM_CATEGORY_HARASSMENT,<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; threshold=SafetySetting.HarmBlockThreshold.OFF<br>&nbsp; &nbsp; &nbsp; &nbsp; ),<br>&nbsp; &nbsp; ]<br><br>&nbsp; &nbsp; responses = model.generate_content(<br>&nbsp; &nbsp; &nbsp; &nbsp; &#91;prompt_default, image1, prompt],<br>&nbsp; &nbsp; &nbsp; &nbsp; generation_config=generation_config,<br>&nbsp; &nbsp; &nbsp; &nbsp; safety_settings=safety_settings,<br>&nbsp; &nbsp; &nbsp; &nbsp; stream=True,<br>&nbsp; &nbsp; )<br><br>&nbsp; &nbsp; response = \"\"<br>&nbsp; &nbsp; for content in responses:<br>&nbsp; &nbsp; &nbsp; &nbsp;response += content.text<br>&nbsp; &nbsp; &nbsp; &nbsp;print(f\"Content: {content}\")<br>&nbsp; &nbsp; &nbsp; &nbsp;print(f\"Content type: {type(content)}\")<br>&nbsp; &nbsp; &nbsp; &nbsp;print(f\"Content attributes: {dir(content)}\")<br><br>&nbsp; &nbsp; print(f\"response_texts={response}\")<br><br>&nbsp; &nbsp; if response.startswith('json'):<br>&nbsp; &nbsp; &nbsp; &nbsp;return clean_json_string(response)<br>&nbsp; &nbsp; else:<br>&nbsp; &nbsp; &nbsp; &nbsp;return response<br><br>def clean_json_string(json_string):<br>&nbsp; &nbsp; pattern = r'^```json\\s*(.*?)\\s*```$'<br>&nbsp; &nbsp; cleaned_string = re.sub(pattern, r'\\1', json_string, flags=re.DOTALL)<br>&nbsp; &nbsp; return cleaned_string.strip()<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"create-index.html\">\u521b\u5efa index.html<\/h3>\n\n\n\n<pre class=\"wp-block-preformatted\">vi ~\/genai-agent\/templates\/index.html\n<\/pre>\n\n\n\n<p>\u5728 index.html \u6587\u4ef6\u4e2d\u8f93\u5165 HTML \u4ee3\u7801\u3002<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>&lt;!DOCTYPE html&gt;<br>&lt;html&gt;<br>&lt;head&gt;<br>&nbsp; &nbsp; &lt;title&gt;GenAI Agent for Retail&lt;\/title&gt;<br>&nbsp; &nbsp; &lt;meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\"&gt;<br>&nbsp; &nbsp; &lt;style&gt;<br>&nbsp; &nbsp; &nbsp; &nbsp; \/* Styles adjusted for chatbot interface *\/<br>&nbsp; &nbsp; &nbsp; &nbsp; body {<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; font-family: Arial, sans-serif;<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; background-color: #343541;<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; margin: 0;<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; padding: 0;<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; display: flex;<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; flex-direction: column;<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; height: 100vh;<br>&nbsp; &nbsp; &nbsp; &nbsp; }<br><br>&nbsp; &nbsp; &nbsp; &nbsp; .chat-container {<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; flex: 1;<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; overflow-y: auto;<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; padding: 10px;<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; background-color: #343541;<br>&nbsp; &nbsp; &nbsp; &nbsp; }<br><br>&nbsp; &nbsp; &nbsp; &nbsp; .message {<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; max-width: 80%;<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; margin-bottom: 15px;<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; padding: 10px;<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; border-radius: 10px;<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; color: #dcdcdc;<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; word-wrap: break-word;<br>&nbsp; &nbsp; &nbsp; &nbsp; }<br><br>&nbsp; &nbsp; &nbsp; &nbsp; .user-message {<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; background-color: #3e3f4b;<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; align-self: flex-end;<br>&nbsp; &nbsp; &nbsp; &nbsp; }<br><br>&nbsp; &nbsp; &nbsp; &nbsp; .assistant-message {<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; background-color: #444654;<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; align-self: flex-start;<br>&nbsp; &nbsp; &nbsp; &nbsp; }<br><br>&nbsp; &nbsp; &nbsp; &nbsp; .message-input {<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; padding: 10px;<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; background-color: #40414f;<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; display: flex;<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; align-items: center;<br>&nbsp; &nbsp; &nbsp; &nbsp; }<br><br>&nbsp; &nbsp; &nbsp; &nbsp; .message-input textarea {<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; flex: 1;<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; padding: 10px;<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; border: none;<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; border-radius: 5px;<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; resize: none;<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; background-color: #40414f;<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; color: #dcdcdc;<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; height: 40px;<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; max-height: 100px;<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; overflow-y: auto;<br>&nbsp; &nbsp; &nbsp; &nbsp; }<br><br>&nbsp; &nbsp; &nbsp; &nbsp; .message-input button {<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; padding: 15px;<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; margin-left: 5px;<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; background-color: #19c37d;<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; border: none;<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; border-radius: 5px;<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; color: white;<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; font-weight: bold;<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; cursor: pointer;<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; flex-shrink: 0;<br>&nbsp; &nbsp; &nbsp; &nbsp; }<br><br>&nbsp; &nbsp; &nbsp; &nbsp; .image-preview {<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; max-width: 100%;<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; border-radius: 10px;<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; margin-bottom: 10px;<br>&nbsp; &nbsp; &nbsp; &nbsp; }<br><br>&nbsp; &nbsp; &nbsp; &nbsp; .hidden {<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; display: none;<br>&nbsp; &nbsp; &nbsp; &nbsp; }<br><br>&nbsp; &nbsp; &nbsp; &nbsp; \/* Media queries for responsive design *\/<br>&nbsp; &nbsp; &nbsp; &nbsp; @media screen and (max-width: 600px) {<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; .message {<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; max-width: 100%;<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }<br><br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; .message-input {<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; flex-direction: column;<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }<br><br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; .message-input textarea {<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; width: 100%;<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; margin-bottom: 10px;<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }<br><br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; .message-input button {<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; width: 100%;<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; margin: 5px 0;<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }<br>&nbsp; &nbsp; &nbsp; &nbsp; }<br>&nbsp; &nbsp; &lt;\/style&gt;<br>&lt;\/head&gt;<br>&lt;body&gt;<br>&nbsp; &nbsp; &lt;div class=\"chat-container\" id=\"chat-container\"&gt;<br>&nbsp; &nbsp; &nbsp; &nbsp; &lt;!-- Messages will be appended here --&gt;<br>&nbsp; &nbsp; &lt;\/div&gt;<br><br>&nbsp; &nbsp; &lt;div class=\"message-input\"&gt;<br>&nbsp; &nbsp; &nbsp; &nbsp; &lt;input type=\"file\" name=\"photo\" id=\"photo\" accept=\"image\/*\" capture=\"camera\" class=\"hidden\"&gt;<br>&nbsp; &nbsp; &nbsp; &nbsp; &lt;button id=\"uploadImageButton\"&gt;\ud83d\udcf7&lt;\/button&gt;<br>&nbsp; &nbsp; &nbsp; &nbsp; &lt;button id=\"recordButton\"&gt;\ud83c\udfa4&lt;\/button&gt;<br>&nbsp; &nbsp; &nbsp; &nbsp; &lt;textarea id=\"transcript\" rows=\"1\" placeholder=\"Enter a message here by voice or typing...\"&gt;&lt;\/textarea&gt;<br>&nbsp; &nbsp; &nbsp; &nbsp; &lt;button id=\"sendButton\"&gt;Send&lt;\/button&gt;<br>&nbsp; &nbsp; &lt;\/div&gt;<br><br>&nbsp; &nbsp; &lt;script&gt;<br>&nbsp; &nbsp; &nbsp; &nbsp; const chatContainer = document.getElementById('chat-container');<br>&nbsp; &nbsp; &nbsp; &nbsp; const transcriptInput = document.getElementById('transcript');<br>&nbsp; &nbsp; &nbsp; &nbsp; const sendButton = document.getElementById('sendButton');<br>&nbsp; &nbsp; &nbsp; &nbsp; const recordButton = document.getElementById('recordButton');<br>&nbsp; &nbsp; &nbsp; &nbsp; const uploadImageButton = document.getElementById('uploadImageButton');<br>&nbsp; &nbsp; &nbsp; &nbsp; const photoInput = document.getElementById('photo');<br><br>&nbsp; &nbsp; &nbsp; &nbsp; let mediaRecorder;<br>&nbsp; &nbsp; &nbsp; &nbsp; let audioChunks = &#91;];<br>&nbsp; &nbsp; &nbsp; &nbsp; let imageUrl = '';<br><br>&nbsp; &nbsp; &nbsp; &nbsp; function appendMessage(content, sender) {<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; const messageDiv = document.createElement('div');<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; messageDiv.classList.add('message', sender === 'user' ? 'user-message' : 'assistant-message');<br><br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if (typeof content === 'string') {<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; const messageContent = document.createElement('p');<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; messageContent.innerText = content;<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; messageDiv.appendChild(messageContent);<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; } else {<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; messageDiv.appendChild(content);<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }<br><br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; chatContainer.appendChild(messageDiv);<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; chatContainer.scrollTop = chatContainer.scrollHeight;<br>&nbsp; &nbsp; &nbsp; &nbsp; }<br><br>&nbsp; &nbsp; &nbsp; &nbsp; sendButton.addEventListener('click', () =&gt; {<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; const message = transcriptInput.value.trim();<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if (message !== '') {<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; appendMessage(message, 'user');<br><br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; \/\/ Prepare form data<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; const formData = new FormData();<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; formData.append('transcript', message);<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; formData.append('image_url', imageUrl);<br><br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; \/\/ Send the message to the server<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; fetch('\/orders', {<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; method: 'POST',<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; body: formData<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; })<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; .then(response =&gt; response.text())<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; .then(data =&gt; {<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; appendMessage(data, 'assistant');<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; \/\/ Reset imageUrl after sending<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; imageUrl = '';<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; })<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; .catch(error =&gt; {<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; console.error('Error:', error);<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; });<br><br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; transcriptInput.value = '';<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }<br>&nbsp; &nbsp; &nbsp; &nbsp; });<br><br>&nbsp; &nbsp; &nbsp; &nbsp; transcriptInput.addEventListener('keypress', (e) =&gt; {<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if (e.key === 'Enter' &amp;&amp; !e.shiftKey) {<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; e.preventDefault();<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; sendButton.click();<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }<br>&nbsp; &nbsp; &nbsp; &nbsp; });<br><br>&nbsp; &nbsp; &nbsp; &nbsp; recordButton.addEventListener('click', async () =&gt; {<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if (mediaRecorder &amp;&amp; mediaRecorder.state === 'recording') {<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; mediaRecorder.stop();<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; recordButton.innerText = '\ud83c\udfa4';<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return;<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }<br><br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; let stream = await navigator.mediaDevices.getUserMedia({ audio: true });<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; mediaRecorder = new MediaRecorder(stream);<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; mediaRecorder.start();<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; recordButton.innerText = '\u23f9\ufe0f';<br><br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; mediaRecorder.ondataavailable = event =&gt; {<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; audioChunks.push(event.data);<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; };<br><br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; mediaRecorder.onstop = async () =&gt; {<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; let audioBlob = new Blob(audioChunks, { type: 'audio\/wav' });<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; audioChunks = &#91;];<br><br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; let reader = new FileReader();<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; reader.readAsDataURL(audioBlob);<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; reader.onloadend = () =&gt; {<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; let base64String = reader.result;<br><br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; \/\/ Send the audio data to the server<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; fetch('\/upload', {<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; method: 'POST',<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; headers: {<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 'Content-Type': 'application\/x-www-form-urlencoded'<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; },<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; body: 'audio_data=' + encodeURIComponent(base64String)<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; })<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; .then(response =&gt; response.json())<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; .then(data =&gt; {<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; transcriptInput.value = data.transcript;<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; })<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; .catch(error =&gt; {<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; console.error('Error:', error);<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; });<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; };<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; };<br>&nbsp; &nbsp; &nbsp; &nbsp; });<br><br>&nbsp; &nbsp; &nbsp; &nbsp; uploadImageButton.addEventListener('click', () =&gt; {<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; photoInput.click();<br>&nbsp; &nbsp; &nbsp; &nbsp; });<br><br>&nbsp; &nbsp; &nbsp; &nbsp; photoInput.addEventListener('change', function() {<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if (photoInput.files &amp;&amp; photoInput.files&#91;0]) {<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; const file = photoInput.files&#91;0];<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; const reader = new FileReader();<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; reader.onload = function(e) {<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; const img = document.createElement('img');<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; img.src = e.target.result;<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; img.classList.add('image-preview');<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; appendMessage(img, 'user');<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; };<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; reader.readAsDataURL(file);<br><br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; const formData = new FormData();<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; formData.append('photo', photoInput.files&#91;0]);<br><br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; \/\/ Upload the image to the server<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; fetch('\/upload_photo', {<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; method: 'POST',<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; body: formData,<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; })<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; .then(response =&gt; response.text())<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; .then(url =&gt; {<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; imageUrl = url;<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; })<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; .catch(error =&gt; {<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; console.error('Error uploading photo:', error);<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; });<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }<br>&nbsp; &nbsp; &nbsp; &nbsp; });<br>&nbsp; &nbsp; &lt;\/script&gt;<br>&lt;\/body&gt;<br>&lt;\/html&gt;<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"create-a-orderlist.html\">\u521b\u5efa orderlist.html<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>vi ~\/genai-agent\/templates\/orderlist.html<\/code><\/pre>\n\n\n\n<p>\u5c06 HTML \u4ee3\u7801\u8f93\u5165\u5230 orderlist.html \u6587\u4ef6\u4e2d\u3002<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>&lt;!DOCTYPE html&gt;<br>&lt;html&gt;<br>&lt;head&gt;<br>&nbsp; &nbsp; &lt;title&gt;Order List&lt;\/title&gt;<br>&nbsp; &nbsp; &lt;style&gt;<br>&nbsp; &nbsp; &nbsp; &nbsp; body {<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; font-family: sans-serif;<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; line-height: 1.6;<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; margin: 20px;<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; background-color: #f4f4f4;<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; color: #333;<br>&nbsp; &nbsp; &nbsp; &nbsp; }<br><br>&nbsp; &nbsp; &nbsp; &nbsp; h1 {<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; text-align: center;<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; color: #28a745; \/* Green header *\/<br>&nbsp; &nbsp; &nbsp; &nbsp; }<br><br>&nbsp; &nbsp; &nbsp; &nbsp; table {<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; width: 100%;<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; border-collapse: collapse;<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; margin-top: 20px;<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; box-shadow: 0 0 10px rgba(0, 0, 0, 0.1); \/* Add a subtle shadow *\/<br>&nbsp; &nbsp; &nbsp; &nbsp; }<br><br>&nbsp; &nbsp; &nbsp; &nbsp; th, td {<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; padding: 12px 15px;<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; text-align: left;<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; border-bottom: 1px solid #ddd;<br>&nbsp; &nbsp; &nbsp; &nbsp; }<br><br>&nbsp; &nbsp; &nbsp; &nbsp; th {<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; background-color: #28a745; \/* Green header background *\/<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; color: white;<br>&nbsp; &nbsp; &nbsp; &nbsp; }<br><br>&nbsp; &nbsp; &nbsp; &nbsp; tr:nth-child(even) {<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; background-color: #f8f9fa; \/* Alternating row color *\/<br>&nbsp; &nbsp; &nbsp; &nbsp; }<br><br>&nbsp; &nbsp; &nbsp; &nbsp; tr:hover {<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; background-color: #e9ecef; \/* Hover effect *\/<br>&nbsp; &nbsp; &nbsp; &nbsp; }<br><br>&nbsp; &nbsp; &lt;\/style&gt;<br>&lt;\/head&gt;<br>&lt;body&gt;<br>&nbsp; &nbsp; &lt;h1&gt;Order List&lt;\/h1&gt;<br>&nbsp; &nbsp; &lt;table&gt;<br>&nbsp; &nbsp; &nbsp; &nbsp; &lt;thead&gt;<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;tr&gt;<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;th&gt;Order ID&lt;\/th&gt;<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;th&gt;Retail Store Name&lt;\/th&gt;<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;th&gt;Order Item&lt;\/th&gt;<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;th&gt;Order Boxes&lt;\/th&gt;<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;th&gt;Order Date&lt;\/th&gt;<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;th&gt;Order Time&lt;\/th&gt;<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;\/tr&gt;<br>&nbsp; &nbsp; &nbsp; &nbsp; &lt;\/thead&gt;<br>&nbsp; &nbsp; &nbsp; &nbsp; &lt;tbody&gt;<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; {% for order in orders %}<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;tr&gt;<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;td&gt;{{ order.OrderId }}&lt;\/td&gt;<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;td&gt;{{ order.VendorName }}&lt;\/td&gt;<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;td&gt;{{ order.OrderItem }}&lt;\/td&gt;<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;td&gt;{{ order.OrderBoxes }}&lt;\/td&gt;<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;td&gt;{{ order.OrderDate }}&lt;\/td&gt;<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;td&gt;{{ order.OrderTime }}&lt;\/td&gt;<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;\/tr&gt;<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; {% endfor %}<br>&nbsp; &nbsp; &nbsp; &nbsp; &lt;\/tbody&gt;<br>&nbsp; &nbsp; &lt;\/table&gt;<br>&lt;\/body&gt;<br>&lt;\/html&gt;<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">6. \u5c06 Flask \u5e94\u7528\u90e8\u7f72\u5230 Cloud Run<\/h2>\n\n\n\n<p>\u5728 genai-agent \u76ee\u5f55\u4e2d\uff0c\u4f7f\u7528\u4ee5\u4e0b\u547d\u4ee4\u5c06\u5e94\u7528\u90e8\u7f72\u5230 Cloud Run\uff1a<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">cd ~\/genai-agent\n<\/pre>\n\n\n\n<pre class=\"wp-block-preformatted\">gcloud run deploy --source . genai-agent-sales-order \\\n--set-env-vars=PROJECT_ID=$PROJECT_ID \\\n--set-env-vars=REGION=$REGION \\\n--set-env-vars=INSTANCE_CONNECTION_NAME=\"${PROJECT_ID}:${REGION}:sql-retail-genai\" \\\n--set-env-vars=DB_USER=aiagent \\\n--set-env-vars=DB_PASS=genaiaigent2@ \\\n--set-env-vars=DB_NAME=retail-orders \\\n--set-env-vars=GENAI_BUCKET=$GENAI_BUCKET \\\n--network=$PROJECT_ID \\\n--subnet=$SUBNET_NAME \\\n--vpc-egress=private-ranges-only \\\n--region=$REGION \\\n--allow-unauthenticated\n<\/pre>\n\n\n\n<p>\u9884\u671f\u8f93\u51fa\uff1a<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">Deploying from source requires an Artifact Registry Docker repository to store built containers. A repository named [cloud-run-source-deploy] in region [us-central1] will be created.\n\nDo you want to continue (Y\/n)?  Y\n<\/pre>\n\n\n\n<p>\u8fd9\u9700\u8981\u51e0\u5206\u949f\u65f6\u95f4\uff0c\u5982\u679c\u6210\u529f\u5b8c\u6210\uff0c\u60a8\u5c06\u770b\u5230\u670d\u52a1\u7f51\u5740\uff0c<\/p>\n\n\n\n<p>\u9884\u671f\u8f93\u51fa\uff1a<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">..........\nBuilding using Buildpacks and deploying container to Cloud Run service [genai-agent-sales-order] in project [xxxx] region [us-central1]\n\u2713 Building and deploying... Done.                                                                                                                                                                                                                                                                                                                               \n  \u2713 Uploading sources...                                                                                                                                                                                                                                                                                                                                        \n  \u2713 Building Container... Logs are available at [https:\/\/console.cloud.google.com\/cloud-build\/builds\/395d141c-2dcf-465d-acfb-f97831c448c3?project=xxxx].                                                                                                                                                                                                \n  \u2713 Creating Revision...                                                                                                                                                                                                                                                                                                                                        \n  \u2713 Routing traffic...                                                                                                                                                                                                                                                                                                                                          \n  \u2713 Setting IAM Policy...                                                                                                                                                                                                                                                                                                                                       \nDone.                                                                                                                                                                                                                                                                                                                                                           \nService [genai-agent-sales-order] revision [genai-agent-sales-order-00013-ckp] has been deployed and is serving 100 percent of traffic.\nService URL: https:\/\/genai-agent-sales-order-xxxx.us-central1.run.app\n<\/pre>\n\n\n\n<p>\u60a8\u8fd8\u53ef\u4ee5\u5728 Cloud Run \u63a7\u5236\u53f0\u4e2d\u67e5\u770b\u670d\u52a1\u7f51\u5740\u3002<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">7. \u6d4b\u8bd5<\/h2>\n\n\n\n<ol class=\"wp-block-list\">\n<li>\u5728\u624b\u673a\u6216\u7b14\u8bb0\u672c\u7535\u8111\u4e2d\u8f93\u5165 Cloud Run \u90e8\u7f72\u7684\u4e0a\u4e00\u6b65\u4e2d\u751f\u6210\u7684\u670d\u52a1\u7f51\u5740\u3002<\/li>\n\n\n\n<li>\u4e3a\u8ba2\u5355\u4e2d\u7684\u5546\u54c1\u62cd\u7167\uff0c\u7136\u540e\u901a\u8fc7\u8f93\u5165\u6216\u8bed\u97f3\u8f93\u5165\u8ba2\u5355\u6570\u91cf\uff08\u7bb1\uff09\u548c\u96f6\u552e\u5546\u5e97\u540d\u79f0\u3002&lt;ex> <strong><em>\u201c\u6211\u60f3\u8ba2\u8d2d\u8fd9\u4e09\u76d2\u5546\u54c1\u3002\u54e6\uff0c\u4e0d\uff0c\u62b1\u6b49\uff0c\u662f 7 \u4e2a\u76d2\u5b50\u3002\u8fd9\u662f\u6c83\u5c14\u739b\u5c71\u666f\u57ce\u5e97\u201d<\/em><\/strong><\/li>\n\n\n\n<li>\u70b9\u51fb\u201c\u53d1\u9001\u201d\uff0c\u7136\u540e\u67e5\u770b\u8ba2\u5355\u662f\u5426\u5df2\u5b8c\u6210\u3002<\/li>\n\n\n\n<li>\u60a8\u53ef\u4ee5\u5728 {Service URL}\/orderlist \u67e5\u770b\u8ba2\u5355\u8bb0\u5f55<\/li>\n<\/ol>\n\n\n\n<figure class=\"wp-block-image\"><img decoding=\"async\" src=\"https:\/\/codelabs.developers.google.com\/static\/codelabs\/GenAI-Agent-Retail\/img\/de0db1a08082c634.png?hl=zh-cn\" alt=\"de0db1a08082c634.png\"\/><\/figure>","protected":false},"excerpt":{"rendered":"<p>1. \u6982\u89c8 \u6784\u5efa\u5185\u5bb9 \u60a8\u7684\u5e94\u7528\u5c06\uff1a 2. \u5b66\u4e60\u5185\u5bb9 \u5b66\u4e60\u5185\u5bb9 \u6240\u9700\u6761\u4ef6 \u67b6\u6784 \u6b64\u4ee3\u7406\u53ef\u8ba9\u60a8\u901a\u8fc7\u56fe\u7247\u548c\u6587\u672c\u63d0\u793a\uff0c\u5229\u7528 Gemini \u7684\u591a\u6a21\u6001\u529f\u80fd\u7b80\u5316\u8ba2\u8d2d\u6d41\u7a0b\u3002\u5982\u679c\u8ba2\u5355\u662f\u901a\u8fc7\u8bed\u97f3\u4e0b\u8fbe\u7684\uff0cGoogle Speech \u7684 Chirp 2 \u6a21\u578b\u4f1a\u5c06\u5176\u8f6c\u5199\u4e3a\u6587\u672c\uff0c\u7136\u540e\u5c06\u8be5\u6587\u672c\u4e0e\u63d0\u4f9b\u7684\u56fe\u7247\u4e00\u8d77\u7528\u4e8e\u67e5\u8be2 Vertex AI \u7684 Gemini \u6a21\u578b\u3002 \u6211\u4eec\u5c06\u6784\u5efa\uff1a 3. \u51c6\u5907\u5de5\u4f5c \u542f\u7528 API \u542f\u7528\u5b9e\u9a8c\u6240\u9700\u7684 AP&hellip;<\/p>","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[5,9],"tags":[],"class_list":["post-377","post","type-post","status-publish","format-standard","hentry","category-ai","category-gcp"],"_links":{"self":[{"href":"https:\/\/flycloud.io\/en\/wp-json\/wp\/v2\/posts\/377","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/flycloud.io\/en\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/flycloud.io\/en\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/flycloud.io\/en\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/flycloud.io\/en\/wp-json\/wp\/v2\/comments?post=377"}],"version-history":[{"count":1,"href":"https:\/\/flycloud.io\/en\/wp-json\/wp\/v2\/posts\/377\/revisions"}],"predecessor-version":[{"id":378,"href":"https:\/\/flycloud.io\/en\/wp-json\/wp\/v2\/posts\/377\/revisions\/378"}],"wp:attachment":[{"href":"https:\/\/flycloud.io\/en\/wp-json\/wp\/v2\/media?parent=377"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/flycloud.io\/en\/wp-json\/wp\/v2\/categories?post=377"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/flycloud.io\/en\/wp-json\/wp\/v2\/tags?post=377"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}