見出し画像

FlaskとOpenAI APIでチャットボットを作る

このたび、ノンプログラマーのためのスキルアップ研究会(以下、ノンプロ研)のご支援のもとで「ラズパイとFlaskでつくる!Webアプリ開発入門」という技術同人誌を出させていただきました。

購入はこちらから!

そこでこの記事では、本の追加コンテンツみたいな感じで、簡単に作れるWebアプリなどを書いていきます。
本を買ってくださった方はぜひ試してみて下さい!


FlaskとOpenAI APIでチャットボットを作る

今回のサンプルアプリではOpenAI APIを使ってFlaskでチャットボットを作成していきます。OpenAI APIはChatGPTを開発したOpenAI社が提供しているWebAPIで、ChatGPTで使われている会話AIモデルを使ってWebアプリを開発することができます。ChatGPTを使う場合サインインが必要ですが、APIを使ったWebアプリではそのような手間がありません。会話AIを使ったことがない人にお試しで使ってもらうのがおススメです。

OpenAI APIを使う

OpenAI APIを使用するには、OpenAIサイトでアカウントを作成したあと、クレジットカード情報を登録してAPIキーを取得します。詳しくは、こちらのサイト様の解説が分かりやすいです。

サンプルWebアプリの作成

サンプルWebアプリとして、やりたいことを入力するとそれにそったExcel VBAのコードを書いてくれるチャットボットを作ります。
以下ではディレクトリの構成から必要なライブラリのインポートと環境変数の設定、及び全コードをそれぞれ載せていきます。

ディレクトリの構成

まず、ディレクトリの構成を図示します。

ディレクトリ・ファイルの内容は以下の通りです。

  • Chatbot:このサンプルアプリのルートディレクトリです。

  • app.py:Webアプリの動作を書いていくファイルです。URLの定義やAPIを利用して会話AIからの回答を出力します。

  • .env:APIキー認証を行うための環境変数を書いたファイルです。

  • templates:以下のHTMLファイルを格納するディレクトリです。

    • index.html:やりたいことを入力するテキストボックスを配置し、会話AIからの回答を表示する画面になります。

  • venv:仮想環境有効化用ディレクトリです。事前に作る必要はありません。

必要なライブラリのインポートと環境変数の設定

まず必要なライブラリをインポートしていきます。仮想環境を有効化して、Flaskライブラリ、openaiライブラリとpython-dotenvをインストールします。

(venv) ユーザー名@raspberrypi:~/Desktop/Chatbot $ pip install flask openai python-dotenv

続いて、APIを使うにあたって、.envファイルに書いた環境変数を読み込む形でAPIキー認証を行うよう以下のように書き込みます。

OPENAI_API_KEY = 取得したAPIキーの値

全コードの記載

続いて、コード全体を記載していきます。
まず、app.pyは下記の通りです。

from flask import Flask, render_template, jsonify, request
from markupsafe import escape
from openai import OpenAI
import os
from dotenv import load_dotenv

load_dotenv()
openai_api_key = os.environ.get("OPENAI_API_KEY")

app = Flask(__name__)

def text_generate(message=None):
    client = OpenAI(
        api_key=openai_api_key,
    )

    res = client.chat.completions.create(
        messages=[
            {
                "role": "user",
                "content": f"次のことができるExcel VBAコードを書いてください:{message}",
            }
        ],
        model="gpt-3.5-turbo",
    )
    
    generated_text = res.choices[0].message.content
    return generated_text

@app.route('/', methods=['GET'])
def toppage():
    return render_template('index.html')

@app.route('/create_text', methods=['POST'])
def create_text():
    message = request.form['message']
    generated_text = text_generate(message=message)
    generated_text = escape(generated_text)
    return jsonify({'message': generated_text})

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

続いて、index.htmlは次の通りです。

<!DOCTYPE html>
<html>
<head>
    <title>Flask Chatbot</title>
</head>
<body>
    <div>
        <div>
            <div>
                <h1>Flask Chatbot</h1>
                <p>やりたいこと:</p>
                <form id="create-text-form">
                    <div>
                        <textarea id="message" name="message" rows="3" required></textarea>
                    </div>
                    <button type="submit">送信</button>
                </form>
                <div id="loading" style="display:none;">考え中・・・</div>
                <pre id="result"></pre>
            </div>
        </div>
    </div>
    <script>
        document.addEventListener('DOMContentLoaded', function() {
            document.getElementById('create-text-form').addEventListener('submit', function(event) {
                event.preventDefault();
                var loading = document.getElementById('loading');
                var result = document.getElementById('result');
                var message = document.getElementById('message').value;

                loading.style.display = 'block';
                result.textContent = '';

                fetch('/create_text', {
                    method: 'POST',
                    headers: {
                        'Content-Type': 'application/x-www-form-urlencoded',
                    },
                    body: 'message=' + encodeURIComponent(message)
                })
                .then(response => response.json())
                .then(data => {
                    loading.style.display = 'none';
                    result.innerHTML = '回答: ' + data.message;
                })
                .catch(error => {
                    console.error('Error:', error);
                    loading.style.display = 'none';
                });
            });
        });
    </script>
</body>
</html>

動かしてみる

作ったWebアプリを、ラズパイとngrokを使って動かしてみましょう。

やりたいことを入力し、送信ボタンを押します。

結果が返ってきました。コードの内容についても解説してくれます。

コードの解説

