👋

Amazon CodeWhispererのユースケースを探る(探り中)

2023/09/07に公開

最近「CodeWhipererって実際どうなんですかねえ」と質問されることが増えまして。

8月は、先行する競合サービスがある中で、CodeWhispererの使いどころってどこにあるんだろう・・?とずっと考えておりました。

ドキュメントを読んだり、実際使ってみたり、AWSの中の人のお話を伺ったり。
ウダウダしておりましたところ、9月になってしまいました。

そして、builders.flashにも、SAさんたちがモブプロっぽくCodeWhispererを試された、とても参考になる記事も掲載されました。読みましょう。

https://aws.amazon.com/jp/builders-flash/202309/try-codewhisperer-100knocks/

CodeWhispererの良いところ

ドキュメントを熟読し、実際に利用してみて、「CodeWhispererここはイケてるかも」と思った点を列挙します。

無料で使える

CodeWhispererには、Individual tierという個人向けのプランが用意されており、
AWS Builder IDさえ登録すれば無料で利用することができます。

有料サブスクリプションであるProfessional tierと比較すると、チーム開発におけるセキュリティ対策やガバナンス強化の機能に差はあります。

https://docs.aws.amazon.com/codewhisperer/latest/userguide/billing.html

が、コーディング作業の支援を行う以下の機能はtier共通であり、それほどの差はないと感じています。

  • VS Code、JetBrainsサポート
  • CodeWhispererがサポートするすべてのプログラミング言語のコード生成に対応
  • 開発したコードや、TelemetryデータのAWSへの提供をopt out可
  • Reference Trackerにより、生成されたコードの学習ソースを追跡可能
  • コードに対するセキュリティスキャンの実行(Individualは50スキャン/month、Professionalは500スキャン/month)

また、AWSユーザーであれば、Cloud9、Lambdaコンソール、SageMaker Studio、Glue Studioにおいて、CodeWhisperer自体の課金は発生しません。ただし、セキュリティスキャンは使えません。

Refernce Trackerにより、生成コードの元になったデータを追跡できる

リファレンストラッカーを有効化することで、CodeWhispererが生成したコードにGithub等のOSSから取得したコードが含まれていた場合、

  • リポジトリに設定されているライセンス種別
  • リポジトリへのURLリンク
  • 該当コードと行数

を確認できます。以下のようなイメージです。

[8/23/2023, 9:47:36 AM] Accepted recommendation with code
const cluster = new Cluster(this, "Cluster", { vpc }); const taskDefinition = new FargateTaskDefinition(this, "TaskDefinition");
provided with reference under ISC from repository aws-ecs-fargate-services-using-cdk. Added to /home/ec2-user/environment/cdk-ts/lib/cdk-ts-stack.ts (lines from 22 to 24).

これは、うまく利用すれば、開発時に意図せずライセンス違反を犯すリスクを減らす効果があると思います。

加えて、生成されたコードで文法エラーが発生した場合、リポジトリへのURLリンクを辿ることで、Suggestionの元データとなったGithub等で公開されているソースコードを確認できます。
これにより、Suggestionで利用された部分的なコードの前後のコンテクストも確認できますので、エラーの原因調査にも役立ちます。
(そもそも利用しているパッケージが全然違った、とか)

生成されたコードのセキュリティスキャンができる

コードのセキュリティスキャンは、IDEのプラグインとして提供されているものがたくさんあります。
なので決定的な優位性とは言えませんが、CodeWhipererでセキュリティスキャン⇒検出されたセキュリティ脆弱性の修正方法のSuggestionを受ける⇒修正、という作業をシームレスに進めることができます。
ただし、以下を読む限り対象言語はPython、Java、JavaScriptだけのようですね。

Therefore, CodeWhisperer comes with a built-in code scanning feature that detects security vulnerabilities within your Python, Java, and JavaScript projects, including code suggestions from CodeWhisperer and code written by you.
https://aws.amazon.com/codewhisperer/faqs/?nc1=h_ls

CodeWhispererに一番期待しているところ

やはりこれですよね。

