Web Speech API を使ったシステムのバックエンド部分作ってみた

# はじめに
株式会社HRBrainでインターンをしている藤井です。
本記事はHRBrain Advent Calendar 2023の7日目の記事になります！
https://qiita.com/advent-calendar/2023/hrbrain

# システム概要
Webぺージでブラウザの音声認識機能を使ってChatGPTに話しかけ、音声合成機能を使って音声でChatGPTの返答を受け取ることができるシステムを作りました！


システムのイメージはこんな感じ。PythonでChatGPTのAPIを叩く部分をバックエンドとしました。
フロントエンドについては、[こちらの記事](https://qiita.com/yvngodowny/items/e101114168c054ff586a)で書いてます！

![スクリーンショット 2023-11-29 16.10.30.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3471914/454ac754-3c0e-f2de-b539-8914379f3ea9.png)

# インストール

flaskをインストールします。
```
pip install -U flask
```
今回はFlaskでCORS(Cross Origin Resource Sharing)を使用するため、[Flask-CORS](https://flask-cors.readthedocs.io/en/latest/)もインストールします。
```
pip install -U flask-cors
```
ChatGPTのAPIを使用するためにライブラリをインストールします。
```
pip install openai
```
# アプリケーションの作成
`YOUR_API_KEY`には自分のAPIキーに書き換えてください。
```python:app.py
from flask import Flask, request, jsonify
import openai
from flask_cors import CORS

openai.api_key = "YOUR_API_KEY"

app = Flask(__name__) 
app.config['JSON_AS_ASCII'] = False #1
CORS(app) #2

@app.route('/', methods=['POST'])
def chat():

    question = request.get_data(as_text=True)

    #3
    res = openai.ChatCompletion.create(
        model = "gpt-3.5-turbo",
        messages=[
            {"role": "system", "content": "威圧的な態度で話してください"}, # 役割設定（省略可）
            {"role": "user", "content": question} # 最初の質問
        ],
        temperature = 1
    )

    #4
    answer = res["choices"][0]["message"]["content"]
    json_data = {
        'answer': answer
    }

    answer = jsonify(json_data)

    return answer


if __name__ == '__main__':
    app.run(host='0.0.0.0', port=80)
```
('/')というルートURLに対して、POSTメソッドを受け付けるように設定し、ChatGPTのAPIを実行するためのエンドポイントを作成しています。POSTリクエストから質問内容を取得して`question`に代入し、`OpenAIGPT-3.5`モデルを使用して質問に対する回答を生成します。生成された回答はJSON形式に変換し、HTTPレスポンスとして返します。

## 1. JSONシリアライズの設定
```python
app.config['JSON_AS_ASCII'] = False #1
```
今回はフロントエンド側にJSON形式でレスポンスを返す形になっていますが、Flaskアプリケーションはデフォルトで文字列をASCIIエンコーディング変換してからJSON形式にシリアライズします。その場合、非ASCII文字（例えば日本語や中国語の漢字とかね…）が含まれる場合に問題が生じる可能性があります。そこで、`JSON_AS_ASCII`を`False`に設定することで、FlaskはUnicode文字列をそのままJSON形式にシリアライズするようになります。
## 2. CORSの設定
```python
CORS(app) #2
```
CORSは、ブラウザの同一オリジンポリシーによって制限された異なるオリジンからのリクエストを許可するための仕組みです。CORS(app)をすることで、Flask-CORS拡張機能により、FlaskアプリケーションにCORSを有効にすることができ、HTTPリクエストのヘッダーにAccess-Control-Allow-Originを含めることで、異なるオリジンからのリクエストを許可することができます。

以下の記事を参考にさせていただきました
[オリジン間リソース共有（CORS）](https://developer.mozilla.org/ja/docs/Web/HTTP/CORS)

## 3. OpenAIGPT-3.5モデルを使用して回答の生成
```python
#3
    res = openai.ChatCompletion.create(
        model = "gpt-3.5-turbo",
        messages=[
            {"role": "system", "content": "威圧的な態度で話してください"}, # 役割設定（省略可）
            {"role": "user", "content": question} # 最初の質問
        ],
        temperature = 1
    )
```
openai.ChatCompletion.create()はOpenAI APIを呼び出して、与えられたメッセージに基づいて応答を生成するためのリクエストを送信します。このリクエストには以下のパラメータが含まれています。
| パラメータ | 詳細 |
|:-----------:|:------------|
| model       | 使用するモデルを指定します。今回はgpt-3.5-turboを指定します。        |
| messages     | 文脈を生成するためのパラメータです。      |
| temperature       |  0~1の間で指定します。数字が高いほど、より多様な文章が生成される傾向あります 。|

また、messageオブジェクトに関しては、以下のような意味があります。
`system`：どのように回答してほしいのか、言語は何かなどの設定を指定することができます。
`user`：ユーザからの質問がここに入ります。
`assistant`：今回は設定していないですが、知識や前提条件を入力することができます。

以下の記事を参考にさせていただきました。
[ChatGPT APIの各種パラメーターを指定して動作確認してみた。](https://qiita.com/kuromame1020611/items/0233be428a92d2d4e762)

## 4. 回答を取得してJSON形式に変換
```python
#4
    answer = res["choices"][0]["message"]["content"]
    json_data = {
        'answer': answer
    }

    answer = jsonify(json_data)
```
JSON形式のレスポンスから`content`を取得し、`answer`に代入しています。HTTPレスポンスはJSON形式に変換して返しています。
以下はChatGPTからのレスポンス内容です。
```
{
  "choices": [
    {
      "finish_reason": "stop",
      "index": 0,
      "message": {
        "content": "\u305d\u308c\u306f\u3042\u306a\u305f\u306b\u6559\u3048\u308b\u5fc5\u8981\u306f\u3042\u308a\u307e\u305b\u3093\u3002\u4eca\u3059\u3050\u305d\u306e\u8cea\u554f\u3092\u3084\u3081\u306a\u3055\u3044\u3002",
        "role": "assistant"
      }
    }
  ],
  "created": 1690107059,
  "id": "chatcmpl-7fQLDRC8bqyRNq86I1ZCBRBUCBxcW",
  "model": "gpt-3.5-turbo-0613",
  "object": "chat.completion",
  "usage": {
    "completion_tokens": 32,
    "prompt_tokens": 36,
    "total_tokens": 68
  }
}
```
# 実際に動かしてみました

実際にAPIを叩いてみます。
200のレスポンスが返ってきたので、リクエストに成功しています。
```
WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead.
 * Running on all addresses (0.0.0.0)
 * Running on http://127.0.0.1:80
 * Running on http://172.17.0.167:80
Press CTRL+C to quit
127.0.0.1 - - [30/Nov/2023 17:08:22] "POST / HTTP/1.1" 200 -
```


フロント側からはこんな感じで見えます。APIがうまく叩けていることが確認できました！

![画面収録 2023-11-30 17.08.04 (2).gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3471914/8b1938ae-39db-9302-3241-76b13ab313a6.gif)



# 最後に
フロントエンドもバックエンドもまだまだ未熟なので、どんどん知識・技術を身に着けてスキルアップしていきたいです💪🏻

株式会社HRBrainでは一緒に働くメンバーを募集しています！ぜひご覧ください👀
https://www.hrbrain.co.jp/recruit

# 参考サイト
オリジン間リソース共有（CORS）
https://developer.mozilla.org/ja/docs/Web/HTTP/CORS

ChatGPT APIの各種パラメーターを指定して動作確認してみた。
https://qiita.com/kuromame1020611/items/0233be428a92d2d4e762