※コードの説明につきましては、拙著を購読下さった前提で進めさせていただきます。

app.py

まず、必要なライブラリ・モジュールをインストールして環境変数を読み込んだ後、インスタンスを作成します。

from flask import Flask, render_template, jsonify, request
from markupsafe import escape
from openai import OpenAI
import os
from dotenv import load_dotenv

load_dotenv()
openai_api_key = os.environ.get("OPENAI_API_KEY")

app = Flask(__name__)

jsonifyは結果をJSONレスポンスに変換するために使います。また、markupsafeライブラリから文字列をHTMLエスケープするためのescape関数を使用します。markupsafeライブラリはFlaskのインストール時に一緒にインストールされるため、追加でインストールする必要はありません。インポート後、load_dotenv関数を用いて.envファイルに書いたAPIキーを読み込みます。

次に、OpenAI APIに質問を送信し、回答を受け取る関数を定義します。

def text_generate(message=None):
    client = OpenAI(
        api_key=openai_api_key,
    )

    res = client.chat.completions.create(
        messages=[
            {
                "role": "user",
                "content": f"次のことができるExcel VBAコードを書いてください:{message}",
            }
        ],
        model="gpt-3.5-turbo",
    )
    
    generated_text = res.choices[0].message.content
    return generated_text

client = OpenAI(api_key=openai_api_key)では、OpenAIのAPIを利用するためのクライアントを初期化しています。ここで、環境変数から読み込まれたAPIキーをOpenAIクライアントのAPIキー(api_key)の引数として渡しています。

res = client.chat.completions.create()では、OpenAIのchat.completions.createメソッドを呼び出して、APIリクエストを送信しています。このメソッドで、指定されたパラメータに基づいてテキストを生成します。

messagesの部分で、APIに送信する会話の内容を定義していきます。
"role"で役割、"content"で会話の内容を定義しており、このコードではユーザーが「入力したこと(message)ができるExcel VBAコードを書いてください」とAIに依頼した会話になっています。
また、model="gpt-3.5-turbo"で、使用するAIモデルにgpt-3.5-turboをしています。

generated_text = res.choices[0].message.contentでAPIのレスポンスから生成されたテキストを取得します。基本的に、回答テキストの取得はchoices[0].message.contentで行うことができます。

続いて、画面から入力されたやりたいことから、AIが生成した回答を返すURLとパスオペレーション関数を定義します。

@app.route('/create_text', methods=['POST'])
def create_text():
    message = request.form['message']
    generated_text = text_generate(message=message)
    generated_text = escape(generated_text)
    return jsonify({'message': generated_text})

誤動作を防ぐためHTTPメソッドはPOSTメソッドとします。画面からやりたいことをmessageとして受け取った後、それを引数として上記の関数に渡し、生成されたテキストをHTMLパース後にJSON形式のデータにして返します。

index.html

<div>
    <h1>Flask Chatbot</h1>
    <p>やりたいこと:</p>
        <form id="create-text-form">
            <div>
                <textarea id="message" name="message" rows="3" required></textarea>
            </div>
            <button type="submit">送信</button>
        </form>
    <div id="loading" style="display:none;">考え中・・・</div>
    <pre id="result"></pre>
</div>

やりたいことを入力するテキストボックスを配置し、回答が生成中であれば「考え中・・」、回答が生成されていればそれを出力する箇所です。各idは次のJavaScriptコードでの識別のために使います。

続いて、ユーザーがフォームを通じて送信したメッセージをサーバーに送り、サーバーからの応答を受け取って表示するJavaScriptを書いていきます。

<script>
    document.addEventListener('DOMContentLoaded', function() {
        document.getElementById('create-text-form').addEventListener('submit', function(event) {
            event.preventDefault();
            var loading = document.getElementById('loading');
            var result = document.getElementById('result');
            var message = document.getElementById('message').value;

            loading.style.display = 'block';
            result.textContent = '';

            fetch('/create_text', {
                method: 'POST',
                headers: {
                    'Content-Type': 'application/x-www-form-urlencoded',
                },
                body: 'message=' + encodeURIComponent(message)
            })
            .then(response => response.json())
            .then(data => {
                loading.style.display = 'none';
                result.innerHTML = '回答: ' + data.message;
            })
            .catch(error => {
                console.error('Error:', error);
                loading.style.display = 'none';
            });
        });
    });
</script>

document.getElementById('create-text-form').addEventListener('submit', function(event) {...})は、idがcreate-text-formである送信フォームからやりたいことが送信されたときに実行される関数を定義しています。
var result = document.getElementById('result');で結果を表示する要素、
var message = document.getElementById('message').value;でユーザーが入力したメッセージを取得します。

その後、fetch関数を使用して、URL'/create_text'に非同期リクエストを送信します。
.then(response => response.json())でURL'/create_text'の結果{'message': generated_text}をJSON形式で取得し、.then(data => {...}):で'message'の値(generated_text)を取得して、結果を表示します。

最後に

今回はOpenAI APIを使ったWebアプリを解説してきましたが、個人で使う分にはChatGPTを使うほうが良い場面が多いと考えられます。
冒頭で書いたように、会話AIを使ったことがない人へのデモンストレーション用として使ったり、AIへの質問・依頼内容をあらかじめ定義できることを活かして用途を制限するといった使い方をすると良いと思います。

この記事が気に入ったらサポートをしてみませんか?