gpt-4oAPIを利用してアーキテクチャ図からCFNテンプレートを生成してみる

# はじめに
株式会社TechoesインフラチームのTです！
今回は、5/13にリリースされたばかりのgpt-4oを使用してAWSのCFNテンプレートを生成する手順を解説していこうと思います。
# 実行環境
- Windows: 11 Home
- Python: 3.11.4
- モデル: gpt-4o
- openai: 1.30.1

# Chatgpt APIとは
OpenAI社が提供しているChatGPTをPython等のプログラムから呼び出すためのインターフェースです。
Pythonなどのプログラミング言語からAPIを呼び出すことでアプリやサービスにテキストを生成する機能や理解する機能を実装することが可能になります。

### 料金について
ChatGPT APIは処理するテキスト量によって料金が決まる**従量課金制**となっています。このテキスト量を計測する単位が**トークン**です。
トークンは入力トークンと出力トークンの2種類があり「1000トークンあたり、いくら」という料金設定になっています。具体的な費用は使用するGPTモデルによっても変化します。
- 入力トークン: APIに送るテキスト量
- 出力トークン: APIから送られてくるテキスト量 

[1000トークンあたりのコスト]
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3492991/22798fdb-f3e7-feda-ae16-883862d124bd.png)

# 事前準備
※Open AIアカウントは発行済みとします。
※請求情報は設定済みとします。
※Python動作環境は構築済みとします。

### API Keyの発行
1. Open AIにログインしたら左のメニューからAPI Keysを選択します。
2. Create new secret Keyを選択してKey Nameを指定したらsecret keyを発行します。
3. 発行後、key情報が表示されるのでコピーして控えておく。
:::note warn
注意
セキュリティ上の理由で一度key表示の画面を閉じると再度確認ができません。
:::

[Key 作成画面]
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3492991/1f83cd83-36c6-efd5-c4aa-a9822f8688f7.png)

### openai ライブラリのインストール
pythonからAPIを呼び出すためにopenaiライブラリが必要になるのでインストールします。
~~~
ps C:\Users> pip install openai
~~~
~~~
ps C:\Users> pip list
openai                    1.30.1
~~~

### テストコードで動作確認
:::note alert
警告
API Keyを実際のシステムで使用する際は、セキュリティの観点から環境変数に格納してください。
:::

~~~python:gpt_api_test.py
from openai import OpenAI

client = OpenAI(
    api_key = "xxxxx", #ここに先ほど取得したAPIキーを入力
)
chat_completion = client.chat.completions.create(
    messages = [
        {
            "role": "user",
            "content": "hello!",
        }
    ],
    model = "gpt-4o",
)

print(chat_completion)
~~~


#### 結果
![スクリーンショット 2024-05-22 171915.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3492991/ed9df42a-fed8-a097-8708-daa24a4673d8.png)
返答だけでなくトークン使用量も出力してくれました。

# 本題
動作環境も整ったので実際にAWSアーキテクチャ図を作成してCFNテンプレート化していただきましょう！
### 流れ
- gpt-4oへの画像入力はURLまたはbase64形式を使用するので、アップロード処理の前に画像をbase64形式でエンコードします。

- 命令文を日本語で記述すると英語に比べて消費トークン数が上昇するので英語での問い合わせをする。
 
- 処理結果はcfnテンプレートになるのでyaml形式で出力する。
### アーキテクチャ図
![chatgpt api test.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3492991/79f5b6bd-6c8d-14a0-ee9c-b03fe8572d0e.png)


### Pythonコード
~~~python:generate_template.py
import base64
from openai import OpenAI

IMAGE_PATH = r"xxx.png" #画像ファイルのパス

client = OpenAI(
   api_key = "xxxxx",
)

def encode_image(image_path):
    with open(image_path, "rb") as image_file:
        return base64.b64encode(image_file.read()).decode("utf-8")

base64_image = encode_image(IMAGE_PATH)

response = client.chat.completions.create(
    model = "gpt-4o",
    messages = [
        {"role": "user", "content": [
            {"type": "text", "text": "Please refer to the attached image to output AWS CloudFormation template in yaml format"},
            {"type": "image_url", "image_url": {"url": f"data:image/png;base64,{base64_image}"}}
        ]}
    ],
    temperature = 0.0,
)

print(response.choices[0].message.content)
~~~
### 結果
~~~
Based on the provided architecture diagram, here is an AWS CloudFormation template in YAML format that sets up a VPC with public and private subnets, an EC2 instance, and an Application Load Balancer (ALB):

