Vscodeとgpt-4oAPIを連携してみる

# はじめに
株式会社TechoesインフラチームのTです！
今回は、前回に引き続きChatGPTAPIを利用した記事を投稿したいと思います。
前回の記事は下記にリンクを貼っておくので合わせて読んでいただく幸いです。
[gpt-4oAPIを利用してアーキテクチャ図からCFNテンプレートを生成してみる](https://qiita.com/y-tomaru-techoes/items/4988aaae1527a033faa2)

:::note warn
注意
ChatGPT APIの料金、APIKeyの発行については前回の記事で解説済みのため今回の記事では省略します。
また、生成される内容は必ずしも正しいとは限らないのであくまで補完ツールとして利用しましょう。
:::
# 経緯
自分は業務上でコードレビューを目的としてChatGPTを使用することがあります。
しかし、使用するたびにブラウザからChatGPTのページにアクセスして質問するのが少々手間だと感じていました。そこで、前回書いた記事でAPIKeyを発行したのでvscodeとの連携ができないかと思い今回の記事を書くことにしました。

# 実行環境
- Windows: 11 Home
- モデル: gpt-4o
- Genie AI v0.0.11
- Visual Studio Code v1.92
- openai: 1.30.1
- Python: 3.11.4

# Genie AI とは
Genie AIとはVisual Studio CodeからChatGPT APIにアクセスできる拡張機能です。
コードのデバッグや最適化などに特化しているため、ブラウザ版のChatGPTよりも精度が高いようです。
[公式ページ](https://marketplace.visualstudio.com/items?itemName=genieai.chatgpt-vscode)
ちなみに、Genie本人に「あなたについて教えて」と質問するとこのような回答が返ってきます。
~~~text:text
こんにちは！私は **Genie** です。コードに関する質問や問題解決をお手伝いするためのプロフェッショナルな開発アシスタントです。得意分野は以下の通りです：

- **プログラミング言語:**
  - Python
  - JavaScript
  - Java
  - C++
  - その他多数

- **フレームワークとライブラリ:**
  - React
  - Angular
  - Django
  - Flask
  - Spring Boot

- **データベース:**
  - MySQL
  - PostgreSQL
  - MongoDB
  - SQLite

- **その他の技術:**
  - Git/GitHub
  - Docker
  - Kubernetes
  - AWS, Azure, GCP

### よくあるサポート内容

1. **コードデバッグ:** バグの特定と修正。
2. **コード最適化:** パフォーマンス向上のためのアドバイス。
3. **技術的な質問への回答:** 新しい技術の使い方やベストプラクティスの提供。
4. **プロジェクト設計:** アーキテクチャの提案や設計支援。

どんな質問でもお気軽にどうぞ！あなたのお手伝いができることを楽しみにしています。
~~~
# 環境準備
### Genie AIのインストール
vscodeの拡張機能から検索をして画像のように選択してインストールを実施します。
![スクリーンショット 2024-08-02 005858.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3492991/778b10d4-3d94-9d3b-df58-4ea991d8a525.png)

インストールが完了するとサイドバーにGenieのCONVERSATION VIEWが出てくるので何か質問をしてみましょう。
するとAPI Keyの設定を求められるので入力しましょう。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3492991/02f9ae9b-62ad-6767-dc26-396f8b85b6bf.png)
設定が完了すると回答が返ってくることを確認できます。
![スクリーンショット 2024-08-02 010435.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3492991/4000ca02-bd7c-9d97-96ae-5e39399d1b89.png)

### Genie AIの日本語化

VSCodeの設定の検索に下記を入力
`@ext:genieai.chatgpt-vscode genieai.`
Genieの設定画面に移動するので`Genieai > Prompt Prefix:***`の値を下記のように設定します。
| 設定 | 値 |
|:-:|:-:|
| Prompt Prefix:Add Tests | このコードのテストを実装してください。 |
| Prompt Prefix:Find Problems | このコードのバグを見つけてください。 |
| Prompt Prefix:Optimize | このコードを最適化してください。 |
| Prompt Prefix:Explain | このコードを日本語で説明をしてください。  |
| Prompt Prefix:Add Comments | このコードに日本語コメントを追加してください。|
| Prompt Prefix:Complete Code | このコードを完成させてください。 |

この設定をすることにより、入力する文言の先頭に設定した値が付け加えられるので日本語の回答を出力させることができます。

# 実際Genieに使用してみる
今回は`Find Problems` `Optimize` `Explain`の機能を使用してみます。
使用するコードは前回の記事で作成した`generate_template.py`を例にします。

:::note 
生成されたテキストに修正後コードの全体が出力されますが省略しています。
:::
~~~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)
~~~
### Find Problems
いざ実行と思った矢先、設定名と機能名が一致していなかったので注意です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3492991/656d6bad-e231-8fd2-36e6-a22612d9258e.png)
#### 結果
~~~text:text
このPythonコードにはいくつかのバグがあります。以下にそれらを指摘し、修正方法を示します。