AWS のサービスで使用するために最適化済み
CodeWhisperer は、AWS API 向けに最適化されたコードの提案を提供することで、Amazon Elastic Compute Cloud (Amazon EC2)、AWS Lambda、Amazon Simple Storage Service (Amazon S3) などの AWS のサービスをデベロッパーがより効率的に使用できるようにします。IDE でコードを記述すると、CodeWhisperer はコードとコメントを自動的に分析します。必要な機能を実現するために、関連するクラウドサービスや公開されているソフトウェアライブラリを使用して提案を行い、次に AWS のベストプラクティスを満たすコードスニペットを推奨します。https://aws.amazon.com/jp/codewhisperer/features/

まず、CodeWhispererのサービス名の頭には、"Amazon"と書かれています。
以前AWSの中の人に伺ったのですが、「Amazon.comで使われている(いた)サービスやツールが元になったAWSサービス」の頭には"Amazon"が付く、らしいです。(伝聞)

上記の製品特徴を読む限り、CodeWhispererの利用しているモデルの学習には「AWSやAmazon社しか持ちえないデータ」が利用されていると推測してしまうので、
S3やSQS、ECSなんかを構築・利用するIaCコードや、AWS CLIを使ったシェル、SageMaker StudioでコーディングするPythonコードなんかは、キレッキレのSuggestionをしてほしいと期待してしまいます。

残念ながら、TerraformのHCLはCodeWhispererのサポート対象外です。
また、JSON、YAMLファイル上でのSuggestionも利用できませんので、CloudFormationのテンプレートを生成することはできません。

https://docs.aws.amazon.com/codewhisperer/latest/userguide/language-ide-support.html

が、TypeScriptやPython等のメジャーなプログラミング言語はサポートされていますので、CDKのコードを書き、間接的にCloudFormationテンプレートを生成することはできます。

Cloud9で、CodeWhispererを活用してCDKのコードを生成する

では、やってみましょう。

なぜVS CodeではなくCloud9かというと、Cloud9が好きだからです。
(もしかしてCloud9使ってる人って少数派・・?)

真面目な話、CodeWhispererを使うべき理由の候補として、「マネジメントコンソールの中でAI支援を受けたコーディングをし、デプロイや動作確認まで含めてすべて完結する」という体験が有力ではないか、と考えているからです。

Cloud9でCodeWhispererを利用する準備

マネジメントコンソールにログインするIAM Userに、以下の権限が必要です。

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "CodeWhispererPermissions",
      "Effect": "Allow",
      "Action": ["codewhisperer:GenerateRecommendations"],
      "Resource": "*"
    }
  ]
}

ドキュメントによると、加えてCodeWhispererの有効化が必要、と書かれていますが、設定した記憶がない・・・。一応確認しましょう。

CDKを利用する準備

Cloud9には、AWS CLI、Node.js、AWS CDK Toolkit、すべてインストール済みです。(古いが・・後述)

今回検証した環境の情報です。
リージョンは東京リージョンを利用しています。

CodeWhispererのAPI自体はヴァージニア北部(us-east-1)リージョンでホストされているようなので、Suggestionの遅延が気になる方は、us-east-1を利用されると若干マシかもしれません。

$ cat /etc/os-release 
NAME="Amazon Linux"
VERSION="2"
ID="amzn"
ID_LIKE="centos rhel fedora"
VERSION_ID="2"
PRETTY_NAME="Amazon Linux 2"
ANSI_COLOR="0;33"
CPE_NAME="cpe:2.3:o:amazon:amazon_linux:2"
HOME_URL="https://amazonlinux.com/"
$ aws --version\naws-cli/2.13.12 Python/3.11.4 Linux/4.14.322-244.536.amzn2.x86_64 exe/x86_64.amzn.2 prompt/off
$ node -v
v16.20.2
$ cdk --version
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!!                                                                                                                  !!
!!  Node 16 is approaching end-of-life and will no longer be supported in new releases after 2023-09-11.            !!
!!  Please upgrade to a supported node version as soon as possible.                                                 !!
!!                                                                                                                  !!
!!  This software is currently running on node v16.20.2.                                                            !!
!!  As of the current release of this software, supported node releases are:                                        !!
!!  - ^20.0.0 (Planned end-of-life: 2026-04-30)                                                                     !!
!!  - ^18.0.0 (Planned end-of-life: 2025-04-30)                                                                     !!
!!  - ^16.3.0 (Planned end-of-life: 2023-09-11) [DEPRECATED]                                                        !!
!!                                                                                                                  !!
!!  This warning can be silenced by setting the JSII_SILENCE_WARNING_DEPRECATED_NODE_VERSION environment variable.  !!
!!                                                                                                                  !!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
2.93.0 (build 724bd01)

