מדריך סדנה טכנית מעשית באמצעות Duet AI למפתחים Codelab

1. מטרות

מטרת הסדנה הזאת היא לספק הדרכה מעשית בנושא Duet AI למשתמשים ולמומחים.

ב-Codelab הזה תלמדו:

  1. צריך להפעיל את Duet AI בפרויקט ב-GCP ולהגדיר אותו לשימוש בסביבת פיתוח משולבת (IDE) ובמסוף Cloud.
  2. שימוש ב-Duet AI ליצירת קוד, להשלמה ולהסבר.
  3. אפשר להשתמש ב-Duet AI כדי להסביר בעיה באפליקציה ולפתור אותה.
  4. תכונות של Duet AI כמו צ'אט בסביבת פיתוח משולבת (IDE) וצ'אט מרובה פניות, צ'אט לעומת יצירת קוד בגוף ההודעה, פעולות חכמות כמו הסבר על הקוד, הכרת הקוד ועוד.

הסיפור

כדי להראות איך השימוש ב-Duet AI למפתחים בפיתוח היומיומי שלו, הפעילויות בסדנה מתקיימות בהקשר של סיפור.

מפתח חדש מצטרף לחברת מסחר אלקטרוני. המשימה שלהם היא להוסיף שירות חדש לאפליקציה הקיימת של המסחר האלקטרוני (שמורכבת מכמה שירותים). השירות החדש מספק מידע נוסף (מידות, משקל וכו') על המוצרים בקטלוג המוצרים. השירות הזה מאפשר עלויות משלוח טובות או זולות יותר על סמך המידות והמשקל של המוצר.

המפתח הוא חדש בחברה, לכן הוא ישתמש ב-Duet AI ליצירת קוד, להסבר ולתיעוד.

לאחר הקידוד של השירות, אדמין של הפלטפורמה ישתמש ב-Duet AI (צ'אט) כדי ליצור את הארטיפקט (קונטיינר Docker) ובמשאבים הדרושים לפריסת הארטיפקט ב-GCP (לדוגמה Artifact Registry, הרשאות IAM, מאגר קוד, תשתית מחשוב, כלומר GKE או CloudRun וכו').

לאחר פריסת האפליקציה ב-GCP, מפעילי אפליקציות/SRE ישתמשו ב-Duet AI (וב-Cloud Ops) כדי לפתור שגיאה בשירות החדש.

פרסונה

הסדנה עוסקת בפרסונה הבאה:

  1. מפתח אפליקציות – נדרש ידע מסוים בתכנות ובפיתוח תוכנות.

הווריאציה הזו של סדנת Duet AI מיועדת למפתחים בלבד. לא נדרש ידע על משאבי הענן של GCP. הסקריפטים הדרושים לפיתוח משאבי ה-GCP הנדרשים להפעלת האפליקציה הזו זמינים כאן. תוכלו לפעול לפי ההוראות שבמדריך הזה כדי לפרוס את המשאבים הנדרשים של GCP.

2. הכנת הסביבה

הפעלה של Duet AI

אפשר להפעיל את Duet AI בפרויקט GCP באמצעות API (כלים של gcloud או IaC כמו Terraform) או דרך ממשק המשתמש של מסוף Cloud.

כדי להפעיל את Duet AI בפרויקט ב-Google Cloud, מפעילים את Cloud AI Companion API ומקצים למשתמשים את התפקידים 'משתמש נלווה ב-Cloud AI Companion' ו'ניהול זהויות והרשאות גישה (IAM)' של השימוש בשירות.

מ-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.

מקצים לחשבון המשתמש את התפקיד 'משתמש Companion ב-Cloud AI' ואת התפקידים 'ניהול זהויות והרשאות גישה (IAM)' של השימוש בשירות. ה-Cloud Companion API נמצא מאחורי התכונות בסביבת הפיתוח המשולבת (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

כדי להפעיל את ה-API, נכנסים לדף Cloud AI Companion API במסוף Google Cloud.

בבורר הפרויקטים, בוחרים את הפרויקט.

לוחצים על Enable.

הדף יתעדכן ויציג את הסטטוס מופעל. Duet AI זמין עכשיו בפרויקט Google Cloud שנבחר לכל המשתמשים שיש להם את תפקידי ה-IAM הנדרשים.

בדף IAM אפשר להקצות את תפקידי ה-IAM שנדרשים לשימוש ב-Duet AI.

בעמודה Principal, מחפשים את המשתמש שעבורו רוצים להפעיל את הגישה ל-Duet AI ואז לוחצים על סמל העיפרון ✏️ Edit principal בשורה הזו.

בחלונית הגישה Edit, לוחצים על Add Add another role.

בקטע 'בחירת תפקיד', בוחרים באפשרות Cloud AI Companion User.

לוחצים על Add another role ובוחרים באפשרות Service Usage Viewer.

לוחצים על שמירה.

הגדרת סביבת הפיתוח המשולבת (IDE)

המפתחים יכולים לבחור מתוך מגוון סביבות פיתוח משולבות (IDE) שהכי מתאימות לצרכים שלהם. אפשר לקבל עזרה בכתיבת קוד באמצעות Duet AI בכמה סביבות פיתוח משולבות (IDE), כמו Visual Studio Code, JetBrains IDEs (IntelliJ, PyCharm, GoLand, WebStorm ועוד), Cloud Workstations ו-Cloud Shell Editor.

בשיעור ה-Lab הזה תוכלו להשתמש ב-Cloud Workstations או ב-Cloud Shell Editor.

בסדנה הזאת אנחנו משתמשים ב-Cloud Shell Editor.

הערה: תהליך ההגדרה של Cloud Workstations עשוי להימשך 20-30 דקות.

כדי להתחיל מיד, משתמשים ב-Cloud Shell Editor.

פותחים את Cloud Shell Editor בלחיצה על סמל העיפרון ✏️ בסרגל התפריטים העליון ב-Cloud Shell.

ממשק המשתמש וחוויית המשתמש של Cloud Shell Editor דומים מאוד ל-VSCode.

d6a6565f83576063.png

לוחצים על CTRL (ב-Windows)/CMD (ב-Mac) + , (פסיק) כדי להיכנס לחלונית ההגדרות.

בסרגל החיפוש, מקלידים 'duet AI'.

צריך לוודא או להפעיל את Cloudcode › Duet AI: הפעלה ו-Cloudcode › Duet AI › הצעות מוטבעות: הפעלה אוטומטית

111b8d587330ec74.png

בשורת הסטטוס התחתונה, לוחצים על Cloud Code – כניסה ופועלים לפי תהליך הכניסה לחשבון.

אם כבר נכנסתם לחשבון, בשורת הסטטוס יופיע Cloud Code – אין פרויקט.

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

3241a59811e3c84a.png

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

c5358fc837588fe.png

בוחרים את PROJECT_ID מרשימת הפרויקטים.

שורת הסטטוס התחתונה תתעדכן ותוכלו לראות את מזהה הפרויקט. אם הוא לא חוזר, יכול להיות שתצטרכו לרענן את הכרטיסייה של Cloud Shell Editor.

לוחצים על הסמל של Duet AI d97fc4e7b594c3af.png בסרגל התפריטים השמאלי, וחלון הצ'אט של Duet AI מופיע. אם מוצגת ההודעה Select GCP Project. לוחצים על הפרויקט ובוחרים אותו מחדש.

עכשיו מוצג חלון הצ'אט של Duet AI

781f888360229ca6.png

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

d3234d237f00fdbb.png

כדי להפעיל את שירות המשלוחים החדש ב-GCP, צריך להשתמש במקורות המידע הבאים של GCP:

  1. מכונה של Cloud SQL, עם מסד נתונים.
  2. אשכול GKE להרצת השירות בקונטיינרים.
  3. Artifact Registry לאחסון קובץ אימג' של Docker.
  4. 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. פיתוח שירות Fithon Flask

9745ba5c70782e76.png

השירות שניצור יכלול בסופו של דבר את הקבצים הבאים. אין צורך ליצור את הקבצים האלה עכשיו. את הקבצים האלה אפשר ליצור בכל פעם בהתאם להוראות הבאות:

  1. package-service.yaml – מפרט API פתוח לשירות החבילה, שכולל נתונים כמו גובה, רוחב, משקל והוראות לטיפול מיוחד.
  2. data_model.py – מודל הנתונים למפרט ה-API של שירות החבילות. יוצרת גם את הטבלה packages ב-product_details DB.
  3. connect_connector.py – חיבור ל-CloudSQL (מגדירים את המנוע, הסשן ו-Base ORM)
  4. db_init.py – יצירת נתונים לדוגמה לטבלה packages.
  5. main.py – שירות Python Flask עם נקודת קצה (endpoint) GET שמאחזר את פרטי החבילה מנתוני packages על סמך product_id.
  6. test.py – בדיקת יחידה
  7. requirement.txt – דרישות Python
  8. Dockerfile – כדי ליצור קונטיינרים של האפליקציה הזו

אם נתקלים בבעיות במיקום קבוע במהלך התרגילים, הקבצים הסופיים מופיעים בנספח ב-Codelab הזה.

בשלב הקודם יצרתם מאגר Cloud Source Repository. משכפלים את המאגר. אתם תיצרו את קובצי האפליקציה בתיקיית המאגר המשוכפל.

בטרמינל של Cloud Shell, מריצים את הפקודה הבאה כדי לשכפל את המאגר.

cd ~
gcloud source repos clone shipping shipping
cd ~/shipping 

פותחים את סרגל הצד של הצ'אט ב-Duet AI בתפריט השמאלי של Cloud Shell Editor. הסמל נראה כמו 8b135a000b259175.png. עכשיו יש לך אפשרות להיעזר ב-Duet AI לקבלת עזרה בתכנות.

package-service.yaml

אם אין קבצים פתוחים, צריך לבקש מ-Duet ליצור מפרט Open API לשירות המשלוחים.

הנחיה 1: יצירת מפרט OpenAPI yaml לשירות שמספק פרטי משלוח וחבילה לפי מזהה מוצר מספרי. השירות צריך לכלול מידע על הגובה, הרוחב, העומק, המשקל של האריזה וכן הוראות לטיפול מיוחד.

ba12626f491a1204.png

מופיעות שלוש אפשרויות בפינה השמאלית העליונה של חלון הקוד שנוצר.

אפשר COPY 71194556d8061dae.png את הקוד ולהדביק אותו בקובץ.

אפשר ADD df645de8c65607a.png את הקוד לקובץ שפתוח עכשיו ב-Editor.

לחלופין, אפשר OPEN a4c7ed6d845df343.png את הקוד בקובץ חדש.

לוחצים על הסמל OPEN a4c7ed6d845df343.png על הקוד בקובץ חדש.

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

f6ebd5b836949366.png

הקובץ הסופי נמצא בקטע 'נספח' ב-Codelab הזה. אם הוא לא מתרחש, צריך לבצע את השינויים הנדרשים באופן ידני.

אפשר גם לנסות כמה הנחיות כדי לראות את התשובות של Duet AI.

כדי לאפס את היסטוריית הצ'אט של Duet AI, לוחצים על סמל האשפה f574ca2c1e114856.png בחלק העליון של סרגל הצד של Duet AI.

data_model.py

בשלב הבא יוצרים את קובץ python של מודל הנתונים עבור השירות, על סמך מפרט OpenAPI.

כשהקובץ package-service.yaml פתוח, מזינים את ההנחיה הבאה.

הנחיה 1: באמצעות python sqlalchemy ORM, יוצרים מודל נתונים לשירות ה-API הזה. צריך לכלול גם פונקציה נפרדת ונקודת כניסה ראשית שיוצרת את הטבלאות של מסדי הנתונים.

b873a6a28bd28ca1.png

בואו נסתכל על כל חלק שנוצר. Duet AI הוא עדיין העוזר הדיגיטלי, ולמרות שהוא יכול לעזור בכתיבה מהירה של קוד, עדיין כדאי לבדוק את התוכן שנוצר ולהבין אותו תוך כדי כתיבה.

ראשית, יש מחלקה בשם Package מסוג 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 a4c7ed6d845df343.png בתהליך עבודה חדש של קבצים כמו קודם. שומרים את הקוד בקובץ בשם data_model.py (שימו לב לקו תחתון בשם ולא במקף).

כדי לאפס את היסטוריית הצ'אט של Duet AI, לוחצים על סמל האשפה f574ca2c1e114856.png בחלק העליון של סרגל הצד של Duet AI.

connect-connector.py

יוצרים את המחבר של CloudSQL.

כשהקובץ data_model.py פתוח, מזינים את ההנחיות הבאות.

הנחיה ראשונה: באמצעות הספרייה cloud-sql-python-connector, יוצרים פונקציה שמפעילה מאגר חיבור למופע של Cloud SQL של Postgres.

ed05cb6ff85d34c5.png

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

נשתמש בהנחיה אחרת.

הנחיה 2: חובה להשתמש בספריית cloud-sql-python-connector.

d09095b44dde35bf.png

חשוב לוודא שהוא משתמש בספרייה cloud-sql-python-connector.

שימוש בקוד OPEN a4c7ed6d845df343.png בתהליך עבודה חדש של קבצים כמו קודם. שומרים את הקוד בקובץ בשם connect_conector.py. יכול להיות שיהיה עליך לייבא באופן ידני את הספרייה pg8000. כדאי לעיין בקובץ שבהמשך.

מוחקים את היסטוריית הצ'אט של Duet AI, וכשהקובץ connect_connector.py פתוח, יוצרים את ה-ORM DB engine, sessionmaker ו-base לשימוש באפליקציה.

הנחיה 1: יצירת מנוע, מחלקה של יוצרי סשן ו-Base ORM באמצעות שיטת connect_with_connector

6e4214b72ab13a63.png

התגובה עשויה לצרף את engine, Session ואת Base לקובץ connect_connector.py.

הקובץ הסופי נמצא בקטע 'נספח' ב-Codelab הזה. אם הוא לא מתרחש, צריך לבצע את השינויים הנדרשים באופן ידני.

אפשר גם לנסות הנחיות שונות כדי לראות את הווריאציה הפוטנציאלית בתשובות של Duet AI.

כדי לאפס את היסטוריית הצ'אט של Duet AI, לוחצים על סמל האשפה f574ca2c1e114856.png בחלק העליון של סרגל הצד של Duet AI.

עדכון data_model.py

כדי ליצור טבלה במסד הנתונים של CloudSQL, עליכם להשתמש במנוע שיצרתם בשלב הקודם (בקובץ connect_connector.py).

מחיקת היסטוריית הצ'אט של Duet AI. פותחים את הקובץ data_model.py. אפשר לנסות להזין את ההנחיה הבאה.

הנחיה 1: בפונקציה הראשית, מייבאים את המנוע מ-connect_connector.py ומשתמשים בו

2e768c9b6c523b9a.png

אמורה להופיע התשובה שמייבאת את engine מ-connect_connector (ל-CloudSQL). ה-create_table משתמש במנוע הזה (במקום בברירת המחדל של דציבלים מקומיים מסוג sqlite).

עדכון קובץ data_model.py.

הקובץ הסופי נמצא בקטע 'נספח' ב-Codelab הזה. אם הוא לא מתרחש, צריך לבצע את השינויים הנדרשים באופן ידני.

אפשר גם לנסות הנחיות שונות כדי לראות את התשובות השונות של Duet AI.

כדי לאפס את היסטוריית הצ'אט של Duet AI, לוחצים על סמל האשפה f574ca2c1e114856.png בחלק העליון של סרגל הצד של Duet AI.

requirements.txt

יוצרים קובץ requirements.txt לאפליקציה.

פותחים גם את connect_connector.py וגם את הקובץ data_model.py ומזינים את ההנחיה הבאה.

הנחיה 1: יצירת קובץ דרישות של PIP למודל הנתונים ולשירות הזה

הנחיה 2: יצירת קובץ דרישות של PIP למודל הנתונים ולשירות הזה באמצעות הגרסאות האחרונות

69fae373bc5c6a18.png

יש לוודא שהשמות והגרסאות נכונים. לדוגמה, בתשובה שלמעלה, השם והגרסה של 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, לוחצים על סמל האשפה f574ca2c1e114856.png בחלק העליון של סרגל הצד של 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.

מתחברים למכונה של CloudSQL ובודקים שמסד הנתונים נוצר.

gcloud sql connect ${INSTANCE_NAME} --user=evolution --database=product_details

אחרי שמזינים את הסיסמה (גם אבולוציה), מקבלים את הטבלאות.

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

כדי לצאת מ-CloudSQL, מקלידים \q.

db_init.py

בשלב הבא נוסיף נתונים לדוגמה לטבלה packages.

מחיקת היסטוריית הצ'אט של Duet AI. כשהקובץ data_model.py פתוח, אפשר לנסות את ההנחיות הבאות.

הנחיה 1: יצירת פונקציה שיוצרת 10 שורות של חבילות לדוגמה ומשייכת אותן לטבלת החבילות

הנחיה 2: באמצעות הסשן מ-connect_connector, יוצרים פונקציה שיוצרת 10 שורות של חבילות לדוגמה ומציבה אותן בטבלת החבילות

34a9afc5f04ba5.png

שימוש בקוד OPEN a4c7ed6d845df343.png בתהליך עבודה חדש של קבצים כמו קודם. שומרים את הקוד בקובץ בשם db_init.py.

הקובץ הסופי נמצא בקטע 'נספח' ב-Codelab הזה. אם הוא לא מתרחש, צריך לבצע את השינויים הנדרשים באופן ידני.

אפשר גם לנסות הנחיות שונות כדי לראות את התשובות השונות של Duet AI.

כדי לאפס את היסטוריית הצ'אט של Duet AI, לוחצים על סמל האשפה f574ca2c1e114856.png בחלק העליון של סרגל הצד של Duet AI.

יצירת נתונים של חבילות לדוגמה

מריצים את הפקודה db_init.py משורת הפקודה.

python db_init.py

הפלט אמור להיראות כך:

Packages created successfully.

מתחברים שוב למכונה של CloudSQL ומוודאים שהנתונים לדוגמה נוספו לטבלת החבילות.

מתחברים למכונה של CloudSQL ובודקים שמסד הנתונים נוצר.

gcloud sql connect ${INSTANCE_NAME} --user=evolution --database=product_details

אחרי הזנת הסיסמה (גם אבולוציה), צריך לאחזר את כל הנתונים מטבלת החבילות.

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)

כדי לצאת מ-CloudSQL, מקלידים \q.

main.py

כשהקבצים data_model.py, package-service.yaml ו-connect_connector.py פתוחים, צריך ליצור main.py לאפליקציה.

הנחיה 1: שימוש בספריית בקבוקי python – יצירת הטמעה שמשתמשת בנקודות קצה של מנוחה ב-http לשירות הזה

הנחיה 2: שימוש בספריית בקבוקי python – יצירת הטמעה שמשתמשת בנקודות קצה של מנוחה ב-http לשירות הזה. לייבא ולהשתמש ב-sessionMaker מ-connect_conector.py אל לנתוני חבילות.

הנחיה 3: שימוש בספריית בקבוקי python – יצירת הטמעה שמשתמשת בנקודות קצה של מנוחה ב-http לשירות הזה. לייבא ולהשתמש ב-Package מ-data_model.py ומ-SessionMaker מ-connect_conector.py אל נתוני חבילות.

הנחיה 4: שימוש בספריית בקבוקי python – יצירת הטמעה שמשתמשת בנקודות קצה של מנוחה ב-http לשירות הזה. לייבא ולהשתמש ב-Package מ-data_model.py ומ-SessionMaker מ-connect_conector.py אל נתוני חבילות. שימוש בכתובת ה-IP של המארח 0.0.0.0 ל-app.run

6d794fc52a90e6ae.png

עליך לעדכן את הדרישות לגבי main.py.

הנחיה: יצירת קובץ דרישות ל-main.py

1cc0b318d2d4ca2f.png

צירוף לקובץ requirements.txt. עליכם לוודא שאתם משתמשים ב-Flask גרסה 3.0.0.

שימוש בקוד OPEN a4c7ed6d845df343.png בתהליך עבודה חדש של קבצים כמו קודם. שומרים את הקוד בקובץ בשם main.py.

הקובץ הסופי נמצא בקטע 'נספח' ב-Codelab הזה. אם הוא לא מתרחש, צריך לבצע את השינויים הנדרשים באופן ידני.

כדי לאפס את היסטוריית הצ'אט של Duet AI, לוחצים על סמל האשפה f574ca2c1e114856.png בחלק העליון של סרגל הצד של 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}

תוכלו גם לבדוק כל מזהה מוצר אחר בנתונים לדוגמה.

כדי לצאת ממאגר ה-Docker הפעיל בטרמינל, מזינים CTRL_C.

יצירת בדיקות יחידה

כשהקובץ main.py פתוח, יוצרים בדיקות יחידה (unit testing).

הנחיה 1: יצירת בדיקות יחידה.

e861e5b63e1b2657.png

שימוש בקוד OPEN a4c7ed6d845df343.png בתהליך עבודה חדש של קבצים כמו קודם. שומרים את הקוד בקובץ בשם test.py.

בפונקציה test_get_package, צריך להגדיר product_id. אפשר להוסיף אותו באופן ידני.

הקובץ הסופי נמצא בקטע 'נספח' ב-Codelab הזה. אם הוא לא מתרחש, צריך לבצע את השינויים הנדרשים באופן ידני.

כדי לאפס את היסטוריית הצ'אט של Duet AI, לוחצים על סמל האשפה f574ca2c1e114856.png בחלק העליון של סרגל הצד של Duet AI.

מתבצעות בדיקות יחידה (unit testing)

מריצים את בדיקת היחידה.

python test.py

הפלט אמור להיראות כך:

.
----------------------------------------------------------------------
Ran 1 test in 1.061s

OK

סוגרים את כל הקבצים ב-Cloud Shell Editor ומנקים את היסטוריית הצ'אט בלחיצה על סמל האשפה 1ecccfe10d6c540.png בשורת הסטטוס העליונה.

קובץ Docker

יצירת Dockerfile לאפליקציה הזו.

אפשר לפתוח את main.py ולנסות את ההנחיות הבאות.

הנחיה 1: יצירת קובץ Docker לאפליקציה הזו.

הנחיה 2: יצירת קובץ Docker לאפליקציה הזו. מעתיקים את כל הקבצים למאגר התגים.

9c473caea437a5c3.png

צריך גם להגדיר את ENVARS עבור INSTANCE_CONNECTION_NAME, DB_USER, DB_PASS וגם DB_NAME. אפשר לעשות זאת באופן ידני. קובץ ה-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"]

שימוש בקוד OPEN a4c7ed6d845df343.png בתהליך עבודה חדש של קבצים כמו קודם. שומרים את הקוד בקובץ בשם Docker.

הקובץ הסופי נמצא בקטע 'נספח' ב-Codelab הזה. אם הוא לא מתרחש, צריך לבצע את השינויים הנדרשים באופן ידני.

הרצה מקומית של האפליקציה

כשהDockerfile פתוח, אפשר לנסות את ההנחיה הבאה.

הנחיה 1: איך מריצים קונטיינר באופן מקומי באמצעות קובץ ה-Docker הזה

570fd5c296ca8c83.png

פועלים לפי ההוראות.

# 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}

האפליקציה בקונטיינרים פועלת.

כדי לצאת ממאגר ה-Docker הפעיל בטרמינל, מזינים CTRL_C.

פיתוח קובץ אימג' של קונטיינר ב-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

אשכול GKE Autopilot נוצר כשיצרתם את משאבי GCP לסדנה הזאת. התחברות לאשכול 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. Extra Credit: פתרון בעיות באפליקציה

הסרת תפקיד ה-IAM של לקוח Cloud SQL מחשבון השירות של 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)

