Duet AI のデベロッパー向け技術ハンズオン ワークショップ ガイド Codelab

1. 目標

このワークショップの目的は、ユーザーと実務担当者に Duet AI の実践的なトレーニングを提供することです。

この Codelab では、次のことを学習します。

  1. GCP プロジェクトで Duet AI を有効にし、IDE と Cloud コンソールで使用できるように構成します。
  2. コードの生成、補完、説明に Duet AI を使用します。
  3. Duet AI を使用してアプリケーションの問題を説明し、トラブルシューティングを行う。
  4. IDE チャットとマルチターン チャット、チャットとインライン コード生成、コード説明や列挙確認などのスマート アクションなどの Duet AI 機能。

ナラティブ

Duet AI for Developers が日常的な開発で実際にどのように使用されているかを示すために、このワークショップのアクティビティはナラティブの文脈で行われます。

新しいデベロッパーが e コマース会社に入社しました。このチームは、既存の e コマース アプリケーション(複数のサービスで構成される)に新しいサービスを追加する役割を担っています。新しいサービスでは、商品カタログ内の商品に関する追加情報(寸法、重量など)が提供されます。このサービスでは、商品の寸法と重量に基づいた、より有利な、またはより安価な送料が可能になります。

開発者は新入社員なので、コードの生成、説明、文書化に 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 を有効にしています

API(gcloud または Terraform などの IaC ツール)または Cloud コンソール UI を使用して、GCP プロジェクトで Duet AI を有効化できます。

Google Cloud プロジェクトで Duet AI を有効にするには、Cloud AI Companion API を有効にして、Cloud AI Companion ユーザーと Service Usage 閲覧者の Identity and Access Management(IAM)ロールを付与します。

gcloud 経由

Cloud Shell をアクティブにします。

