1. מטרות
מטרת הסדנה הזו היא לספק למשתמשים ולאנשי מקצוע הדרכה מעשית בנושא Duet AI.
ב-codelab הזה תלמדו:
- מפעילים את Duet AI בפרויקט GCP ומגדירים אותו לשימוש בסביבת פיתוח משולבת (IDE) ובמסוף Cloud.
- שימוש ב-Duet AI ליצירה, להשלמה ולהסבר של קוד.
- להשתמש ב-Duet AI כדי להסביר ולפתור בעיה באפליקציה.
- תכונות של Duet AI כמו צ'אט IDE ושיחה עם זיכרון, צ'אט לעומת יצירת קוד בשורה, פעולות חכמות כמו הסבר על קוד ואישור של הקראת קוד ועוד.
הסיפור
כדי להראות איך משתמשים ב-Duet AI למפתחים באופן אותנטי בפיתוח יומיומי, הפעילויות בסדנה הזו מתרחשות בהקשר נרטיבי.
מפתח חדש מצטרף לחברת מסחר אלקטרוני. הם קיבלו משימה להוסיף שירות חדש לאפליקציית המסחר האלקטרוני הקיימת (שמורכבת מכמה שירותים). השירות החדש מספק מידע נוסף (מידות, משקל וכו') על מוצרים בקטלוג המוצרים. השירות הזה יאפשר לכם להציע עלויות משלוח טובות יותר או זולות יותר על סמך המידות והמשקלים של המוצרים.
המפתח חדש בחברה, ולכן הוא ישתמש ב-Duet AI כדי ליצור קוד, לקבל הסברים וליצור תיעוד.
אחרי שהשירות מקודד, אדמין פלטפורמה משתמש ב-Duet AI (צ'אט) כדי ליצור את הארטיפקט (מאגר Docker) ואת המשאבים שנדרשים לפריסת הארטיפקט ב-GCP (לדוגמה, Artifact Registry, הרשאות IAM, מאגר קוד, תשתית מחשוב, כלומר GKE או CloudRun וכו').
אחרי שהאפליקציה נפרסת ב-GCP, מפעיל אפליקציה או SRE ישתמש ב-Duet AI (וב-Cloud Ops) כדי לפתור בעיה בשירות החדש.
פרסונה
הסדנה מתמקדת בדמות הבאה:
- מפתח אפליקציות – נדרש ידע מסוים בתכנות ובפיתוח תוכנה.
הסדנה הזו בנושא Duet AI מיועדת למפתחים בלבד. לא נדרש ידע במשאבי ענן של GCP. כאן אפשר למצוא את הסקריפטים ליצירת משאבי ה-GCP הנדרשים להפעלת האפליקציה הזו. כדי לפרוס את משאבי ה-GCP הנדרשים, אפשר לפעול לפי ההוראות במדריך הזה.
2. הכנת הסביבה
הפעלת Duet AI
אפשר להפעיל את Duet AI בפרויקט GCP באמצעות API (gcloud או כלים של IaC כמו Terraform) או באמצעות ממשק המשתמש של Cloud Console.
כדי להפעיל את Duet AI בפרויקט בענן ב-Google Cloud, צריך להפעיל את Cloud AI Companion API ולהקצות למשתמשים את תפקידי ה-IAM Cloud AI Companion User ו-Service Usage Viewer.
באמצעות gcloud
מפעילים את Cloud Shell:
מגדירים את PROJECT_ID ואת USER ומפעילים את Cloud AI Companion API.
export PROJECT_ID=<YOUR PROJECT ID>
export USER=<YOUR USERNAME> # Use your full LDAP, e.g. name@example.com
gcloud config set project ${PROJECT_ID}
gcloud services enable cloudaicompanion.googleapis.com --project ${PROJECT_ID}
הפלט אמור להיראות כך:
Updated property [core/project]. Operation "operations/acat.p2-60565640195-f37dc7fe-b093-4451-9b12-934649e2a435" finished successfully.
מקצים לחשבון המשתמש את התפקידים 'משתמש ב-Cloud AI Companion' ו'צפייה בשימוש בשירות' בניהול הזהויות והרשאות הגישה (IAM). ה-API של Cloud Companion פועל ברקע של התכונות שבהן נשתמש ב-IDE ובמסוף. ההרשאה 'צפייה בשימוש בשירות' משמשת לבדיקה מהירה לפני הפעלת ממשק המשתמש במסוף (כדי שממשק המשתמש של Duet יופיע רק בפרויקטים שבהם מופעל ה-API).
gcloud projects add-iam-policy-binding ${PROJECT_ID} \
--member=user:${USER} --role=roles/cloudaicompanion.user
gcloud projects add-iam-policy-binding ${PROJECT_ID} \
--member=user:${USER} --role=roles/serviceusage.serviceUsageViewer
הפלט אמור להיראות כך:
... - members: - user:<YOUR USER ACCOUNT> role: roles/cloudaicompanion.user ... - members: - user:<YOUR USER ACCOUNT> role: roles/serviceusage.serviceUsageViewer
דרך Cloud Console
כדי להפעיל את ה-API, נכנסים לדף Cloud AI Companion API במסוף Google Cloud.
בוחרים את הפרויקט מרשימת הפרויקטים.
לוחצים על הפעלה.
הדף יתעדכן ויופיע הסטטוס Enabled. Duet AI זמין עכשיו בפרויקט Google Cloud שנבחר לכל המשתמשים שיש להם את תפקידי ה-IAM הנדרשים.
נכנסים לדף IAM כדי להקצות את התפקידים ב-IAM שנדרשים בשביל Duet AI.
בעמודה Principal, מחפשים את המשתמש שרוצים לתת לו גישה ל-Duet AI, ולוחצים על סמל העיפרון ✏️ Edit principal בשורה הזו.
בחלונית Edit access, לוחצים על Add another role.
ברשימת התפקידים Select a role בוחרים את Cloud AI Companion User.
לוחצים על Add another role ובוחרים את התפקיד Service Usage Viewer.
לוחצים על שמירה.
הגדרת סביבת הפיתוח המשולבת
מפתחים יכולים לבחור מתוך מגוון סביבות פיתוח משולבות (IDE) את זו שהכי מתאימה לצרכים שלהם. התכונה Duet AI Code Assistance זמינה בסביבות פיתוח משולבות (IDE) רבות, כמו Visual Studio Code, סביבות פיתוח משולבות של JetBrains (IntelliJ, PyCharm, GoLand, WebStorm ועוד), Cloud Workstations ו-Cloud Shell Editor.
בשיעור ה-Lab הזה אפשר להשתמש ב-Cloud Workstations או בכלי Cloud Shell Editor.
בסדנה הזו משתמשים ב-Cloud Shell Editor.
שימו לב: יכול להיות שיעברו 20-30 דקות עד שתהליך ההגדרה של Cloud Workstations יסתיים.
כדי להשתמש בו באופן מיידי, אפשר להשתמש ב-Cloud Shell Editor.
לוחצים על סמל העיפרון ✏️ בסרגל התפריטים העליון של Cloud Shell כדי לפתוח את Cloud Shell Editor.
ממשק המשתמש וחוויית המשתמש של Cloud Shell Editor דומים מאוד לאלה של VSCode.

לוחצים על CTRL (ב-Windows) או על CMD (ב-Mac) + , (פסיק) כדי להיכנס לחלונית ההגדרות.
בסרגל החיפוש, מקלידים 'duet ai'.
מוודאים שהאפשרויות Cloudcode › Duet AI: Enable ו-Cloudcode › Duet AI › Inline Suggestions: Enable Auto מופעלות.

בסרגל המצב התחתון, לוחצים על Cloud Code - Sign In ופועלים לפי תהליך הכניסה.
אם כבר נכנסתם לחשבון, בשורת המצב יופיע הכיתוב Cloud Code – No project (Cloud Code – אין פרויקט).
לוחצים על Cloud Code – No project (Cloud Code – אין פרויקט), ולוחצים על החלונית הנפתחת של הפעולה שמופיעה בחלק העליון. לוחצים על Select a Google Cloud project (בחירת פרויקט בענן של Google).

מתחילים להקליד את מזהה הפרויקט והפרויקט אמור להופיע ברשימה.

בוחרים את PROJECT_ID מרשימת הפרויקטים.
שורת הסטטוס התחתונה מתעדכנת ומציגה את מזהה הפרויקט. אם הוא לא מופיע, יכול להיות שתצטרכו לרענן את הכרטיסייה של Cloud Shell Editor.
לוחצים על סמל Duet AI
בסרגל התפריטים שמשמאל, וחלון הצ'אט של Duet AI יופיע. אם מופיעה ההודעה 'בחירת פרויקט ב-GCP'. לוחצים על הפרויקט ומסמנים אותו מחדש.
יוצג חלון הצ'אט של Duet AI

3. הגדרת התשתית

כדי להפעיל את שירות המשלוחים החדש ב-GCP, אתם צריכים את משאבי GCP הבאים:
- מכונה של Cloud SQL, עם מסד נתונים.
- אשכול GKE להרצת השירות בקונטיינר.
- Artifact Registry לאחסון קובץ האימג' של Docker.
- מאגר Cloud Source Repository לקוד.
בטרמינל של Cloud Shell, משכפלים את מאגר הנתונים הבא ומריצים את הפקודות הבאות כדי להגדיר את התשתית בפרויקט GCP.
# Set your project
export PROJECT_ID=<INSERT_YOUR_PROJECT_ID>
gcloud config set core/project ${PROJECT_ID}
# Enable Cloudbuild and grant Cloudbuild SA owner role
export PROJECT_NUMBER=$(gcloud projects describe ${PROJECT_ID} --format 'value(projectNumber)')
gcloud services enable cloudbuild.googleapis.com
gcloud projects add-iam-policy-binding ${PROJECT_ID} --member serviceAccount:${PROJECT_NUMBER}@cloudbuild.gserviceaccount.com --role roles/owner
# Clone the repo
git clone https://github.com/duetailabs/dev.git ~/duetaidev
cd ~/duetaidev
# Run Cloudbuild to create the necessary resources
gcloud builds submit --substitutions=_PROJECT_ID=${PROJECT_ID}
# To destroy all GCP resources, run the following
# gcloud builds submit --substitutions=_PROJECT_ID=${PROJECT_ID} --config=cloudbuild_destroy.yaml
4. פיתוח שירות Python Flask

השירות שניצור יכלול בסופו של דבר את הקבצים הבאים. לא צריך ליצור את הקבצים האלה עכשיו, אלא ליצור אותם אחד אחד לפי ההוראות שבהמשך:
-
package-service.yaml– מפרט Open API לשירות החבילות, שכולל נתונים כמו גובה, רוחב, משקל והוראות מיוחדות לטיפול. -
data_model.py– מודל נתונים למפרט של package-service API. יוצר גם את הטבלהpackagesבמסד הנתונים product_details. -
connect_connector.py– חיבור ל-CloudSQL (הגדרת המנוע, Session ו-Base ORM) -
db_init.py– יוצר נתוני מדגם בטבלתpackages. -
main.py– שירות Python Flask עם נקודת קצהGETלאחזור פרטי חבילה מנתוניpackagesעל סמך product_id. -
test.py– בדיקת יחידה -
requirement.txt– דרישות Python -
Dockerfile– כדי להוסיף את האפליקציה הזו לקונטיינר
אם נתקלתם בבעיות קשות במהלך התרגילים, כל הקבצים הסופיים נמצאים בנספח של ה-codelab הזה לעיון.
בשלב הקודם יצרתם מאגר קוד בענן. משכפלים את המאגר. תבנו את קובצי האפליקציה בתיקיית המאגר המשוכפל.
במסוף Cloud Shell, מריצים את הפקודה הבאה כדי לשכפל את המאגר.
cd ~ gcloud source repos clone shipping shipping cd ~/shipping
פותחים את סרגל הצד של הצ'אט עם Duet AI מהתפריט הימני של Cloud Shell Editor. הסמל נראה כך:
. עכשיו אפשר להשתמש ב-Duet AI כדי לקבל עזרה בכתיבת קוד.
package-service.yaml
כשלא פתוחים קבצים, מבקשים מ-Duet ליצור מפרט Open API לשירות המשלוחים.
הנחיה 1: צור מפרט OpenAPI yaml לשירות שמספק מידע על משלוחים וחבילות בהינתן מזהה מוצר מספרי. השירות צריך לכלול מידע על הגובה, הרוחב, העומק והמשקל של החבילות, וגם הוראות מיוחדות לטיפול בהן.

בפינה השמאלית העליונה של חלון הקוד שנוצר מופיעות שלוש אפשרויות.
אפשר COPY
את הקוד ולהדביק אותו בקובץ.
אפשר ADD
את הקוד לקובץ שפתוח כרגע בכלי העריכה.
אפשר גם OPEN
את הקוד בקובץ חדש.
לוחצים על OPEN
הקוד בקובץ חדש.
לוחצים על CTRL/CMD + s כדי לשמור את הקובץ, ומאחסנים אותו בתיקיית האפליקציה עם שם הקובץ package-service.yaml. לחץ על 'אישור'.

הקובץ הסופי נמצא בקטע 'נספח' של ה-codelab הזה. אם לא, מבצעים את השינויים המתאימים באופן ידני.
אפשר גם לנסות הנחיות שונות כדי לראות את התשובות של Duet AI.
כדי לאפס את היסטוריית הצ'אט עם Duet AI, לוחצים על סמל האשפה
בחלק העליון של סרגל הצד של Duet AI.
data_model.py
לאחר מכן, יוצרים את קובץ ה-Python של מודל הנתונים לשירות על סמך מפרט OpenAPI.
פותחים את קובץ package-service.yaml ומזינים את ההנחיה הבאה.
הנחיה 1: באמצעות python sqlalchemy ORM, צור מודל נתונים לשירות ה-API הזה. צריך לכלול גם פונקציה נפרדת ונקודת כניסה ראשית שיוצרת את טבלאות מסד הנתונים.

בואו נסתכל על כל חלק שנוצר. Duet AI הוא עדיין עוזר, ולמרות שהוא יכול לעזור לכתוב קוד במהירות, עדיין כדאי לבדוק את התוכן שנוצר ולהבין אותו תוך כדי.
קודם כל, יש Class בשם Package מסוג kind Base שמגדיר את מודל הנתונים עבור מסד הנתונים packages, כמו בדוגמה הבאה:
class Package(Base):
__tablename__ = 'packages'
id = Column(Integer, primary_key=True)
product_id = Column(String(255))
height = Column(Float)
width = Column(Float)
depth = Column(Float)
weight = Column(Float)
special_handling_instructions = Column(String(255))
לאחר מכן, צריך פונקציה שיוצרת את הטבלה במסד הנתונים, כמו הפונקציה הבאה:
def create_tables(engine):
Base.metadata.create_all(engine)
לבסוף, צריך פונקציה ראשית שמריצה את הפונקציה create_tables כדי ליצור את הטבלה במסד הנתונים של CloudSQL, כמו בדוגמה הבאה:
if __name__ == '__main__':
from sqlalchemy import create_engine
engine = create_engine('sqlite:///shipping.db')
create_tables(engine)
print('Tables created successfully.')
שימו לב שהפונקציה main יוצרת מנוע באמצעות מסד נתונים מקומי sqlite. כדי להשתמש ב-CloudSQL, צריך לשנות את ההגדרה. את זה עושים קצת יותר מאוחר.
משתמשים ב-OPEN
בקוד בתהליך עבודה של קובץ חדש כמו קודם. שומרים את הקוד בקובץ בשם data_model.py (שימו לב לקו התחתון בשם, ולא למקף).
כדי לאפס את היסטוריית הצ'אט עם Duet AI, לוחצים על סמל האשפה
בחלק העליון של סרגל הצד של Duet AI.
connect-connector.py
יוצרים את המחבר של Cloud SQL.
פותחים את הקובץ data_model.py ומזינים את ההנחיות הבאות.
הנחיה 1: באמצעות ספריית cloud-sql-python-connector, צור פונקציה שמאתחלת מאגר חיבורים למופע Cloud SQL של Postgres.

שימו לב שהתשובה לא משתמשת בספרייה cloud-sql-python-connector. כדי לתת ל-Duet כיוון, אתם יכולים לשפר את ההנחיות על ידי הוספת פרטים ספציפיים לאותו שרשור צ'אט.
אפשר להשתמש בהנחיה אחרת.
הנחיה 2: חובה להשתמש בספרייה cloud-sql-python-connector.

מוודאים שהיא משתמשת בספרייה cloud-sql-python-connector.
משתמשים ב-OPEN
בקוד בתהליך עבודה של קובץ חדש כמו קודם. שומרים את הקוד בקובץ בשם connect_conector.py. יכול להיות שתצטרכו לייבא ידנית את הספרייה pg8000. פרטים נוספים מופיעים בקובץ שלמטה.
מנקים את היסטוריית הצ'אט עם Duet AI, ופותחים את הקובץ connect_connector.py כדי ליצור את ה-ORM DB engine, sessionmaker ו-base לשימוש באפליקציה.
הנחיה 1: יצירת מנוע, מחלקה של sessionmaker ו-Base ORM באמצעות השיטה connect_with_connector

יכול להיות שהתשובה תצורף לקובץ connect_connector.py עם התוספים engine, Session ו-Base.
הקובץ הסופי נמצא בקטע 'נספח' של ה-codelab הזה. אם לא, מבצעים את השינויים המתאימים באופן ידני.
אפשר גם לנסות הנחיות שונות כדי לראות את הווריאציות האפשריות בתשובות של Duet AI.
כדי לאפס את היסטוריית הצ'אט עם Duet AI, לוחצים על סמל האשפה
בחלק העליון של סרגל הצד של Duet AI.
עדכון של data_model.py
כדי ליצור טבלה במסד הנתונים של CloudSQL, צריך להשתמש במנוע שיצרתם בשלב הקודם (בקובץ connect_connector.py).
לנקות את היסטוריית הצ'אט עם Duet AI. פותחים את הקובץ data_model.py. אפשר לנסות את ההנחיה הבאה.
הנחיה 1: בפונקציה הראשית, מייבאים את המנוע מ-connect_connector.py ומשתמשים בו

אמורה להופיע התגובה 'ייבוא engine מ-connect_connector' (במקרה של CloudSQL). המאפיין create_table משתמש במנוע הזה (במקום במסד הנתונים המקומי sqlite שמוגדר כברירת מחדל).
מעדכנים את הקובץ data_model.py.
הקובץ הסופי נמצא בקטע 'נספח' של ה-codelab הזה. אם לא, מבצעים את השינויים המתאימים באופן ידני.
אפשר גם לנסות הנחיות שונות כדי לראות את התשובות השונות של Duet AI.
כדי לאפס את היסטוריית הצ'אט עם Duet AI, לוחצים על סמל האשפה
בחלק העליון של סרגל הצד של Duet AI.
requirements.txt
יוצרים קובץ requirements.txt לאפליקציה.
פותחים את connect_connector.py ואת הקובץ data_model.py ומזינים את ההנחיה הבאה.
הנחיה 1: יצירת קובץ דרישות pip למודל הנתונים ולשירות הזה
הנחיה 2: יצירת קובץ דרישות pip למודל הנתונים ולשירות הזה באמצעות הגרסאות העדכניות ביותר

מוודאים שהשמות והגרסאות נכונים. לדוגמה, בתגובה שלמעלה, השם והגרסה של google-cloud-sql-connecter שגויים. צריך לתקן את הגרסאות באופן ידני וליצור קובץ requirements.txt שנראה כך:
cloud-sql-python-connector==1.2.4
sqlalchemy==1.4.36
pg8000==1.22.0
בטרמינל הפקודות, מריצים את הפקודה הבאה:
pip3 install -r requirements.txt
כדי לאפס את היסטוריית הצ'אט עם Duet AI, לוחצים על סמל האשפה
בחלק העליון של סרגל הצד של Duet AI.
יצירת טבלת חבילות ב-CloudSQL
מגדירים את משתני הסביבה עבור מחבר מסד הנתונים של CloudSQL.
export INSTANCE_NAME=$(gcloud sql instances list --format='value(name)')
export INSTANCE_CONNECTION_NAME=$(gcloud sql instances describe ${INSTANCE_NAME} --format="value(connectionName)")
export DB_USER=evolution
export DB_PASS=evolution
export DB_NAME=product_details
עכשיו מריצים את data_model.py.
python data_model.py
הפלט אמור להיראות כך (כדאי לבדוק את הקוד כדי לראות מה צפוי בפועל):
Tables created successfully.
מתחברים למכונה של Cloud SQL ובודקים שנוצר מסד הנתונים.
gcloud sql connect ${INSTANCE_NAME} --user=evolution --database=product_details
אחרי שמזינים את הסיסמה (גם evolution), מקבלים את הטבלאות.
product_details=> \dt
הפלט אמור להיראות כך:
List of relations Schema | Name | Type | Owner --------+----------+-------+----------- public | packages | table | evolution (1 row)
אפשר גם לבדוק את מודל הנתונים ואת פרטי הטבלה.
product_details=> \d+ packages
הפלט אמור להיראות כך:
Table "public.packages"
Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
-------------------------------+-------------------+-----------+----------+--------------------------------------+----------+-------------+--------------+-------------
id | integer | | not null | nextval('packages_id_seq'::regclass) | plain | | |
product_id | integer | | not null | | plain | | |
height | double precision | | not null | | plain | | |
width | double precision | | not null | | plain | | |
depth | double precision | | not null | | plain | | |
weight | double precision | | not null | | plain | | |
special_handling_instructions | character varying | | | | extended | | |
Indexes:
"packages_pkey" PRIMARY KEY, btree (id)
Access method: heap
מקלידים \q כדי לצאת מ-Cloud SQL.
db_init.py
בשלב הבא, נוסיף כמה נתונים לדוגמה לטבלה packages.
לנקות את היסטוריית הצ'אט עם Duet AI. אחרי שפותחים את הקובץ data_model.py, מנסים את ההנחיות הבאות.
הנחיה 1: יצירת פונקציה שיוצרת 10 שורות של חבילות לדוגמה ומעבירה אותן לטבלת החבילות
הנחיה 2: באמצעות הסשן מ-connect_connector, צריך ליצור פונקציה שיוצרת 10 שורות של חבילות לדוגמה ומבצעת commit שלהן לטבלת החבילות

משתמשים ב-OPEN
בקוד בתהליך עבודה של קובץ חדש כמו קודם. שומרים את הקוד בקובץ בשם db_init.py.
הקובץ הסופי נמצא בקטע 'נספח' של ה-codelab הזה. אם לא, מבצעים את השינויים המתאימים באופן ידני.
אפשר גם לנסות הנחיות שונות כדי לראות את התשובות השונות של Duet AI.
כדי לאפס את היסטוריית הצ'אט עם Duet AI, לוחצים על סמל האשפה
בחלק העליון של סרגל הצד של Duet AI.
יצירת נתונים לדוגמה של חבילות
מריצים את הפקודה db_init.py משורת הפקודה.
python db_init.py
הפלט אמור להיראות כך:
Packages created successfully.
מתחברים שוב למופע CloudSQL ומוודאים שנתוני הדוגמה נוספו לטבלה packages.
מתחברים למכונה של Cloud SQL ובודקים שנוצר מסד הנתונים.
gcloud sql connect ${INSTANCE_NAME} --user=evolution --database=product_details
אחרי שמזינים את הסיסמה (גם evolution), מקבלים את כל הנתונים מהטבלה packages.
product_details=> SELECT * FROM packages;
הפלט אמור להיראות כך:
id | product_id | height | width | depth | weight | special_handling_instructions ----+------------+--------+-------+-------+--------+----------------------------------- 1 | 0 | 10 | 10 | 10 | 10 | No special handling instructions. 2 | 1 | 10 | 10 | 10 | 10 | No special handling instructions. 3 | 2 | 10 | 10 | 10 | 10 | No special handling instructions. 4 | 3 | 10 | 10 | 10 | 10 | No special handling instructions. 5 | 4 | 10 | 10 | 10 | 10 | No special handling instructions. 6 | 5 | 10 | 10 | 10 | 10 | No special handling instructions. 7 | 6 | 10 | 10 | 10 | 10 | No special handling instructions. 8 | 7 | 10 | 10 | 10 | 10 | No special handling instructions. 9 | 8 | 10 | 10 | 10 | 10 | No special handling instructions. 10 | 9 | 10 | 10 | 10 | 10 | No special handling instructions. (10 rows)
מקלידים \q כדי לצאת מ-Cloud SQL.
main.py
אחרי שפותחים את הקבצים data_model.py, package-service.yaml ו-connect_connector.py, יוצרים main.py לאפליקציה.
הנחיה 1: באמצעות ספריית Python Flask – צור הטמעה שמשתמשת בנקודות קצה של HTTP REST עבור השירות הזה
הנחיה 2: באמצעות ספריית ה-flask של Python – צור הטמעה שמשתמשת בנקודות קצה של http rest עבור השירות הזה. ייבא את SessionMaker מ-connect_conector.py והשתמש בו כדי לארוז נתונים.
הנחיה 3: באמצעות ספריית ה-flask של Python – יוצרים הטמעה שמשתמשת בנקודות קצה של http rest עבור השירות הזה. מייבאים ומשתמשים בחבילה מ-data_model.py וב-SessionMaker מ-connect_conector.py כדי לקבל נתוני חבילות.
הנחיה 4: באמצעות ספריית ה-flask של Python – יוצרים הטמעה שמשתמשת בנקודות קצה של HTTP REST עבור השירות הזה. מייבאים ומשתמשים בחבילה מ-data_model.py וב-SessionMaker מ-connect_conector.py לנתוני חבילות. שימוש בכתובת ה-IP של המארח 0.0.0.0 עבור app.run

צריך לעדכן את הדרישות של main.py.
הנחיה: צור קובץ דרישות עבור main.py

מוסיפים את הטקסט הזה לקובץ requirements.txt. חשוב להשתמש בגרסה 3.0.0 של Flask.
משתמשים בOPEN
הקוד בתהליך עבודה של קובץ חדש כמו קודם. שומרים את הקוד בקובץ בשם main.py.
הקובץ הסופי נמצא בקטע 'נספח' של ה-codelab הזה. אם לא, מבצעים את השינויים המתאימים באופן ידני.
כדי לאפס את היסטוריית הצ'אט עם Duet AI, לוחצים על סמל האשפה
בחלק העליון של סרגל הצד של Duet AI.
5. בדיקה והרצה של האפליקציה
מתקינים את הדרישות.
pip3 install -r requirements.txt
מריצים את main.py.
python main.py
הפלט אמור להיראות כך:
* Serving Flask app 'main' * Debug mode: off WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead. * Running on all addresses (0.0.0.0) * Running on http://127.0.0.1:5000 * Running on http://10.88.0.3:5000 Press CTRL+C to quit
ממסוף שני, בודקים את נקודת הקצה /packages/<product_id>.
curl localhost:5000/packages/1
הפלט אמור להיראות כך:
{"depth":10.0,"height":10.0,"special_handling_instructions":"No special handling instructions.","weight":10.0,"width":10.0}
אפשר גם לבדוק כל מזהה מוצר אחר בנתוני המדגם.
מזינים CTRL_C כדי לצאת מהקונטיינר הפעיל של Docker בטרמינל.
יצירת בדיקות יחידה
פותחים את הקובץ main.py ומפיקים בדיקות יחידה.
הנחיה 1: יצירת בדיקות יחידה.

משתמשים ב-OPEN
בקוד בתהליך עבודה של קובץ חדש כמו קודם. שומרים את הקוד בקובץ בשם test.py.
בפונקציה test_get_package, צריך להגדיר product_id. אפשר להוסיף אותו באופן ידני.
הקובץ הסופי נמצא בקטע 'נספח' של ה-codelab הזה. אם לא, מבצעים את השינויים המתאימים באופן ידני.
כדי לאפס את היסטוריית הצ'אט עם Duet AI, לוחצים על סמל האשפה
בחלק העליון של סרגל הצד של Duet AI.
הרצת בדיקות יחידה
מריצים את בדיקת היחידה.
python test.py
הפלט אמור להיראות כך:
. ---------------------------------------------------------------------- Ran 1 test in 1.061s OK
סוגרים את כל הקבצים ב-Cloud Shell Editor ומנקים את היסטוריית הצ'אט על ידי לחיצה על סמל הפח
בסרגל הסטטוס העליון.
קובץ Docker
ליצור Dockerfile לאפליקציה הזו.
פותחים את main.py ומנסים את ההנחיות הבאות.
הנחיה 1: צור קובץ Dockerfile לאפליקציה הזו.
הנחיה 2: צור קובץ Dockerfile לאפליקציה הזו. מעתיקים את כל הקבצים למאגר.

צריך גם להגדיר את ENVARS עבור INSTANCE_CONNECTION_NAME, DB_USER, DB_PASS ו-DB_NAME. אפשר לעשות את זה באופן ידני. קובץ ה-Dockerfile צריך להיראות כך:
FROM python:3.10-slim
WORKDIR /app
COPY . ./
RUN pip install -r requirements.txt
# Add these manually for your project
ENV INSTANCE_CONNECTION_NAME=YOUR_INSTANCE_CONNECTION_NAME
ENV DB_USER=evolution
ENV DB_PASS=evolution
ENV DB_NAME=product_details
CMD ["python", "main.py"]
משתמשים ב-OPEN
בקוד בתהליך עבודה של קובץ חדש כמו קודם. שומרים את הקוד בקובץ בשם Dockerfile.
הקובץ הסופי נמצא בקטע 'נספח' של ה-codelab הזה. אם לא, מבצעים את השינויים המתאימים באופן ידני.
הפעלת האפליקציה באופן מקומי
אחרי שפותחים את Dockerfile, אפשר לנסות את ההנחיה הבאה.
הנחיה 1: איך מריצים באופן מקומי קונטיינר באמצעות קובץ Dockerfile הזה

פועלים לפי ההוראות.
# Build docker build -t shipping . # And run docker run -p 5000:5000 -it shipping
הפלט אמור להיראות כך:
* Serving Flask app 'main' * Debug mode: off WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead. * Running on all addresses (0.0.0.0) * Running on http://127.0.0.1:5000 * Running on http://172.17.0.2:5000 Press CTRL+C to quit
מחלון טרמינל שני, ניגשים למאגר התגים.
curl localhost:5000/packages/1
הפלט אמור להיראות כך:
{"depth":10.0,"height":10.0,"special_handling_instructions":"No special handling instructions.","weight":10.0,"width":10.0}
האפליקציה בקונטיינר פועלת.
מזינים CTRL_C כדי לצאת מהקונטיינר הפעיל של Docker בטרמינל.
פיתוח קובץ אימג' של קונטיינר ב-Artifact Registry
יוצרים את קובץ האימג' של הקונטיינר ומעבירים אותו בדחיפה ל-Artifact Registry.
cd ~/shipping
gcloud auth configure-docker us-central1-docker.pkg.dev
docker build -t us-central1-docker.pkg.dev/${PROJECT_ID}/shipping/shipping .
docker push us-central1-docker.pkg.dev/${PROJECT_ID}/shipping/shipping
קונטיינר האפליקציה נמצא עכשיו במיקום us-central1-docker.pkg.dev/${PROJECT_ID}/shipping/shipping, שאפשר לפרוס אותו ב-GKE.
6. פריסת האפליקציה באשכול GKE
כשבניתם את משאבי ה-GCP לסדנה הזו, נוצר אשכול GKE Autopilot. מתחברים לאשכול GKE.
gcloud container clusters get-credentials gke1 \
--region=us-central1
מוסיפים הערה לחשבון השירות שמוגדר כברירת מחדל ב-Kubernetes עם חשבון השירות של Google.
kubectl annotate serviceaccount default iam.gke.io/gcp-service-account=cloudsqlsa@${PROJECT_ID}.iam.gserviceaccount.com
הפלט אמור להיראות כך:
serviceaccount/default annotated
מכינים ומחילים את הקובץ k8s.yaml.
cp ~/duetaidev/k8s.yaml_tmpl ~/shipping/.
export INSTANCE_NAME=$(gcloud sql instances list --format='value(name)')
export INSTANCE_CONNECTION_NAME=$(gcloud sql instances describe ${INSTANCE_NAME} --format="value(connectionName)")
export IMAGE_REPO=us-central1-docker.pkg.dev/${PROJECT_ID}/shipping/shipping
envsubst < ~/shipping/k8s.yaml_tmpl > k8s.yaml
kubectl apply -f k8s.yaml
הפלט אמור להיראות כך:
deployment.apps/shipping created service/shipping created
מחכים עד שה-Pods יפעלו ולשירות תוקצה כתובת IP של מאזן עומסים חיצוני.
kubectl get pods kubectl get service shipping
הפלט אמור להיראות כך:
# kubectl get pods NAME READY STATUS RESTARTS AGE shipping-f5d6f8d5-56cvk 1/1 Running 0 4m47s shipping-f5d6f8d5-cj4vv 1/1 Running 0 4m48s shipping-f5d6f8d5-rrdj2 1/1 Running 0 4m47s # kubectl get service shipping NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE shipping LoadBalancer 34.118.225.125 34.16.39.182 80:30076/TCP 5m41s
באשכולות GKE Autopilot, צריך להמתין כמה רגעים עד שהמשאבים יהיו מוכנים.
גישה לשירות דרך הכתובת EXTERNAL-IP.
export EXTERNAL_IP=$(kubectl get svc shipping --output jsonpath='{.status.loadBalancer.ingress[0].ip}')
curl http://${EXTERNAL_IP}/packages/1
הפלט אמור להיראות כך:
{"depth":10.0,"height":10.0,"special_handling_instructions":"No special handling instructions.","weight":10.0,"width":10.0}
7. בונוס: פתרון בעיות באפליקציה
מסירים את תפקיד ה-IAM של לקוח CloudSQL מחשבון השירות cloudsqlsa. הפעולה הזו גורמת לשגיאה בחיבור למסד הנתונים של CloudSQL.
gcloud projects remove-iam-policy-binding ${PROJECT_ID} \
--member="serviceAccount:cloudsqlsa@${PROJECT_ID}.iam.gserviceaccount.com" \
--role="roles/cloudsql.client"
מפעילים מחדש את ה-Pod של המשלוח.
kubectl rollout restart deployment shipping
אחרי שה-Pod מופעל מחדש, מנסים לגשת שוב לשירות shipping.
export EXTERNAL_IP=$(kubectl get svc shipping --output jsonpath='{.status.loadBalancer.ingress[0].ip}')
curl http://${EXTERNAL_IP}/packages/1
הפלט אמור להיראות כך:
... <title>500 Internal Server Error</title> <h1>Internal Server Error</h1> <p>The server encountered an internal error and was unable to complete your request. Either the server is overloaded or there is an error in the application.</p>
כדי לבדוק את היומנים, עוברים אל Kubernetes Engine > Workloads

לוחצים על הפריסה shipping ואז על הכרטיסייה Logs (יומנים).

לוחצים על הסמל הצגה בכלי לבדיקת יומנים
בצד שמאל של שורת הסטטוס. ייפתח חלון חדש של Log Explorer.

לוחצים על אחת מהשגיאות Traceback ואז על Explain this Log Entry (הסבר על רשומה ביומן).

אפשר לקרוא את ההסבר לשגיאה.
בשלב הבא, נבקש מ-Duet AI לעזור בפתרון הבעיה.
אפשר לנסות את ההנחיה הבאה.
הנחיה 1: עזרה בפתרון השגיאה הזו

מזינים את הודעת השגיאה בהנחיה.
הנחיה 2: Forbidden: נראה שחשבון המשתמש המאומת ב-IAM לא מורשה לשלוח בקשה ל-API. צריך לוודא ש-Cloud SQL Admin API מופעל בפרויקט GCP ושניתן התפקיד Cloud SQL Client לחשבון הראשי ב-IAM

ואז.
הנחיה 3: איך מקצים את התפקיד 'לקוח Cloud SQL' לחשבון שירות של Google באמצעות gcloud?

מקצים את התפקיד Cloud SQL Client למשתמש cloudsqlsa.
gcloud projects add-iam-policy-binding ${PROJECT_ID} \
--member="serviceAccount:cloudsqlsa@${PROJECT_ID}.iam.gserviceaccount.com" \
--role="roles/cloudsql.client"
מחכים כמה רגעים ומנסים שוב לגשת לאפליקציה.
export EXTERNAL_IP=$(kubectl get svc shipping --output jsonpath='{.status.loadBalancer.ingress[0].ip}')
curl http://${EXTERNAL_IP}/packages/1
הפלט אמור להיראות כך:
{"depth":10.0,"height":10.0,"special_handling_instructions":"No special handling instructions.","weight":10.0,"width":10.0}
השתמשתם בהצלחה ב-Duet AI ב-Cloud Logging, ב-Log Explorer ובתכונה Log Explainer כדי לפתור את הבעיה.
8. סיכום
מעולה! סיימתם את ה-Codelab הזה.
ב-codelab הזה למדתם:
- מפעילים את Duet AI בפרויקט GCP ומגדירים אותו לשימוש בסביבת פיתוח משולבת (IDE) ובמסוף Cloud.
- שימוש ב-Duet AI ליצירה, להשלמה ולהסבר של קוד.
- להשתמש ב-Duet AI כדי להסביר ולפתור בעיה באפליקציה.
- תכונות של Duet AI כמו צ'אט IDE ושיחה עם זיכרון, צ'אט לעומת יצירת קוד בשורה, פעולות חכמות כמו הסבר על קוד ואישור של הקראת קוד ועוד.
9. נספח
package-service.yaml
swagger: "2.0"
info:
title: Shipping and Package Information API
description: This API provides information about shipping and packages.
version: 1.0.0
host: shipping.googleapis.com
schemes:
- https
produces:
- application/json
paths:
/packages/{product_id}:
get:
summary: Get information about a package
description: This method returns information about a package, including its height, width, depth, weight, and any special handling instructions.
parameters:
- name: product_id
in: path
required: true
type: integer
format: int64
responses:
"200":
description: A successful response
schema:
type: object
properties:
height:
type: integer
format: int64
width:
type: integer
format: int64
depth:
type: integer
format: int64
weight:
type: integer
format: int64
special_handling_instructions:
type: string
"404":
description: The product_id was not found
data_model.py
from sqlalchemy import Column, Integer, String, Float
from sqlalchemy.ext.declarative import declarative_base
from connect_connector import engine
Base = declarative_base()
class Package(Base):
__tablename__ = 'packages'
id = Column(Integer, primary_key=True)
product_id = Column(Integer, nullable=False)
height = Column(Float, nullable=False)
width = Column(Float, nullable=False)
depth = Column(Float, nullable=False)
weight = Column(Float, nullable=False)
special_handling_instructions = Column(String, nullable=True)
def create_tables():
Base.metadata.create_all(engine)
if __name__ == '__main__':
create_tables()
print('Tables created successfully.')
connect_connector.py
import os
from google.cloud.sql.connector import Connector, IPTypes
import sqlalchemy
# You may need to manually import pg8000 and Base as follows
import pg8000
from sqlalchemy.ext.declarative import declarative_base
def connect_with_connector() -> sqlalchemy.engine.base.Engine:
"""Initializes a connection pool for a Cloud SQL instance of Postgres."""
# Note: Saving credentials in environment variables is convenient, but not
# secure - consider a more secure solution such as
# Cloud Secret Manager (https://cloud.google.com/secret-manager) to help
# keep secrets safe.
instance_connection_name = os.environ[
"INSTANCE_CONNECTION_NAME"
] # e.g. 'project:region:instance'
db_user = os.environ["DB_USER"] # e.g. 'my-database-user'
db_pass = os.environ["DB_PASS"] # e.g. 'my-database-password'
db_name = os.environ["DB_NAME"] # e.g. 'my-database'
ip_type = IPTypes.PRIVATE if os.environ.get("PRIVATE_IP") else IPTypes.PUBLIC
connector = Connector()
def getconn() -> sqlalchemy.engine.base.Engine:
conn: sqlalchemy.engine.base.Engine = connector.connect(
instance_connection_name,
"pg8000",
user=db_user,
password=db_pass,
db=db_name,
ip_type=ip_type,
)
return conn
pool = sqlalchemy.create_engine(
"postgresql+pg8000://",
creator=getconn,
# ...
)
return pool
# Create a connection pool
engine = connect_with_connector()
# Create a sessionmaker class to create new sessions
SessionMaker = sqlalchemy.orm.sessionmaker(bind=engine)
# Create a Base class for ORM
# You may need to manually fix the following
Base = declarative_base()
db_init.py
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
from connect_connector import engine
from data_model import Package
def create_packages():
# Create a session
session = sessionmaker(bind=engine)()
# Create 10 sample packages
for i in range(10):
package = Package(
product_id=i,
height=10.0,
width=10.0,
depth=10.0,
weight=10.0,
special_handling_instructions="No special handling instructions."
)
# Add the package to the session
session.add(package)
# Commit the changes
session.commit()
if __name__ == '__main__':
create_packages()
print('Packages created successfully.')
main.py
from flask import Flask, request, jsonify
from data_model import Package
from connect_connector import SessionMaker
app = Flask(__name__)
session_maker = SessionMaker()
@app.route("/packages/<int:product_id>", methods=["GET"])
def get_package(product_id):
"""Get information about a package."""
session = session_maker
package = session.query(Package).filter(Package.product_id == product_id).first()
if package is None:
return jsonify({"message": "Package not found."}), 404
return jsonify(
{
"height": package.height,
"width": package.width,
"depth": package.depth,
"weight": package.weight,
"special_handling_instructions": package.special_handling_instructions,
}
), 200
if __name__ == "__main__":
app.run(host="0.0.0.0")
test.py
import unittest
from data_model import Package
from connect_connector import SessionMaker
from main import app
class TestPackage(unittest.TestCase):
def setUp(self):
self.session_maker = SessionMaker()
def tearDown(self):
self.session_maker.close()
def test_get_package(self):
"""Test the `get_package()` function."""
package = Package(
product_id=11, # Ensure that the product_id different from the sample data
height=10,
width=10,
depth=10,
weight=10,
special_handling_instructions="Fragile",
)
session = self.session_maker
session.add(package)
session.commit()
response = app.test_client().get("/packages/11")
self.assertEqual(response.status_code, 200)
self.assertEqual(
response.json,
{
"height": 10,
"width": 10,
"depth": 10,
"weight": 10,
"special_handling_instructions": "Fragile",
},
)
if __name__ == "__main__":
unittest.main()
requirements.txt
cloud-sql-python-connector==1.2.4
sqlalchemy==1.4.36
pg8000==1.22.0
Flask==3.0.0
gunicorn==20.1.0
psycopg2-binary==2.9.3
קובץ Docker
FROM python:3.10-slim
WORKDIR /app
COPY . ./
RUN pip install -r requirements.txt
# Add these manually for your project
ENV INSTANCE_CONNECTION_NAME=YOUR_INSTANCE_CONNECTION_NAME
ENV DB_USER=evolution
ENV DB_PASS=evolution
ENV DB_NAME=product_details
CMD ["python", "main.py"]