d225b1916c829167.png

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

1d0459141483d6a7.png

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

e86d1c265e176bc4.png

לוחצים על אחת מרשומות השגיאה Traceback, ולאחר מכן לוחצים על הסבר על ערך היומן הזה.

d6af045cf03008bc.png

אפשר לקרוא את הסבר לשגיאה.

בשלב הבא צריך להיעזר ב-Duet AI כדי לפתור את השגיאה.

אפשר לנסות להזין את ההנחיה הבאה.

הנחיה 1: עזרה בפתרון השגיאה הזו

9288dd6045369167.png

מזינים את הודעת השגיאה שמופיעה בהנחיה.

הנחיה 2: אסור: נראה שלחשבון משתמש מאומת ב-IAM אין הרשאה לשלוח בקשת API. אימות של Cloud SQL Admin API מופעלת בפרויקט GCP ובחשבון לקוח Cloud SQL. הוקצה לחשבון המשתמש ב-IAM

f1e64fbdc435d31c.png

ואז.

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

bb8926b995a8875c.png

מקצים את התפקיד 'לקוח Cloud SQL' ל-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 הזה למדנו את הדברים הבאים:

  1. צריך להפעיל את Duet AI בפרויקט ב-GCP ולהגדיר אותו לשימוש בסביבת פיתוח משולבת (IDE) ובמסוף Cloud.
  2. שימוש ב-Duet AI ליצירת קוד, להשלמה ולהסבר.
  3. אפשר להשתמש ב-Duet AI כדי להסביר בעיה באפליקציה ולפתור אותה.
  4. תכונות של 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"]