```yaml
AWSTemplateFormatVersion: '2010-09-09'
Description: AWS CloudFormation template to create a VPC with public and private subnets, an EC2 instance, and an Application Load Balancer.

Resources:
  VPC:
    Type: AWS::EC2::VPC
    Properties:
      CidrBlock: 172.16.0.0/16
      EnableDnsSupport: true
      EnableDnsHostnames: true
      Tags:
        - Key: Name
          Value: gpt_test_VPC

  InternetGateway:
    Type: AWS::EC2::InternetGateway
    Properties:
      Tags:
        - Key: Name
          Value: gpt_test_IGW

  AttachGateway:
    Type: AWS::EC2::VPCGatewayAttachment
    Properties:
      VpcId: !Ref VPC
      InternetGatewayId: !Ref InternetGateway

  PublicSubnet1a:
    Type: AWS::EC2::Subnet
    Properties:
      VpcId: !Ref VPC
      CidrBlock: 172.16.10.0/24
      AvailabilityZone: ap-northeast-1a
      MapPublicIpOnLaunch: true
      Tags:
        - Key: Name
          Value: Public-1a

  PrivateSubnet1a:
    Type: AWS::EC2::Subnet
    Properties:
      VpcId: !Ref VPC
      CidrBlock: 172.16.11.0/24
      AvailabilityZone: ap-northeast-1a
      Tags:
        - Key: Name
          Value: Private-1a

  PublicSubnet1c:
    Type: AWS::EC2::Subnet
    Properties:
      VpcId: !Ref VPC
      CidrBlock: 172.16.20.0/24
      AvailabilityZone: ap-northeast-1c
      MapPublicIpOnLaunch: true
      Tags:
        - Key: Name
          Value: Public-1c

  PrivateSubnet1c:
    Type: AWS::EC2::Subnet
    Properties:
      VpcId: !Ref VPC
      CidrBlock: 172.16.21.0/24
      AvailabilityZone: ap-northeast-1c
      Tags:
        - Key: Name
          Value: Private-1c

  RouteTable:
    Type: AWS::EC2::RouteTable
    Properties:
      VpcId: !Ref VPC
      Tags:
        - Key: Name
          Value: gpt_test_RouteTable

  PublicRoute:
    Type: AWS::EC2::Route
    Properties:
      RouteTableId: !Ref RouteTable
      DestinationCidrBlock: 0.0.0.0/0
      GatewayId: !Ref InternetGateway

  SubnetRouteTableAssociation1a:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      SubnetId: !Ref PublicSubnet1a
      RouteTableId: !Ref RouteTable

  SubnetRouteTableAssociation1c:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      SubnetId: !Ref PublicSubnet1c
      RouteTableId: !Ref RouteTable

  SecurityGroup:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupDescription: Allow HTTP and SSH
      VpcId: !Ref VPC
      SecurityGroupIngress:
        - IpProtocol: tcp
          FromPort: 22
          ToPort: 22
          CidrIp: 0.0.0.0/0
        - IpProtocol: tcp
          FromPort: 80
          ToPort: 80
          CidrIp: 0.0.0.0/0
      Tags:
        - Key: Name
          Value: gpt_test_SG

  Instance:
    Type: AWS::EC2::Instance
    Properties:
      InstanceType: t2.micro
      KeyName: your-key-pair
      SubnetId: !Ref PublicSubnet1a
      SecurityGroupIds:
        - !Ref SecurityGroup
      ImageId: ami-0c55b159cbfafe1f0 # Replace with a valid AMI ID for your region
      Tags:
        - Key: Name
          Value: test_instance

  ALB:
    Type: AWS::ElasticLoadBalancingV2::LoadBalancer
    Properties:
      Name: test_alb
      Subnets:
        - !Ref PublicSubnet1a
        - !Ref PublicSubnet1c
      SecurityGroups:
        - !Ref SecurityGroup
      Scheme: internet-facing
      LoadBalancerAttributes:
        - Key: idle_timeout.timeout_seconds
          Value: '60'
      Tags:
        - Key: Name
          Value: test_alb

  ALBListener:
    Type: AWS::ElasticLoadBalancingV2::Listener
    Properties:
      DefaultActions:
        - Type: forward
          TargetGroupArn: !Ref ALBTargetGroup
      LoadBalancerArn: !Ref ALB
      Port: 80
      Protocol: HTTP

  ALBTargetGroup:
    Type: AWS::ElasticLoadBalancingV2::TargetGroup
    Properties:
      Name: test_alb_target_group
      Port: 80
      Protocol: HTTP
      VpcId: !Ref VPC
      TargetType: instance
      Targets:
        - Id: !Ref Instance
      HealthCheckProtocol: HTTP
      HealthCheckPort: '80'
      HealthCheckPath: /
      Matcher:
        HttpCode: 200
      Tags:
        - Key: Name
          Value: test_alb_target_group
```

This template creates the following resources:
- A VPC with CIDR block `172.16.0.0/16`.
- An Internet Gateway attached to the VPC.
- Two public subnets (`172.16.10.0/24` in `ap-northeast-1a` and `172.16.20.0/24` in `ap-northeast-1c`).
- Two private subnets (`172.16.11.0/24` in `ap-northeast-1a` and `172.16.21.0/24` in `ap-northeast-1c`).
- A route table with a default route to the Internet Gateway.
- Security group allowing SSH and HTTP access.
- An EC2 instance in the public subnet `ap-northeast-1a`.
- An Application Load Balancer (ALB) spanning the two public subnets.
- A listener for the ALB on port 80.
- A target group for the ALB with the EC2 instance as the target.

Make sure to replace `your-key-pair` with your actual key pair name and `ami-0c55b159cbfafe1f0` with a valid AMI ID for your region.
~~~

- 生成時間は20秒ほど
- 総消費トークン : 2321 
- 入力トークン : 787
- 出力トークン : 1534 
- アーキテクチャ図内のcidrや名称も認識してテンプレート内に反映されている。
- 画像内に存在していない必要なリソースまで生成している。
- 作成したリソース一覧の出力をしてる。
- ユーザーが作成、確認するべきリソースをピックアップしている。

# 所感
検証前は画像にあるものだけを生成して、大枠だけ作ってくれるくらいの気持ちでいたので正直驚きました。また、今回の検証では超低コストで検証できたのも嬉しいポイントです。ただ、もっと複雑なインフラ構成を生成できるのか、生成できたとしても複雑な分生成するテンプレートファイルも長文になるのでどのくらいのコストで生成できるのかが気になるところです。

# 参考資料
https://openai.com/api/pricing/

https://platform.openai.com/docs/api-reference

https://chatgpt-lab.com/n/nea590e8f3e87

https://qiita.com/NaruNeko/items/0dc2317cc28193162661