### バグの指摘と修正

1. **`OpenAI`クラスのインポート**:
    `openai`パッケージをインポートしていますが、使用するクラスやメソッドが違います。

   ```python
   from openai import OpenAI  # 修正前
   import openai  # 修正後
   ```

2. **`client.chat.completions.create`部分**:
    OpenAI APIの呼び出しに誤りがあります。messagesフィールドの構造が間違っています。また、モデル名も不正確です。正しいフォーマットは以下の通りです。

   ```python
   response = openai.ChatCompletion.create(
       model="gpt-4",
       messages=[
           {"role": "user", "content": "Please refer to the attached image to output AWS CloudFormation template in yaml format"},
           {"role": "user", "content": f"data:image/png;base64,{base64_image}"}
       ],
       temperature=0.0,
   )
   ```

3. **APIレスポンスの取り出し方**:
    応答からメッセージ内容を取得する部分も修正が必要です。

   ```python
   print(response['choices'][0]['message']['content'])
   ```

### 変更点のまとめ

1. `OpenAI`クラスではなく、`openai`モジュール全体をインポート。
2. `openai.ChatCompletion.create`を正しく利用。
3. `messages`リストの構造を修正。
4. 応答からメッセージ内容を正しく取り出す。

これでコードは正常に動作し、指定された画像をエンコードしてAPIリクエストを送信し、その応答を表示するようになります。
~~~

### Optimize
#### 結果
~~~text:text
このコードを最適化する際、以下のポイントに注意して修正します。

1. **`openai`モジュールから直接インポートし、APIキーの設定も単純化**。
2. **メッセージフォーマットを正しく修正**（Content内でリストを使用せず、シンプルな文字列形式に変更）。
3. **変数名やコメントを明確にして、可読性を向上させる**。
4. **不要な複雑さを排除し、簡素化する**。

### 修正と最適化ポイントの解説

1. **`openai` モジュールを直接インポートし、APIキーを設定**:
   ```python
   import openai
   openai.api_key = "xxxxx"
   ```
   これにより、不必要なクライアントオブジェクトの生成を避けられます。

2. **画像エンコード関数のドキュメンテーション**:
   ```python
   def encode_image(image_path):
       """Encode an image to a base64 string."""
       with open(image_path, "rb") as image_file:
           return base64.b64encode(image_file.read()).decode("utf-8")
   ```
   関数に簡単な説明を加えました。

3. **メッセージのフォーマットの修正**:
   ```python
   messages=[
       {"role": "user", "content": "Please refer to the attached image to output AWS CloudFormation template in yaml format"},
       {"role": "user", "content": f"data:image/png;base64,{base64_image}"}
   ]
   ```
   正しい形式で内容を記述しています。ここではメッセージ内容が単一の文字列となるように調整しました。

4. **JSONレスポンスのアクセス方法の修正**:
   ```python
   print(response['choices'][0]['message']['content'])
   ```
   辞書形式のデータへのアクセスを明確にしました。

この最適化により、コードがより直感的かつ効率的になりました。
~~~
### Explain
#### 結果
~~~text:text
このPythonコードは、指定された画像ファイルをBase64形式にエンコードし、そのエンコード結果をOpenAIのAPIを使用して特定の要求と共に送信し、応答を取得するという一連の操作を行います。以下に各部分の説明を示します。

### コードの説明

1. **ライブラリのインポート**
    ```python
    import base64
    from openai import OpenAI
    ```
    - `base64`: 画像ファイルをBase64エンコードするためのモジュール。
    - `OpenAI`: OpenAIのAPIを操作するためのクライアントライブラリ。

