[Python]ChatGPT API利用時に画面が欲しい方へ(Flask + XMLHttpRequest)

2023.04.18

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

データアナリティクス事業本部のueharaです。

突然ですが、ChatGPT流行ってますよね。

特に、Pythonを使ってChatGPT (OpenAI)のAPIを叩くサンプルが多いので、Pythonを使って遊んでいる方も多いのではないでしょうか。

中でもPythonで記載した処理ロジックはそのままに、「ちょっと画面が欲しいな...」というケースがあると思うので、今回はPythonのWebアプリケーションフレームワークであるFlaskを用いて、画面側(HTML/JS)と非同期にやり取りする骨子部分を作ってみたいと思います。

Flaskのインストール方法

特に難しい手順はなく、以下のようにpipでインストールすることができます。

pip install flask

まずはFlaskを使ってみる

今回は簡単ですが、以下のファイル構成を作成します。

.
├ main.py
└ templates/
  └ index.html

main.pyで、いわゆるサーバサイドの処理を行い、index.htmlを画面として使うことを想定しています。

index.htmlの表示

Flaskにおいて、「トップページにアクセスされるとindex.htmlを返す」という処理は以下のように書くことができます。

main.py

from flask import Flask, render_template, request, jsonify

app = Flask(__name__)


@app.route("/")
def index():
    html = render_template("index.html")
    return html


if __name__ == "__main__":
    app.run(debug=True)

index.htmlは、簡単ですが以下のようにしました。

index.html

<html lang="ja">
<header>
    <meta charset="utf-8" />
    <title>Flask Test</title>
</header>

<body>
    <h1>Flask Test</h1>
</body>
</html>

ここまで準備できたら、Flaskを起動します。

$ python main.py

起動すると、デフォルトでは http://127.0.0.1:5000 でListenしている旨が表示されると思うので、ブラウザでアクセスしてみます。

上記のように表示されていれば成功です。

XMLHttpRequestによる非同期通信

まず、Python (Flask)側での処理を記載します。

今回は /endpoint にPOSTされたメッセージをAPIで処理する、といったケースを考え、以下のように書いてみました。

main.py

from flask import Flask, render_template, request, jsonify

app = Flask(__name__)


@app.route("/")
def index():
    html = render_template("index.html")
    return html


def process_by_gpt(message):
    """
    ChatGPT APIを利用して何かしらの処理をする
    """
    return "ChatGPTで処理した {} に対するレスポンス".format(message)


@app.route("/endpoint", methods=["POST"])
def endpoint():
    # エンドポイントにPOSTされたリクエストボディからメッセージを取得
    message = request.json["message"] 
    print("user_message: {}".format(message))
    
    # ChatGPTで処理した結果のメッセージ
    gpt_res_message = process_by_gpt(message)
    
    # 画面側に渡すレスポンスを作成
    response_data = {"gpt_res_message": gpt_res_message}
    
    # レスポンスをJSON形式で返す
    return jsonify(response_data) 


if __name__ == "__main__":
    app.run(debug=True)

ここでは、ChatGPTでの処理は敢えて書かずに(スクリプトがごちゃごちゃしてしまうので)、ChatGPTで処理した {message} に対するレスポンスという形で何かしらの処理をした体で返すようにしました。

次に、先程のindex.htmlを少し編集します。

具体的には入力フォームを用意し、XMLHttpRequestでフォームに入力されたメッセージを /endpoint にPOSTする処理をJavascriptで書いてみます。

※[注] 今回は簡単化のため1つのファイルにまとめるべくhtml上に直接Javascriptの処理を記載していますが、本来であればファイルを分ける方が適切です。

<html lang="ja">
<header>
    <meta charset="utf-8" />
    <title>Flask Test</title>
</header>

<body>
    <form class="msg-form">
        <input type="text" class="msg-input" placeholder="input text">
        <button type="submit" class="msg-send-btn">送信</button>
    </form>
    <div class="msg-res"></div>
    
    <script>
        const msgForm = document.querySelector(".msg-form");
        const msgInput = document.querySelector(".msg-input");
        const msgRes = document.querySelector(".msg-res");

        msgForm.addEventListener("submit", event => {
            event.preventDefault();
            const msgText = msgInput.value;
            console.log(msgText);
            if (!msgText) return;
            
            const xhr = new XMLHttpRequest();
            // エンドポイントを指定
            xhr.open('POST', '/endpoint');
            xhr.setRequestHeader('Content-Type', 'application/json'); 
            
            // レスポンス処理を記述
            xhr.onload = function () {
                const response = JSON.parse(xhr.responseText);
                const gptResMessage = response.gpt_res_message;
                appendResponce(gptResMessage);
            };

            // メッセージをJSON形式に変換して送信
            xhr.send(JSON.stringify({ message: msgText })); 
            msgInput.value = "";
        });
        
        // レスポンスのメッセージを追加していく
        function appendResponce(msg) {
            const msgHTML = `<div class="msg-res">${msg}</div>`;
            msgRes.insertAdjacentHTML("beforeend", msgHTML);
        }
    </script>
</body>
</html>

今回、Python (Flask)側から返ってきたレスポンスはフォームの下に追記していくようにしてみました。

早速、実行してみます。まず、アクセスすると以下のような画面になっていると思います。

入力フォームに適当に文字列を入力して送信すると、レスポンスが下に追記されていきます。

最後に

今回はPythonのWebアプリケーションフレームワークであるFlask用いて、画面側(HTML/JS)と非同期にやり取りする骨子部分を作ってみました。

他にも画面を作る方法としてネイティブアプリであればtkinterや、同じくWebでもより簡単に作成できるStreamlit等様々な方法があるのですが、画面側はHTML/CSS/JSで作れた方がより汎用的な開発ができるので今回このような形で紹介させて頂きました。

上記は非常にシンプルな例となっていますが、作り込めばよりリッチなUIも作成可能ですので(それこそ、本家ChatGPTのようなチャット形式のアプリケーションなど)、参考になりましたら幸いです。

参考文献