PROJECT_IDUSER を構成し、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 ユーザーと Service Usage 閲覧者の Identity and Access Management(IAM)ロールを付与します。Cloud Companion API は、使用する IDE とコンソールの両方の機能の背後に存在します。サービスの使用状況閲覧者の権限は、コンソールで UI を有効にする前のクイック チェックに使用されます(Duet UI は、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 を有効にするには、Google Cloud コンソールの [Cloud AI Companion API] ページに移動します。

プロジェクト セレクタでプロジェクトを選択します。

[有効にする] をクリックします。

ページが更新され、ステータスが [有効] になります。これで、必要な IAM ロールを持つすべてのユーザーが、選択した Google Cloud プロジェクトで Duet AI を使用できるようになりました。

Duet AI の使用に必要な IAM ロールを付与するには、[IAM] ページに移動します。

[プリンシパル] 列で、Duet AI へのアクセスを有効にするユーザーを見つけ、その行の鉛筆アイコン ✏️ [プリンシパルを編集] をクリックします。

[アクセス権の編集] ペインで、[別のロールを追加] をクリックします。

[ロールを選択] で、[Cloud AI Companion ユーザー] を選択します。

[別のロールを追加] をクリックし、[Service Usage 閲覧者] を選択します。

[保存] をクリックします。

IDE の設定

デベロッパーは、さまざまな IDE の中からニーズに最適なものを選択できます。Duet AI コード アシスタンスは、Visual Studio CodeJetBrains IDE(IntelliJ、PyCharm、GoLand、WebStorm など)、Cloud WorkstationsCloud Shell エディタなどの複数の IDE で利用できます。

このラボでは、Cloud Workstations または Cloud Shell エディタを使用できます。

このワークショップでは、Cloud Shell エディタを使用します。

Cloud Workstations の設定には 20 ~ 30 分かかることがあります。

すぐに使用するには、Cloud Shell エディタを使用します。

Cloud Shell エディタを開くには、Cloud Shell の上部にあるメニューバーで鉛筆アイコン ✏️ をクリックします。

Cloud Shell エディタの UI と UX は VSCode と非常によく似ています。

d6a6565f83576063.png

Ctrl(Windows)/ CMD(Mac)+,(カンマ)をクリックして [設定] ペインに移動します。

検索バーに「duet ai」と入力します。

[Cloudcode] › [Duet AI: 有効にする][Cloudcode] › [Duet AI] › [インライン候補: 自動を有効にする] を確認します。

111b8d587330ec74.png

下部のステータスバーで [Cloud Code - Sign In] をクリックし、ログイン ワークフローに沿って対応します。

すでにログインしている場合は、ステータスバーに Cloud Code - プロジェクトなしと表示されます。

[Cloud Code - No project] をクリックすると、アクション プルダウン ペインが上部に表示されます。[Google Cloud プロジェクトを選択] をクリックします。

3241a59811e3c84a.png

プロジェクト ID の入力を開始すると、リストにプロジェクトが表示されます。

c5358fc837588fe.png

プロジェクトのリストから PROJECT_ID を選択します。

下部のステータスバーが更新され、プロジェクト ID が表示されます。表示されない場合は、Cloud Shell エディタのタブを更新する必要があります。

左側のメニューバーで Duet AI アイコン d97fc4e7b594c3af.png をクリックすると、Duet AI チャット ウィンドウが表示されます。[Select GCP Project] というメッセージが表示されたら、プロジェクトをクリックして再度選択します。

Duet AI のチャット ウィンドウが表示されます。

781f888360229ca6.png

3. インフラストラクチャの設定

d3234d237f00fdbb.png

GCP で新しい配送サービスを実行するには、次の GCP リソースが必要です。

  1. Cloud SQL インスタンスとデータベース。
  2. コンテナ化されたサービスを実行する GKE クラスタ。
  3. Docker イメージを保存する Artifact Registry。
  4. コード用の Cloud Source Repositories のリポジトリ。

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 サービスを開発する

9745ba5c70782e76.png

作成するサービスは、最終的に次のファイルで構成されます。今すぐこれらのファイルを作成する必要はありません。以下の手順に沿って 1 つずつ作成します。

  1. package-service.yaml - 高さ、幅、重量、特別な取り扱い指示などのデータを含むパッケージ サービスの Open API 仕様。
  2. data_model.py - package-service API 仕様のデータモデル。また、product_details DB に packages テーブルも作成します。
  3. connect_connector.py - Cloud SQL 接続(エンジン、セッション、ベース ORM を定義)
  4. db_init.py - packages テーブルにサンプルデータを生成します。
  5. main.py - product_id に基づいて packages データからパッケージの詳細を取得するための GET エンドポイントを持つ Python Flask サービス。
  6. test.py - 単体テスト
  7. requirement.txt - Python の要件
  8. Dockerfile - このアプリケーションをコンテナ化するため

演習中に厄介な問題に遭遇した場合、最終的なファイルはすべて参考用に、この Codelab の付録にあります。

前のステップでは、Cloud Source Repositories のリポジトリを作成しました。リポジトリのクローンを作成します。クローンを作成したリポジトリ フォルダにアプリケーション ファイルをビルドします。

Cloud Shell ターミナルで次のコマンドを実行して、リポジトリのクローンを作成します。

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

Cloud Shell エディタの左側のメニューから Duet AI チャット サイドバーを開きます。アイコンは 8b135a000b259175.png のようになります。Duet AI をコード アシスタンスに使用できるようになりました。

package-service.yaml

ファイルを開かずに、配送サービスの Open API 仕様を生成するよう Duet に指示します。

プロンプト 1: 数値の商品 ID で配送情報とパッケージ情報を提供するサービスの OpenAPI yaml 仕様を生成する。サービスには、荷物の高さ、幅、奥行き、重量、特別な取り扱い上の指示などの情報を含める必要があります。

ba12626f491a1204.png

生成されたコード ウィンドウの右上に、3 つのオプションが表示されています。

コードをCOPY 71194556d8061dae.pngファイルに貼り付けるか、

ADD df645de8c65607a.png を使用して、エディタで現在開いているファイルにコードを移動できます。

または、コードを新しいファイルで OPEN a4c7ed6d845df343.png することもできます。

新しいファイルで OPEN a4c7ed6d845df343.png コードをクリックします。

CTRL/CMD + s をクリックしてファイルを保存し、アプリケーション フォルダに package-service.yaml というファイル名でファイルを保存します。[OK] をクリックします。

f6ebd5b836949366.png

最後のファイルは、この Codelab の付録セクションにあります。存在していない場合は、手動で適切な変更を行います。

さまざまなプロンプトを試して、Duet AI の回答を確認することもできます。

Duet AI のサイドバーの上部にあるゴミ箱アイコン f574ca2c1e114856.png をクリックして、Duet AI のチャット履歴をリセットします。

data_model.py

次に、OpenAPI 仕様に基づいて、サービスのデータモデルの Python ファイルを作成します。

package-service.yaml ファイルを開いた状態で、次のプロンプトを入力します。

プロンプト 1: Python sqlalchemy ORM を使用して、この API サービスのデータモデルを生成します。また、個別の関数と、データベース テーブルを作成するメイン エントリ ポイントを含めます。

b873a6a28bd28ca1.png

生成されたパーツを 1 つずつ見ていきましょう。Duet AI は依然としてアシスタントであり、コードを迅速に作成するのに役立ちますが、生成されたコンテンツを確認しながら理解する必要があります。

まず、種類BasePackage という Class があります。このクラスは、次のように 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 関数を実行して実際に Cloud SQL データベースにテーブルを構築するメイン関数が必要です。

if __name__ == '__main__':
    from sqlalchemy import create_engine

    engine = create_engine('sqlite:///shipping.db')
    create_tables(engine)

    print('Tables created successfully.')

なお、main 関数は、ローカルの sqlite データベースを使用してエンジンを作成します。Cloud SQL を使用するには、パスワードを変更する必要があります。これは少し後で行います。

以前と同様に、新しいファイル ワークフローで OPEN a4c7ed6d845df343.png を使用してコードを実行します。コードを data_model.py というファイルに保存します(名前にはダッシュではなくアンダースコアを使用できます)。

Duet AI のサイドバーの上部にあるゴミ箱アイコン f574ca2c1e114856.png をクリックして、Duet AI のチャット履歴をリセットします。

connect-connector.py

Cloud SQL コネクタを作成します。

data_model.py ファイルを開いた状態で、次のプロンプトを入力します。

プロンプト 1: cloud-sql-python-connector ライブラリを使用して、Postgres の Cloud SQL インスタンスの接続プールを初期化する関数を生成します。

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 ファイルを開いた状態で、アプリケーションで使用する DB enginesessionmakerbase ORM を生成します。

プロンプト 1: connect_with_connector メソッドを使用してエンジン、sessionmaker クラス、ベース ORM を作成する

6e4214b72ab13a63.png

レスポンスで、engineSessionBaseconnect_connector.py ファイルに追加されます。

最後のファイルは、この Codelab の付録セクションにあります。存在していない場合は、手動で適切な変更を行います。

さまざまなプロンプトを試して、Duet AI のレスポンスのバリエーションの可能性を確認することもできます。

Duet AI のサイドバーの上部にあるゴミ箱アイコン f574ca2c1e114856.png をクリックして、Duet AI のチャット履歴をリセットします。

data_model.py の更新

Cloud SQL データベースにテーブルを作成するには、前のステップで作成したエンジン(connect_connector.py ファイル内)を使用する必要があります。

Duet AI のチャット履歴を削除します。data_model.py ファイルを開きます。次のプロンプトを試します。

プロンプト 1: main 関数で、connect_connector.py からエンジンをインポートして使用する

2e768c9b6c523b9a.png

connect_connector(Cloud SQL の場合)から engine がインポートされるレスポンスが表示されます。create_table は(デフォルトの sqlite ローカル DB ではなく)このエンジンを使用します。

data_model.py ファイルを更新します。

最後のファイルは、この Codelab の付録セクションにあります。存在していない場合は、手動で適切な変更を行います。

さまざまなプロンプトを試して、Duet AI のさまざまな回答を確認することもできます。

Duet AI のサイドバーの上部にあるゴミ箱アイコン f574ca2c1e114856.png をクリックして、Duet AI のチャット履歴をリセットします。

requirements.txt

アプリケーションの requirements.txt ファイルを作成します。

connect_connector.pydata_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 のチャット履歴をリセットします。

Cloud SQL でのパッケージ テーブルの作成

Cloud SQL データベース コネクタの環境変数を設定します。

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 個作成し、パッケージ テーブルに commit する関数を生成する

プロンプト 2: connect_connector のセッションを使用してサンプル パッケージ行を 10 個作成し、パッケージ テーブルに commit する関数を生成する。

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.

Cloud SQL インスタンスに再度接続し、サンプルデータが packages テーブルに追加されることを確認します。

Cloud SQL インスタンスに接続し、データベースが作成されたことを確認します。

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

パスワード(evolution)を入力したら、package テーブルからすべてのデータを取得します。

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.pypackage-service.yamlconnect_connector.py の各ファイルを開いた状態で、アプリケーションの main.py を作成します。

プロンプト 1: Python Flask ライブラリを使用する - このサービスに HTTP REST エンドポイントを使用する実装を作成する

プロンプト 2: Python Flask ライブラリを使用する - このサービスに HTTP REST エンドポイントを使用する実装を作成します。connect_conector.py から SessionMaker をインポートしてパッケージ データに使用できます。

プロンプト 3: Python Flask ライブラリを使用する - このサービスに HTTP REST エンドポイントを使用する実装を作成します。data_model.py のパッケージと connect_conector.py の SessionMaker のパッケージをインポートしてパッケージのデータに使用する。

プロンプト 4: Python Flask ライブラリを使用する - このサービスに HTTP REST エンドポイントを使用する実装を作成します。data_model.py のパッケージと、connect_conector.py の SessionMaker のパッケージをインポートしてパッケージのデータに使用します。app.run にはホスト IP 0.0.0.0 を使用します。

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

2 つ目のターミナルで、/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}