2. **定数の定義**
    ```python
    IMAGE_PATH = r"xxx.png"  # 画像ファイルのパス
    ```
    - 変数`IMAGE_PATH`に、エンコード対象の画像ファイルのパスを指定します。

3. **OpenAIクライアントの設定**
    ```python
    client = OpenAI(
       api_key = "xxxxx",
    )
    ```
    - OpenAIのAPIキーを使用してクライアントオブジェクトを作成します。

4. **画像のエンコード用関数の定義**
    ```python
    def encode_image(image_path):
        with open(image_path, "rb") as image_file:
            return base64.b64encode(image_file.read()).decode("utf-8")
    ```
    - `encode_image`関数は、指定した画像ファイルを開き、その内容をBase64でエンコードし、文字列として返します。

5. **画像のエンコード**
    ```python
    base64_image = encode_image(IMAGE_PATH)
    ```
    - 指定されたパスの画像ファイルをエンコードし、その結果を`base64_image`変数に格納します。

6. **APIリクエストの作成と送信**
    ```python
    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,
    )
    ```
    - OpenAIのチャットモデル（ここでは`gpt-4o`）に対してメッセージを送信します。メッセージにはテキストとエンコードされた画像のURLが含まれています。
    - `temperature`パラメータは出力の多様性を制御します。この場合は0.0に設定されているため、最も確実な応答を選択します。

7. **応答の表示**
    ```python
    print(response.choices[0].message.content)
    ```
    - APIの応答から最初の選択肢のメッセージ内容を取り出して表示します。

### 全体の流れ

1. 画像ファイルのパスを指定。
2. OpenAIのクライアントオブジェクトを生成。
3. 画像をBase64形式にエンコード。
4. エンコードされた画像データを含むメッセージを作成し、APIに送信。
5. 応答を受け取って表示。

このプロセスを通じて、画像を基に適切なCloudFormationテンプレートを生成するための指示をAPIに与えています。
~~~
### 感想
`Find Problems` `Optimize`の結果からコードの変更点を確認すると説明を追加している以外は同じ出力になりました。今回のケースだけ見ると質問文が違うだけでやっていることは同じなのでほかのコードだとどうなるか気になります。

`Explain`に関しては前回の記事で説明しているよりも詳しく正確に説明されているので精度は高そう。

# 出力されたコードを実行してみる
実際に実行してみます。
~~~python:generate_template.py
import base64
import openai

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

# Set the API key directly in the openai module
openai.api_key = "xxxxx"

def encode_image(image_path):
    """Encode an image to a base64 string."""
    with open(image_path, "rb") as image_file:
        return base64.b64encode(image_file.read()).decode("utf-8")

base64_image = encode_image(IMAGE_PATH)

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

print(response['choices'][0]['message']['content'])
~~~

#### 結果
~~~
Traceback (most recent call last):
  File "*:\***\***\generate_template.py", line 16, in <module>
    response = openai.ChatCompletion.create(
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "*:\****\***\Python\Python311\site-packages\openai\lib\_old_api.py", line 39, in __call__
    raise APIRemovedInV1(symbol=self._symbol)
openai.lib._old_api.APIRemovedInV1:

You tried to access openai.ChatCompletion, but this is no longer supported in openai>=1.0.0 - see the README at https://github.com/openai/openai-python for the API.

You can run `openai migrate` to automatically upgrade your codebase to use the 1.0.0 interface.

Alternatively, you can pin your installation to the old version, e.g. `pip install openai==0.28`

A detailed migration guide is available here: https://github.com/openai/openai-python/discussions/742
~~~
エラーが出ました
原因としては`openai`ライブラリのバージョンが1.0.0以上だと`openai.ChatCompletion.create`メソッドが廃止されているようです。

# 所感
生成されたコードを実際に実行してみた結果から確かにコードレビューは楽になりました。でも他の部分でエラーが出ていることから冒頭で注意書きしたように`あくまで補完ツールとして利用する`ということが重要です。
また、今回Genie AIに実行環境を入力していなかったこともエラーの原因にもなるので人間が適切な命令文を作る能力も必要だと感じました。
今回の記事がTechoes社員の他に閲覧していただいた方の参考になれば幸いです。

# 参考資料
https://namileriblog.com/python/chatgpt-genie-ai

https://note.com/gorojy/n/n5e37bf9df9b0

https://qiita.com/yuk1_sys/items/82e14597c7d5be00ce0d