CDKはじめてという方も、AWS CDK Workshopを参照しながら作業すると安心です。

Cloud9のコマンドラインで以下を実行していき、TypeScript CDK Projectを作成していきます。

mkdir cdk-workshop && cd cdk-workshop
cdk init sample-app --language typescript

これで、コーディングの準備はできました。
編集する対象のファイルは、lib/cdk-workshop-stack.tsです。

プロンプトエンジニアリング

AI支援型コード生成ツールの場合、対話型のインターフェースを持っているわけではないので、ChatGPTのように凝ったプロンプトエンジニアリングはできなさそうです(研究中)。

CodeWhipererのWorkshop studioのPython編の中に、「Prompt Engineering」という項目があります。

https://catalog.us-east-1.prod.workshops.aws/workshops/6838a1a5-4516-4153-90ce-ac49ca8e1357/en-US/10-python/03-02-prompts

上記を読む限り、CodeWhispererでは、

  • コメントとしてコンテキストを書いて、コードを生成する(いわゆるInput/Output Prompting?一般名はあるのかしら?)
  • テストデータのようなサンプルデータをインプットとして数行記入し、続けて類似のコードを生成する(いわゆるFew-Shot Prompting?)

などが試せそうです。

今回は、コードの冒頭でマルチラインコメントとして生成したいコードの内容を記載し、CodeWhispererだけでどこまでコード生成できるか試してみましょう。

マルチラインコメントを英語で記入してコード生成

cdk initで生成されたlib/cdk-workshop-stack.tsを開きます。
サンプルコードとして以下のコードが記載されていますが、全部消します。

cdk-workshop-stack.ts
import { Duration, Stack, StackProps } from 'aws-cdk-lib';
import * as sns from 'aws-cdk-lib/aws-sns';
import * as subs from 'aws-cdk-lib/aws-sns-subscriptions';
import * as sqs from 'aws-cdk-lib/aws-sqs';
import { Construct } from 'constructs';

export class CdkTsStack extends Stack {
  constructor(scope: Construct, id: string, props?: StackProps) {
    super(scope, id, props);

    const queue = new sqs.Queue(this, 'CdkTsQueue', {
      visibilityTimeout: Duration.seconds(300)
    });

    const topic = new sns.Topic(this, 'CdkTsTopic');

    topic.addSubscription(new subs.SqsSubscription(queue));
  }
}

その上で、以下のようにマルチラインコメントとして要件を記入してみます。

cdk-workshop-stack.ts
/* 
Run containerized application on Fargate.
Container image 'amazon/amazon-ecs-sample' will be used.
Publish the application behind public application load balancer.
*/

この状態で、MacならOption+C、WindowsならAlt+Cを押し、CodeWhispererを呼び出します。

Suggestがポップアップ表示されます。(水色〇のコード)

TabキーまたはEnterを押し、Suggestを受け入れるとコードが挿入されます。

多少カーソル位置の調整などはしましたが、ひたすらAlt+C⇒Enterを押し続けると、なんだかそれっぽいコードが生成されました。

cdk-workshop-stack.ts
/* 
Run containerized application on Fargate.
Container image 'amazon/amazon-ecs-sample' will be used.
Publish the application behind public application load balancer.
*/
import { Vpc } from "aws-cdk-lib/aws-ec2";
import { Cluster, ContainerImage } from "aws-cdk-lib/aws-ecs";
import { ApplicationLoadBalancedFargateService } from "aws-cdk-lib/aws-ecs-patterns";
import { Construct } from "constructs";

export class CdkTsStack extends Construct {
  constructor(scope: Construct, id: string, props?: any) {
    super(scope, id);

    const vpc = new Vpc(this, "Vpc");
    const cluster = new Cluster(this, "Cluster", { vpc });

    new ApplicationLoadBalancedFargateService(this, "FargateService", {
      cluster,
      taskImageOptions: {
        image: ContainerImage.fromRegistry("amazon/amazon-ecs-sample"),
      },
    });
  }
}

