Taste of Tech Topics

Acroquest Technology株式会社のエンジニアが書く技術ブログ

Amazon CodeWhispererでどの程度コーディングが効率化できそうか試してみた

ここのところ気温も暖かくなり、外に出かけるのが楽しみになってきた、カメラ好き機械学習エンジニアの@yktm31です。

いま世間を賑わせている生成系AI、ChatGPTは私にとって欠かせないものになりました。
そんな中つい先日、AWSから「Amazon CodeWhisperer」がGAになりました。

といことで、さっそく試してみました。

目次

概要

Amazon CodeWhispererは、リアルタイムでAIによるコード提案をしてくれるサービスです。類似のサービスとしては「GitHub Copilot」があります。

以下のように、コードを書いている途中でもその先を予測し、候補を出してくれるというものです。

aws.amazon.com

特徴

CodeWhispererは、他のコード生成AIに比べ以下のような特徴があるようです。

  • AWSサービスに最適化されたコード提案
  • AWSサービスを利用するためのライブラリや、ベストプラクティスに則ったコードスニペットの提案など、書いたコメントやコードから提案される内容が、AWSサービス利用のために最適化されているようです。
  • セキュリティスキャン
  • この機能の対象はJavaJavaScriptPythonになります。
    OWASP Top 10にあたるセキュリティ脆弱性のあるコードや、ベストプラクティスに倣っていないコードなどを検出し、修正案も出してくれるようです。
  • ライセンス検知
  • AIが提案したコードが、どのようなライセンスのオープンソースを参照して生成されたものか、チェックできるようです。
    オープンソースのコードをそのライセンスのフラグとともに学習しているため、ライセンス違反していないかチェックすることができるようです。
  • SSOサポート
  • エンタープライズ向けに、SSO統合がサポートされているようです。

    サポート

    サポートされるプログラミング言語

    Amazon CodeWhispererで生成可能なプログラミング言語は15種類あるようです。

    その中でも、JavaPythonJavaScript、TypeScript、C#の5つが、学習データの質が高い(=より精度の高い提案をしてくれると想定できる)ようです。

    サポートされるIDE

    以下のIDEがサポートされているようです。

    • Visual Studio Code
    • すべてのJetBrains IDE
      • IDEによってAmazon CodeWhispererのふるまいが変わることはないようです
    • AWS Cloud9
    • AWS Lambda コンソールエディタ

    docs.aws.amazon.com

    サポートされる自然言語

    コード生成のためのコメントは、現時点では、英語以外は公式にはサポートしていないようです。 ただ、学習データに英語以外の言語が含まれているため、日本語を利用した場合でも、コードの候補が出てくることもあるようです。

    使い方

    利用開始方法

    AWSのサイトに動画で各IDEでの利用方法が説明されています。 VSCodeでの利用を試してみましたが、インストールは非常に簡単でした。

    VS Code拡張機能AWS Toolkit」をインストールします

    ②サイドパネルからAWS Toolkitのアイコンを選択し、CodeWhispererを開始します

    ③認証を求められるので認証方法を選び認証を通します

    AWS LambdaコンソールやAWS Cloud9で利用する際は、codewhisperer:GenerateRecommendationsのIAMポリシーをIAM userかroleにアタッチする必要があるようです。

    aws.amazon.com

    基本操作

    VS Codeでは、以下のような操作で行います。

    アクション キー操作
    コードを提案させる Alt + C
    別の候補を出す 矢印キー(←、→)
    提案されたコードを採用する Tabキー

    もちろん、コーディング中に自動で続きのコードの提案も出てくるのですが、 Alt + Cで明示的に提案させることができる点が、使い勝手がよいと感じました。

    VS Code拡張機能IntelliJ系のキーマップにするものを入れていると、上記の操作が上手くいかないことがあるので注意です。

    Lambdaで、DynamoDBのレコードを取得する処理と、そのユニットテストを書いてみた

    実際どんなコードを生成してくれるのか、まずは基本的な処理として、DynamoDBのレコードを取得するようなコードを書いてみました。
    言語はPythonを利用し、pytestとmotoを利用したユニットテストも生成してみました。

    結論として、100%そのまま動くわけではありませんでしたが、手直しは1~2割程度で、8割のコードは自動生成されたものが使えました。

    コメントを書いて「Ctrl+C」で提案されたコードを使い、手直しする際も、最初の数文字入力してから、「Ctrl+C」をすれば、続きの実装が提案され非常に楽にできました。 生産性は十分向上するレベルだと感じました。

    DynamoDBへのアクセスを担うRepositoryクラスの実装をしている様子を撮影してみました。

    以下に実装したコードの全体像を載せます。 CodeWhispererを使って、手直しつつ、5分かからずに書くことができました。 モックのDynamoDBテーブル作成など、正直面倒な部分も、さくっと一発作成されるので、かなり体験がよかったです。

    リポジトリクラスとLambda handler

    import boto3
    
    # Write a DynamodbRepository class for accessing DynamoDB.
    # Use boto3.client('dynamodb') to create a client.
    # get_item/put_item methods and error handling are required.
    
    class DynamodbRepository:
        def __init__(self, table_name):
            self.client = boto3.client('dynamodb', region_name='us-east-1')
            self.table_name = table_name
    
        def get_item(self, key):
            try:
                response = self.client.get_item(TableName=self.table_name, Key=key)
                return response['Item']
            except Exception as e:
                print(e)
                return None
    
        def put_item(self, item):
            try:
                response = self.client.put_item(TableName=self.table_name, Item=item)
                return response
            except Exception as e:
                print(e)
                return None
        
        # Write delete_item method here.
        def delete_item(self, key):
            try:
                response = self.client.delete_item(TableName=self.table_name, Key=key)
                return response
            except Exception as e:
                print(e)
                return None
    
    
    # Write a handler for get user item from DynamoDB using DynamodbRepository.
    # Trable name is 'users' and key is 'user_id'
    
    def get_user_item(event, context):
        table_name = 'users'
        dynamodb_repository = DynamodbRepository(table_name)
    
        key = {'user_id': {'S': event['pathParameters']['user_id']}}
        user_item = dynamodb_repository.get_item(key)
        if user_item is None:
            return {
                'statusCode': 404,
                'body': 'User not found'
            }
        else:
            return {
                'statusCode': 200,
                'body': user_item
            }
    

    テストコード

    import boto3
    from handler import DynamodbRepository, get_user_item
    
    from moto import mock_dynamodb
    
    # write a test for DynamodbRepository.get_item()
    # Table item schema is {user_id: str, name: str, age: int}
    
    @mock_dynamodb
    def test_get_item():
        client = boto3.client("dynamodb", region_name="us-east-1")
        client.create_table(
            TableName="users",
            KeySchema=[
                {"AttributeName": "user_id", "KeyType": "HASH"},
            ],
            AttributeDefinitions=[
                {"AttributeName": "user_id", "AttributeType": "S"},
            ],
            ProvisionedThroughput={"ReadCapacityUnits": 5, "WriteCapacityUnits": 5},
        )
    
        # put item to table using boto3 client
        client.put_item(
            TableName="users",
            Item={
                "user_id": {"S": "1"},
                "name": {"S": "John"},
                "age": {"N": "20"}, 
            }
        )
    
        repo = DynamodbRepository('users')
        item = repo.get_item( key={'user_id': {'S': '1'}})
        
        # assert
        assert item["user_id"]['S'] == "1"
        assert item["name"]['S'] == "John"
        assert item["age"]['N'] == "20"
    
    
    # write test code for get_user_item handler using pytest. using @mock_dynamodb and prepare data.
    
    @mock_dynamodb
    def test_get_user_item():
        client = boto3.client("dynamodb", region_name="us-east-1")
        client.create_table(
            TableName="users",
            KeySchema=[
                {"AttributeName": "user_id", "KeyType": "HASH"},
            ],
            AttributeDefinitions=[
                {"AttributeName": "user_id", "AttributeType": "S"},
            ],
            ProvisionedThroughput={"ReadCapacityUnits": 5, "WriteCapacityUnits": 5},
        )
    
        # put item to table using boto3 client
        client.put_item(
            TableName="users",
            Item={
                "user_id": {"S": "1"},
                "name": {"S": "John"},
                "age": {"N": "20"}, 
            }
        )
    
        # prepare event
        event = {
            "pathParameters": {
                "user_id": "1"
            }
        }
    
        # call handler
        response = get_user_item(event, None)
    
        # assert
        assert response["statusCode"] == 200
    

    実装として、もう少し丁寧に作りたい部分はありつつも、十分助けになるサポートになると感じました。 より使いこなすには、コメントを詳細に、より具体的するなど、指示の出し方のスキルが大事になりそうです。

    なお、DynamoDB操作に関しては、直近非推奨になってしまったboto3.resourceを使った実装が提案されることもありました。 これは、学習段階ではboto3.resourceが利用されたコードがあったのだと想像します。

    boto3.resourceに関するドキュメントは以下です。
    https://boto3.amazonaws.com/v1/documentation/api/latest/guide/resources.html

    コード参照(Code references)を試してみる

    CodeWhispererでコードを生成していると、VS Codeであれば、「CODEWHISPERER REFERENCE LOG」の部分に 生成したコードがどんなオープンソースのコードを参照したか教えてくれます。

    今回は、「Apache-2.0 license」のオープンソースのコードを参照しているようなので、問題なさそうです。

    この機能はデフォルトでONになっており、オプトアウト可能なものです。

    CodeWhispererを利用した結果、意図せずライセンス違反をする、なんてことを防げる便利な機能だと思いました。

    docs.aws.amazon.com

    セキュリティスキャンを試してみる

    試しに、セキュリティキーをハードコーディングしてみると、ちゃんと警告を出してくれます。 ただし、セキュリティスキャンは、明示的に「Run Security Scan」を実行する必要があります。(画像赤枠部分)

    次に、ベストプラクティスに沿っていない書き方をしてみます。

    AWSリソースを関数実行のたびに作成するのは非効率だと、警告が出ています。

    セキュリティスキャン機能を使うことで問題に気づいて対処すれば、問題も防げ、レビュイーの負担も減って嬉しいですね。

    使い方については、AWSYoutubeチャンネルには、PythonでDDDベースのサーバレスアプリをAmazon CodeWhispererで作るデモもあります。 この動画では、より大規模に開発をしているので、参考になりそうです。

    www.youtube.com

    また、どんな使い方があるのかも、ドキュメントで例が載っていました。 こちらも参考にできそうです。 docs.aws.amazon.com

    ドキュメントからわかったこと

    最後に、ドキュメント等を一通り読んでわかったことです。 実際に業務で利用する上で、確認しておきたいプランの違いやセキュリティについて、特に重要だと感じた部分をまとめてみました。

    安全性・セキュリティ

    この手のサービスで気になるのは、セキュリティや安全性だと思います。 ドキュメントやFAQsのページで説明されている内容で、特に重要と思った項目を挙げてみたいと思います。

    CloudTrail適用
    • Amazon CloudTrailを使用し、CodeWhispererに関連するアクティビティを記録することができます。
    • これにより、AWSインフラのコンプライアンス、セキュリティ、運用監査を確保できます。
    TLSサポート
    • CodeWhispererでは、TLSを使用してIDEAWSサービス間のデータを暗号化し、コードやデータのセキュリティと機密性を確保しています。
    データ収集
    • Professionalプランでは、CodeWhispererは、コードの提案を提供するためのコードスニペット、コメント、カーソル位置、IDEで開いているファイルの内容などのコンテンツ収集はしていないようです。
    • クライアント側のメトリクスやテレメトリーなどは、Individual・Professionalの両方でデータがAWSに送信されるようになっているようです。
    • メトリクス・テレメトリーは、オプトアウト可能です。オプトアウト方法は、後ほど触れたいと思います。
    著作権
    • Amazon CodeWhisperer のFAQsページでは、はっきりと、CodeWhispererで生成したコードを含め、開発者本人が所有することが明記されています。
    • FAQsの原文を引用すると、こう表現されています。「Just like with your IDE, you own the code that you write, including any code suggestions provided by CodeWhisperer. 」
    • 生成AIで作成したコードは誰が所有するのか?という問題は、しばしば議論される領域で、他のサービスではコードの所有が誰にあるのか明言していないものもあります。CodeWhispererがここで「you own the code」と明言しているのは、業務で利用するという点で、一つハードルをクリアしやすくなっていると思いました。
    収集されたコードの漏洩
    • Professionalでは、「No. Content processed by CodeWhisperer Professional, such as code snippets, comments, and contents from files open in the IDE, is not stored or used to train the model, and therefore will never be reproduced in a code suggestion for another user.」と書かれており、はっきりと否定されています。
    • Individualでは、「We have safeguards designed to prevent reproduction of unique private code collected from CodeWhisperer Individual users.」という説明がされています。
    • このことから、コード漏洩のリスクとしては、Professionalプランを利用する方が安全であると思われます。

    ProfessionalとIndividualの違い

    ユースケース
    • Professional利用は、組織や企業向けに設計されています。ソフトウェア開発プロジェクトに取り組むチームのニーズに対応する追加機能と構成を提供します。
    • Individual利用は、CodeWhispererの機能から恩恵を受けたいが、高度な機能や組織レベルの構成を必要としない個人の開発者や小規模なチーム向けに設計されています。
    データの収集と使用
    • Professionalでは、コードスニペット、コメント、IDEで開いているファイルの内容など、CodeWhispererが処理したコンテンツは保存されず、サービス向上のために使用されていないようです。
    • Individualでは、CodeWhispererは、コードスニペット、コメント、IDEで開いているファイルからのコンテンツなど、ユーザのコンテンツを保存し、サービスの品質を改善し開発するために使用されるようです。
    メトリクスの送信
    • ProfessionalとIndividualの両方で、サービス改善のためにクライアントサイドのメトリクスを収集し、使用されるようです。IDEの設定でオプトアウトすることができるようです。
    ユーザ管理
    • Professionalでは、AWS管理者はAWSマネジメントコンソールから組織レベルで設定を一元的に構成することができるようです。組織レベルで、特定のポリシーや設定を強制することができるようです。
    • Individualでは、ユーザーはIDE内で設定を調整することができますが、中央管理機能はありません。

    料金と制限

    IndividualとProfessionalの、料金・制限の面での違いをまとめました。

    機能 Individual Professional
    料金 無料 $19/ユーザー/月
    認証 AWS Builder ID AWS IAM Identity Center
    コード生成言語 全てのサポート言語 全てのサポート言語
    コード参照 (Code references) あり あり
    コードセキュリティスキャン $50/ユーザー/月 $500/ユーザー/月
    組織ライセンス管理 なし あり
    組織ポリシー管理 なし あり

    オプトアウト方法

    CodeWhisperer利用時にAWSに送信されるデータのオプトアウト方法についてみてみます。

    先に触れたように、Individualプランではコメント、IDEで開いているファイルの内容などのコンテンツがAWSに送信されます。 IDEのメトリクスやテレメトリーは、IndividualでもProfessionalでもAWSに送信されます。

    それぞれのプラン、IDEでのオプトアウト方法は、以下のドキュメントに記載がありました。

    docs.aws.amazon.com

    まとめ

    Amazon CodeWhispererを使用してみて、使い勝手の良さが非常によかったです。 特に、AWSサービスを利用する実装は、他のAIコード生成サービスよりも、CodeWhispererに軍配が上がる感触を持ちました。 セキュリティスキャン、ライセンス検知などの機能も便利で、コード品質向上のために実際的に役に立つと感じました。

    また、セキュリティ・安全性の観点から、業務での利用はProfessionalを利用することが望ましいように思います。 組織の要件に応じて、オプトアウトすることも忘れずに適用しておきたいところです。

    今回はPythonでLambda関数を書く例でしたが、CodeWhispererを活用すれば、CDKの実装やFargateで動かすAPIサーバーなんかも、楽につくれるようになるのではと思います。

    生成AI、万歳!
    それでは

    Acroquest Technologyでは、キャリア採用を行っています。
    • ディープラーニング等を使った自然言語/画像/音声/動画解析の研究開発
    • Elasticsearch等を使ったデータ収集/分析/可視化
    • マイクロサービス、DevOps、最新のOSSクラウドサービスを利用する開発プロジェクト
    • 書籍・雑誌等の執筆や、社内外での技術の発信・共有によるエンジニアとしての成長
      少しでも上記に興味を持たれた方は、是非以下のページをご覧ください。 www.wantedly.com