サンプルデータで他の商品 ID をテストすることもできます。

CTRL_C」と入力して、ターミナルで実行中の Docker コンテナを終了します。

単体テストの生成

main.py ファイルを開いた状態で、単体テストを生成します。

プロンプト 1: 単体テストを生成する。

e861e5b63e1b2657.png

以前と同様に、新しいファイル ワークフローで OPEN a4c7ed6d845df343.png を使用してコードを実行します。test.py というファイルにコードを保存します。

test_get_package 関数では、product_id を定義する必要があります。手動で追加できます。

最後のファイルは、この Codelab の付録セクションにあります。存在していない場合は、手動で適切な変更を行います。

Duet AI のサイドバーの上部にあるゴミ箱アイコン f574ca2c1e114856.png をクリックして、Duet AI のチャット履歴をリセットします。

単体テストの実施

単体テストを実行します。

python test.py

出力は次のようになります。

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

OK

Cloud Shell エディタですべてのファイルを閉じて、上部のステータスバーにあるゴミ箱アイコン 1ecccfe10d6c540.png をクリックしてチャット履歴を消去します。

Dockerfile

このアプリケーションの Dockerfile を作成します。

main.py を開き、次のプロンプトを試します。

プロンプト 1: このアプリケーションの Dockerfile を生成する。