生成されたコード、それっぽいのですが、
CdkTsStackクラスがcdk.Stackクラス継承してないので、Stack作れずデプロイでエラーになります。
併せて、今回Constructを作成したわけではないので、constructorの引数も変更し、import文も更新します。

結果こうなりました。

cdk-workshop-stack.ts
/* 
Run containerized application on Fargate.
Container image 'amazon/amazon-ecs-sample' will be used.
Publish the application behind public application load balancer.
*/
+ import * as cdk from "aws-cdk-lib";
import { Vpc } from "aws-cdk-lib/aws-ec2";
import { Cluster, ContainerImage } from "aws-cdk-lib/aws-ecs";
import { ApplicationLoadBalancedFargateService } from "aws-cdk-lib/aws-ecs-patterns";
import * as cdk from "aws-cdk-lib";
- import { Construct } from "constructs";

+ export class CdkTsStack extends cdk.Stack {
- export class CdkTsStack extends Construct {
+  constructor(scope: cdk.App, id: string, props?: cdk.StackProps) {
-  constructor(scope: Construct, id: string, props?: any) {
    super(scope, id, props);

    const vpc = new Vpc(this, "VPC");
    const cluster = new Cluster(this, "Cluster", { vpc });

    new ApplicationLoadBalancedFargateService(this, "FargateService", {
      cluster,
      taskImageOptions: {
        image: ContainerImage.fromRegistry("amazon/amazon-ecs-sample"),
      },
    });
  }
}

これでデプロイ通ります。
はじめてCDK実行される方はcdk bootstrap、その後にcdk deployします。

CdkTsStack: deploying... [1/1]
CdkTsStack: creating CloudFormation changeset...

 ✅  CdkTsStack

✨  Deployment time: 240.3s

Outputs:
(省略)
Stack ARN:
arn:aws:cloudformation:ap-northeast-1:000000000000:stack/CdkTsStack/6a38e960-4be6-11ee-ab43-0aac76c8c3c5

✨  Total time: 269.06s

うーん、そこそこ大事なとこ手直ししてる気がしますが、次行ってみましょう。

マルチラインコメントを日本語で記入してコード生成

一旦cdk destroyして環境を削除した後、lib/cdk-workshop-stack.tsを書き変えます。
今度は、さきほど英語で記入したマルチラインコメントを、日本語に書き換えてみます。

cdk-workshop-stack.ts
/*
Fargate上でコンテナ化されたアプリケーションを実行する。
コンテナイメージ「amazon/amazon-ecs-sample」を使用する。
パブリックアプリケーションロードバランサーの後ろにアプリケーションを公開する。
*/

この状態から、さきほどと同じ要領で、ひたすらAlt+C⇒Enterを押し続け、CodeWhispererのSuggestを適用していきます。

そうすると以下のようになりました。コードが変わりましたね。

cdk-workshop-stack.ts
/*
Fargate上でコンテナ化されたアプリケーションを実行する。
コンテナイメージ「amazon/amazon-ecs-sample」を使用する。
パブリックアプリケーションロードバランサーの後ろにアプリケーションを公開する。
*/
import { Stack, StackProps, CfnOutput } from "aws-cdk-lib";
import { Construct } from "constructs";
import {
  Cluster,
  ContainerImage,
  FargateTaskDefinition,
  FargateService,
  Protocol,
} from "aws-cdk-lib/aws-ecs";
import { Vpc } from "aws-cdk-lib/aws-ec2";

export class CdkTsStack extends Stack {
  constructor(scope: Construct, id: string, props?: StackProps) {
    super(scope, id, props);

    const vpc = new Vpc(this, "Vpc");
    const cluster = new Cluster(this, "Cluster", { vpc });

    const taskDefinition = new FargateTaskDefinition(this, "TaskDefinition");
    taskDefinition.addContainer("AppContainer", {
      image: ContainerImage.fromRegistry("amazon/amazon-ecs-sample"),
      memoryLimitMiB: 512,
    });

    const service = new FargateService(this, "Service", {
      cluster,
      taskDefinition,
    });

    service.connections.allowFromAnyIpv4(Protocol.TCP, "Open to the world");

    new CfnOutput(this, "LoadBalancerDNS", {
      value: service.loadBalancer.loadBalancerDnsName,
    });
  }

今回はエラーも出ています。

そして、噂の?Reference Trackerも動きました!
引用します。

[9/6/2023, 12:39:41 PM] Accepted recommendation with code
const cluster = new Cluster(this, "Cluster", { vpc }); const taskDefinition = new FargateTaskDefinition(this, "TaskDefinition");
provided with reference under ISC from repository aws-ecs-fargate-services-using-cdk. Added to /home/ec2-user/environment/cdk-ts/lib/cdk-ts-stack.ts (lines from 22 to 24).
And with code
const service = new FargateService(this, "Service", { cluster, taskDefinition, });
provided with reference under MIT,MIT-0 from repository cdk-chart-app-sample. Added to /home/ec2-user/environment/cdk-ts/lib/cdk-ts-stack.ts (lines from 30 to 33).
And with code
new CfnOutput(this, "LoadBalancerDNS", { value: service.loadBalancer.loadBalancerDnsName, });
provided with reference under Apache-2.0 from repository ctfd-cdk-example. Added to /home/ec2-user/environment/cdk-ts/lib/cdk-ts-stack.ts (lines from 40 to 42).

どうもGithubのコードをコピーしてSuggestされた箇所がエラーになっているようなので、デバッグしていきましょう。

デバッグその1
service.connections.allowFromAnyIpv4(Protocol.TCP, "Open to the world");

こちら、Cloud9が「Protocol.TCPはおかしくって、Port型じゃないとダメよ」と教えてくれています。

CodeWhispererさんに修正してもらいましょう。

惜しい。
ポート番号は3000番ではなく80番ですが、FargateServiceクラスのAPI Referenceを見る手間は省けます。

あと、Portクラスを利用するにはaws-cdk-lib » aws_ec2 » Portのimportが必要なので、併せて修正します。

+ import { Vpc, Port } from "aws-cdk-lib/aws-ec2";
- import { Vpc } from "aws-cdk-lib/aws-ec2";
デバッグその2 ⇒ ここで力尽きた
new CfnOutput(this, "LoadBalancerDNS", {
      value: service.loadBalancer.loadBalancerDnsName,
});

Cloud9は、「FargateServiceに"LoadBalancer"なんてプロパティないよ」と言っています。
というか、そもそもコード中でALB作ってないですね。
ALB作ってないのに、エンドポイントURLだけアウトプットしようとしてるっていう。

こういう時は、Reference Trackerのログを見てみましょう。
該当コードのログです。

And with code
new CfnOutput(this, "LoadBalancerDNS", { value: service.loadBalancer.loadBalancerDnsName, });
provided with reference under Apache-2.0 from repository ctfd-cdk-example. Added to /home/ec2-user/environment/cdk-ts/lib/cdk-ts-stack.ts (lines from 40 to 42).

Cloud9上で閲覧すると、参照されているリポジトリ名がリンクになっています。
参照されたリポジトリはこちらのようです。

https://github.com/aleksil/ctfd-cdk-example

リポジトリ中でCodeWhispererのモデルによって使われたと思われるコードは、おそらくこれです。

https://github.com/aleksil/ctfd-cdk-example/blob/master/src/stacks/ctfd-stack.ts

なーるーほーどー。

ctfd-stack.ts
import * as ecspatterns from "aws-cdk-lib/aws-ecs-patterns";

学習元のコードではaws-ecs-patternsコンストラクト使ってるので、
service.loadBalancer.loadBalancerDnsNameでエンドポイント取得できたわけですね。

CDKの場合「ALBを作る」という要件に対してL2、L3で複数手段があるので、
今回のようなSuggestがなされることがあるのかもしれません。

ただ、「ALBがほしい」という意図は日本語のコメントでもCodeWhispererさんに伝わったようなので、ここで満足しました。

まとめ

「キレッキレ」とまではいかないですが、CodeWhispererと対話しながらコーディングするのは楽しかったです。

モデルの精度やUI/UXに関する改善要望はありますが・・・マネジメントコンソールと統合されているので、Cloud9やLambdaコンソールでちょっとしたコードを試しに書いてみる、というケースでは便利かと思います。
GitHub Copilotとの比較は別途やってみようかと。

とりあえずAWSは推しクラウドですので、個人でCodeWisperer利用する場合は"Share Code Whisperer Content With AWS"をオンにして、AWSさんに情報提供していきたいと思います。

Discussion