chatGPT apiをPython3で使用してみた

## はじめに

2023年03月08日にchatGPTのAPIがリリースされました。  
気になったのでPythonを使用し、リクエストを飛ばして遊んでみたいと思います。  

詳しくは[chatGPT公式ドキュメント](https://platform.openai.com/docs/api-reference/chat/create)を参考にしてください。

## ゴール
今回のゴールはchatGPTのAPIを使用して質問を投げることです。

## 環境
今回はDocker + ubuntu上で開発をしていますが、Pythonが動作すればどの環境でも問題ありません。

:::note info
本環境は前提としてwindows10にwsl2を用いてubuntu環境を構築しています。
:::
~~~
Docker Desktop for windows
Docker version 20.10.22
Docker Compose version v2.15.1
VS vode
Python version 3.11
Fastapi version latest
~~~

## 構成
ディレクトリ構成です。
~~~
chatGPT
    |── docker-compose.yml
    |── .env
    └── api
         |── app.py
         └── Dockerfile
~~~

## 準備
[open api](https://platform.openai.com/account/api-keys)サイトからAPIキーを発行します。
open api のアカウントが必要なためアカウントを所持していない方は登録してください。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3078307/a15b535e-777c-3c35-fa52-2c7f2b5fcc2a.png)

## docker-compose.ymlの作成
chatGPTディレクトリ直下に作成します。docker-composeコマンドを使用する際の設定ファイルです。
~~~yml:docker-compose.yml
version: '3'

services:
  api:
    build:
      context: ./api
      dockerfile: ./Dockerfile
    ports:
      - "8002:8002"
    restart: on-failure
    tty: true
    volumes:
      - ./api/:/app
    env_file:
      - ./.env

networks:
  default:
    name: chatGPT
    external: true
~~~

* env_file
APIキーを.envファイルに記述し、Compose内に環境変数を展開するための設定を行う。

## Dockerfileの作成
apiディレクトリ直下にdockerコンテナをビルドするDockerfileを作成します。
今回はchatGPTへリクエストを送信するだけなため、使用頻度の低いツールやライブラリを除外した最小パッケージであるslimを指定していますが、特に気にする必要はありません。
~~~Dockerfile:Dockerfile
FROM python:3.11-slim-bullseye
ENV PYTHONUNBUFFERED 1

RUN pip install --upgrade pip
RUN pip install fastapi uvicorn
RUN pip install requests 
RUN pip install openai
RUN pip install aiohttp

RUN mkdir /app
COPY ./ /app
WORKDIR /app

ENTRYPOINT ["uvicorn", "app:app", "--host", "0.0.0.0", "--port", "8002"]

EXPOSE 8002

~~~

## 環境変数の設定（.envファイル）
chatGPTディレクトリ直下に.envファイルを作成し、APIキーを配置するパスの環境変数を設定します。
~~~python:env
#open api key
OPENAI_API_KEY=先ほど発行したAPIキー
~~~

## Python実装
続いてPythonでの実装に移ります。といってもほぼ[open api公式サンプル](https://platform.openai.com/docs/api-reference/chat/create)と変化はありません。

chatGPT APIを使用するために必要なライブラリはos, openaiなのですが、今回はFastAPIを使用してJsonレスポンスを行うためfastapi系ライブラリもインポートしています。
~~~python:app.py
import os
import openai
from fastapi import (FastAPI, status)
from fastapi.responses import JSONResponse
from pydantic import BaseModel
~~~
<br>

**openai.api_key**でAPIキーを指定するため**os.getenv**で.envに記述したAPIキーを認識させます。

~~~python
openai.api_key = os.getenv("OPENAI_API_KEY")
~~~

chatGPTにリクエストを送信するためには**ChatCompletion**というオブジェクトを使用する必要があり、リクエストボディには**model**と**messages**は記述必須となっています。ほかにも複数引数が用意されているので気になる方は[公式ドキュメント](https://platform.openai.com/docs/api-reference/chat/create)を参照ください。

~~~python
completion = openai.ChatCompletion.create(
            model="gpt-3.5-turbo",
            messages=[body.dict()]
        )
~~~

実際には**messages**へ下記のようなjson形式のリクエストが入りますが今回はここをリクエストボディとしています。

~~~python:messages
messages=[{"role": "user", "content": "Hello!"}]
~~~

この**content**にchatGPTへ送るリクエスト内容を記述します。今回はHello!と入力しchatGPTからの回答を見てみましょう。
***
▼以下コード全体像▼

~~~python:app.py
app = FastAPI()
openai.api_key = os.getenv("OPENAI_API_KEY")

class request_body(BaseModel):
    role: str
    content: str

@app.post("/chatGPT")
async def chatGPT(body: request_body):
    
    try:
        completion = openai.ChatCompletion.create(
            model="gpt-3.5-turbo",
            messages=[body.dict()]
        )

        return JSONResponse(content=completion, status_code=status.HTTP_200_OK)
    except Exception as e:
        print(e)
        return JSONResponse(content={"error": "internal server error"}, status_code=status.HTTP_500_INTERNAL_SERVER_ERROR)
~~~

## いざ検証！！

curlコマンドから以下を実行します。
~~~
curl -X POST localhost:8002/chatGPT -H "Content-Type: application/json" -d '{"role": "user", "content": "Hello!"}'
~~~
レスポンスはこんな感じ
**content**の部分がchatGPTから質問に対しての回答である。正常に動作できていることを確認。
~~~json:response
{
    "id": "chatcmpl-6rmUmikIKgYUNXRcgSE54p4elhFVs",
    "object": "chat.completion",
    "created": 1678275820,
    "model": "gpt-3.5-turbo-0301",
    "usage": {
        "prompt_tokens": 9,
        "completion_tokens": 11,
        "total_tokens": 20
    },
    "choices": [
        {
            "message": {
                "role": "assistant",
                "content": "\n\nHello! How can I assist you today?"
            },
            "finish_reason": "stop",
            "index": 0
        }
    ]
}
~~~

## おまけ
chatGPTにケーキの作り方を聞いてみた
~~~
curl -X POST localhost:8002/chatGPT -H "Content-Type: application/json" -d '{"role": "user", "content": "ケーキの作り方を教えて"}'
~~~

非常に読みにくいが、しっかりとレスポンスが返ってきました。
~~~json:response
{
    "id": "chatcmpl-6rmvz0EhoL7oOnx94hRV9rdhLaaNb",
    "object": "chat.completion",
    "created": 1678277507,
    "model": "gpt-3.5-turbo-0301",
    "usage": {
        "prompt_tokens": 20,
        "completion_tokens": 417,
        "total_tokens": 437
    },
    "choices": [
        {
            "message": {
                "role": "assistant",
                "content": "\n\n材料（20cm丸型分量）\n\n・薄力粉 100g\n・コーンスターチ 20g\n・卵黄 2個分\n・卵白 2個分\n・グラニュー糖 80g\n・サラダ油 大さじ1\n・牛乳 50ml\n\n作り方\n\n1.薄力粉とコーンスターチを混ぜてふるいにかける。\n\n2.卵黄をボウルに入れて泡立て器で混ぜる。グラニュー糖を少しずつ加え、白っぽくなるまでよく混ぜる。油、牛乳を加えてさらに混ぜる。\n\n3.1の粉を2に加え、ゴムべらで切るように混ぜる。\n\n4.別のボウルに卵白を入れ、泡立て器でツノが立つまでしっかりと泡立てる。\n\n5.3に1/3の量の4を加え、ゴムべらで丁寧に混ぜる。\n\n6.残りの4を半分に分け、2回に分けて入れ、ゴムべらで切るように混ぜる。\n\n7.生地を20cm丸型に入れ、170度に温めたオーブンで40分焼く。\n\n8.焼きあがったら取り出し、型から外して冷まして完成。"
            },
            "finish_reason": "stop",
            "index": 0
        }
    ]
}
~~~

## おわりに
PythonによるchatGPT APIの基本的な使用方法を紹介しました。
まだ応答速度が割と遅く複雑な質問になってくるほど応答時間がかかりそうなので活用する場を精査する必要がありそうです。

指摘事項などがありましたら遠慮なくコメントお願いします！！

## 参考サイト
* openai docs
https://platform.openai.com/docs/introduction