プロンプト 2: このアプリケーションの Dockerfile を生成する。すべてのファイルをコンテナにコピーする。

9c473caea437a5c3.png

また、INSTANCE_CONNECTION_NAMEDB_USERDB_PASSDB_NAMEENVARS も設定する必要があります。これは手動で行うことができます。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 a4c7ed6d845df343.png を使用してコードを実行します。Dockerfile というファイルにコードを保存します。

最後のファイルは、この Codelab の付録セクションにあります。存在していない場合は、手動で適切な変更を行います。

アプリケーションをローカルで実行する

Dockerfile を開いた状態で、次のプロンプトを試します。

プロンプト 1: この Dockerfile を使用してコンテナをローカルで実行する方法

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

2 つ目のターミナル ウィンドウから、コンテナにアクセスします。

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 に push します。

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

Pod が実行され、Service に外部ロードバランサの 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. 追加の実習: アプリケーションのトラブルシューティング

cloudsqlsa サービス アカウントから Cloud SQL クライアント IAM ロールを削除します。これにより、Cloud SQL データベースへの接続でエラーが発生します。

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] > [ワークロード

d225b1916c829167.png

shipping Deployment をクリックし、[ログ] タブをクリックします。

1d0459141483d6a7.png

ステータスバーの右側にある [ログ エクスプローラで表示 ] df8b9d19a9fe4c73.png アイコンをクリックします。新しい [ログ エクスプローラ] ウィンドウが開きます。

e86d1c265e176bc4.png

いずれかの Traceback エラーエントリをクリックし、[このログエントリの説明] をクリックします。

d6af045cf03008bc.png

エラーの説明を確認できます。

次に、Duet AI にエラーのトラブルシューティングを手伝ってもらいましょう。

次のプロンプトを試します。

プロンプト 1: このエラーのトラブルシューティングをサポートしてほしい

9288dd6045369167.png

プロンプトにエラー メッセージを入力します。

プロンプト 2: Forbidden: 認証された IAM プリンシパルに API リクエストを行う権限がないようです。「Cloud SQL Admin API」を確認する「Cloud SQL クライアント」内で有効にしているIAM プリンシパルにロールを付与しました

f1e64fbdc435d31c.png

そして、

プロンプト 3: gcloud を使用して Cloud SQL クライアント ロールを Google サービス アカウントに割り当てるにはどうすればよいですか?

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}

Cloud Loggingログ エクスプローラログの説明機能で Duet AI を正常に使用し、問題のトラブルシューティングを行いました。

8. おわりに

これで、この Codelab は終了です。

この Codelab では次のことを学びました。

  1. GCP プロジェクトで Duet AI を有効にし、IDE と Cloud コンソールで使用できるように構成します。
  2. コードの生成、補完、説明に Duet AI を使用します。
  3. Duet AI を使用してアプリケーションの問題を説明し、トラブルシューティングを行う。
  4. IDE チャットとマルチターン チャット、チャットとインライン コード生成、コード説明や列挙確認などのスマート アクションなどの Duet AI 機能。

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

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"]