MENU

FlaskとChatGPT APIで作る初めてのWebチャットボット|実践で学ぶAI開発入門

これまで、このブログでは「ChatGPTのAPIをカスタマイズしてチャットボットを作成」する方法や、「Flask入門:Pythonで簡単Webアプリ開発に挑戦!]」といったテーマで、私自身の学習過程を記録してきました。

ChatGPT APIでAIの応答をコントロールする方法を学び、FlaskでWebアプリの基本的な作り方を覚えました。ということで、この二つを組み合わせて、Web上で動くオリジナルのAIチャットボットを作ることに挑戦しようと思います!

この記事で分かること
  • FlaskプロジェクトでOpenAIライブラリを使うための環境設定
  • Webフォームからの入力をChatGPT APIに渡す具体的な方法
  • ChatGPTからの応答をWebページに表示する流れ
  • 簡単な会話履歴を保持する仕組み(Flaskのsession利用)
  • APIキーの安全な管理方法 (.envファイル)
  • 開発中に遭遇しやすいエラーとその対処法(体験談ベース)

今回も、専門家による完璧な解説というよりは、私自身が試行錯誤した道のりを共有するスタイルでお届けします。FlaskとAIの連携に興味がある方の、最初の一歩の後押しになれば嬉しいです!

最終的に以下のアプリを作りました!

作成したwebチャットボット

目次

環境構築と準備: Webチャットボット開発の第一歩

まずは、開発環境を整えるところからスタートです。Webアプリを作るためのFlaskと、ChatGPT APIを使うためのOpenAIライブラリが必要になります。そして、大事なAPIキーの管理方法もここで押さえておきましょう。

実施内容の概要
  1. 必要なPythonライブラリ (Flask, openai, python-dotenv) をインストール。
  2. OpenAI APIキーを取得(まだの方はOpenAIのサイトで取得してください)。
  3. APIキーを安全に管理するため、プロジェクトルートに.envファイルを作成し、そこにキーを記述。
  4. 基本的なFlaskプロジェクトのフォルダ構成を作成 (app.py, templates/, .env)。

ライブラリのインストール

まずは開発環境を整えることから始めました。以前の記事で使ったcondaを活用して、クリーンな環境を用意します。

# 新しい仮想環境を作成
conda create -n flask-chatbot python
conda activate flask-chatbot

# 必要なパッケージをインストール(今回はpipを使用)
pip install flask openai python-dotenv

python-dotenv は、.envファイルに書かれた環境変数をプログラム内から簡単に読み込めるようにするためのライブラリです。これが非常に便利なので、ぜひ利用しましょう!

以前も説明しましたが、環境構築は仮想環境を設定してから行うようにしましょう。
詳しくは以下の記事をご確認ください。

APIキーの安全な管理 (.envファイル)

絶対にやってはいけないのが、APIキーを直接Pythonコード (app.pyなど) に書き込むことです! もしコードをGitHubなどで公開したら、APIキーが世界中に漏洩してしまいます。
そこで、プロジェクトのルートディレクトリ(app.pyと同じ場所)に .env という名前のファイルを作成し、以下のように記述します。

# ご自身のAPIキーに置き換えてください 
OPENAI_API_KEY=sk-◯◯◯
# Flaskのsession機能で使う秘密鍵。後述します。適当な文字列でOK。
FLASK_SECRET_KEY=your_very_secret_flask_key

注意点: .envファイルは.gitignoreに追加して、Gitリポジトリに含めないようにしましょう!

具体的な手順としては以下のとおりです。

1. .gitignore ファイルを作成する
プロジェクトのルートディレクトリ(app.py と同じ場所)に .gitignore という名前のファイルを作ります。
2. .gitignore.env を追加する
.gitignore に次の1行を書きます。

.env


この設定をすることで、.env ファイルは Git に無視されリポジトリに追加されなくなります。

プロジェクト構成

最後にプロジェクトの構成を考えます。以前のFlask入門で学んだように、適切なファイル構成がWebアプリ開発では重要になります。今回は以下のような構成にしました。

学んだこと・感じたこと
  • APIキーの扱い
    • 最初は「まあローカルだし」とコードに直接書こうかと思いましたが、将来的に公開する可能性を考えると、最初から.envを使う習慣をつけるのが大事だと感じました。(うっかりAPIキーを公開してしまうと、悪用された場合大変なことに…)
    • python-dotenvのおかげで、思ったより簡単に扱えました。
  • .gitignoreの重要性
    • うっかり.envをコミットしないように、最初に.gitignore.envを追加することが重要だと感じました。

FlaskアプリケーションとChatGPT APIの統合(app.py)

環境が整ったので、いよいよFlaskアプリの骨格と、ChatGPT APIとの連携部分を作っていきます。Flaskアプリケーションのベースとなるコードをapp.pyに書いていきます。

from flask import Flask, render_template, request, jsonify, session
from openai import OpenAI
import os
from dotenv import load_dotenv
import secrets

# 環境変数のロード
load_dotenv()

# Flaskアプリケーションの初期化
app = Flask(__name__)
# sessionを使うためにSECRET_KEYを設定 (環境変数から読み込む)
app.secret_key = os.getenv('FLASK_SECRET_KEY')

# OpenAI APIクライアントの初期化
client = OpenAI(api_key=os.getenv("OPENAI_API_KEY"))

@app.route('/')
def index():
    # 新しいセッションの場合、チャット履歴を初期化
    if 'chat_history' not in session:
        session['chat_history'] = [
            {"role": "system", "content": "あなたは親切なAIアシスタントです。簡潔で役立つ回答をしてください。"}
        ]
    return render_template('index.html')

@app.route('/chat', methods=['POST'])
def chat():
    data = request.json
    user_message = data.get('message', '')
    
    # セッションからチャット履歴を取得
    chat_history = session.get('chat_history', [
        {"role": "system", "content": "あなたは親切なAIアシスタントです。簡潔で役立つ回答をしてください。"}
    ])
    
    # ユーザーのメッセージをチャット履歴に追加
    chat_history.append({"role": "user", "content": user_message})
    
    try:
        # ChatGPT APIを呼び出し
        response = client.chat.completions.create(
            model="gpt-3.5-turbo",
            messages=chat_history,
            temperature=0.7,
            max_tokens=500
        )
        
        # APIからの応答をチャット履歴に追加
        assistant_message = response.choices[0].message.content
        chat_history.append({"role": "assistant", "content": assistant_message})
        
        # セッションにチャット履歴を保存
        session['chat_history'] = chat_history
        
        return jsonify({"response": assistant_message})
    
    except Exception as e:
        return jsonify({"error": str(e)}), 500

if __name__ == '__main__':
    app.run(debug=True)
コード解説
  • チャットの処理(@app.route('/chat', methods=['POST'])
    • /chat にPOSTリクエストが送られたときに実行される関数
    • request.json でクライアントから送られたJSONデータを取得
    • data.get('message', '') でユーザーのメッセージを取得
コードのポイント
  • Flaskのsessionを活用:
    • Flaskの組み込みセッション機能(session)を使うことで、クッキーベースのセッション管理が簡単に実装できました。
    • これによりユーザーごとに別々の会話履歴を保持できます。
  • エラーハンドリング
    • APIリクエストは時折失敗することがあるので、try-except文でエラーをキャッチしています。
  • OpenAIクライアントの初期化
    • 以前はOpenAIのライブラリの使い方が少し違いましたが、最新バージョンでは上記のclient=のように初期化する方法になっているようでした。
    • 古い書き方でも動きますが、できれば推奨されている最新の書き方が良いですね。

チャットの処理(jsonの送受信)とFlaskのsession以外は、以前の記事で学んだ内容なので細かくは解説していません。気になる方は下記の記事をご確認ください。

チャットインターフェースのフロントエンド開発(HTML,CSS,JavaScript)

Pythonを使ってバックエンド部分の作成が出来たため、次にユーザーがチャットボットと会話するためのフロントエンドを作ります。まずは基本的なHTMLテンプレートから始めます。

※長くなってしまうため、フロントエンドのコード解説は最低限にします。フロントエンドの話は、また別の記事で詳しくできればと思います。

JavaScriptとの連携は以下の記事でまとめていますので、そちらを御覧ください。

<!-- templates/layout.html -->
<!DOCTYPE html>
<html>
<head>
    <title>Flask ChatGPT ボット</title>
    <link rel="stylesheet" href="{{ url_for('static', filename='css/style.css') }}">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
</head>
<body>
    {% block content %}{% endblock %}
    <script src="{{ url_for('static', filename='js/chat.js') }}"></script>
</body>
</html>
<!-- templates/index.html -->
{% extends "layout.html" %}

{% block content %}
<div class="chat-container">
    <div class="chat-header">
        <h1>FlaskとChatGPT APIで作るWebチャットボット</h1>
    </div>
    <div class="chat-messages" id="chat-messages">
        <div class="message bot-message">
            <div class="message-content">こんにちは!何か質問がありますか?</div>
        </div>
    </div>
    <div class="chat-input-container">
        <input type="text" id="user-input" placeholder="メッセージを入力...">
        <button id="send-button">送信</button>
    </div>
</div>
{% endblock %}
コード解説
  • layout.html共通のレイアウト を定義
  • index.htmlチャット画面 を作成
  • extendslayout.html を継承し、block content で上書き
  • チャットのデザインには static/css/style.css、動作には static/js/chat.js が関与

次に、CSSを用いてチャット画面の見た目を整えます。

/* static/css/style.css */
* {
    box-sizing: border-box;
    margin: 0;
    padding: 0;
}

body {
    font-family: 'Helvetica Neue', Arial, sans-serif;
    background-color: #f5f5f5;
    height: 100vh;
    display: flex;
    justify-content: center;
    align-items: center;
}

.chat-container {
    width: 100%;
    max-width: 800px;
    height: 90vh;
    background-color: white;
    border-radius: 10px;
    overflow: hidden;
    box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
    display: flex;
    flex-direction: column;
}

.chat-header {
    background-color: #4285f4;
    color: white;
    padding: 15px;
    text-align: center;
}

.chat-messages {
    flex: 1;
    overflow-y: auto;
    padding: 20px;
}

.message {
    margin-bottom: 15px;
    display: flex;
}

.user-message {
    justify-content: flex-end;
}

.bot-message {
    justify-content: flex-start;
}

.message-content {
    max-width: 70%;
    padding: 10px 15px;
    border-radius: 18px;
    font-size: 16px;
    line-height: 1.4;
}

.user-message .message-content {
    background-color: #e3f2fd;
    color: #0d47a1;
}

.bot-message .message-content {
    background-color: #f1f1f1;
    color: #333;
}

.chat-input-container {
    display: flex;
    padding: 15px;
    border-top: 1px solid #eee;
}

#user-input {
    flex: 1;
    padding: 12px;
    border: 1px solid #ddd;
    border-radius: 20px;
    font-size: 16px;
    outline: none;
}

#send-button {
    margin-left: 10px;
    padding: 0 20px;
    background-color: #4285f4;
    color: white;
    border: none;
    border-radius: 20px;
    cursor: pointer;
    font-size: 16px;
}

#send-button:hover {
    background-color: #3367d6;
}
コード解説

チャットボットの見た目を整える役割

  • 中央配置 & レスポンシブ対応
  • ユーザーとボットのメッセージを色分け
  • 入力欄とボタンをスッキリデザイン
  • 吹き出し風のデザインで見やすく

最後に、JavaScriptを使ってユーザーの入力を受け取り、それをAPIに送る処理を作ります。Flaskのセッション機能を使っているので、ユーザー側でセッションIDを管理する必要はありません。

// static/js/chat.js
document.addEventListener('DOMContentLoaded', function() {
    const chatMessages = document.getElementById('chat-messages');
    const userInput = document.getElementById('user-input');
    const sendButton = document.getElementById('send-button');
    
    function addMessage(content, isUser) {
        const messageDiv = document.createElement('div');
        messageDiv.classList.add('message');
        messageDiv.classList.add(isUser ? 'user-message' : 'bot-message');
        
        const messageContent = document.createElement('div');
        messageContent.classList.add('message-content');
        messageContent.textContent = content;
        
        messageDiv.appendChild(messageContent);
        chatMessages.appendChild(messageDiv);
        
        // スクロールを最下部に自動調整
        chatMessages.scrollTop = chatMessages.scrollHeight;
    }
    
    function sendMessage() {
        const message = userInput.value.trim();
        if (message === '') return;
        
        // ユーザーのメッセージを表示
        addMessage(message, true);
        userInput.value = '';
        
        // ボットの応答中を示す表示
        const loadingDiv = document.createElement('div');
        loadingDiv.classList.add('message', 'bot-message');
        const loadingContent = document.createElement('div');
        loadingContent.classList.add('message-content');
        loadingContent.textContent = '考え中...';
        loadingDiv.appendChild(loadingContent);
        chatMessages.appendChild(loadingDiv);
        chatMessages.scrollTop = chatMessages.scrollHeight;
        
        // APIリクエストを送信
        fetch('/chat', {
            method: 'POST',
            headers: {
                'Content-Type': 'application/json',
            },
            body: JSON.stringify({
                message: message
            }),
        })
        .then(response => response.json())
        .then(data => {
            // ローディング表示を削除
            chatMessages.removeChild(loadingDiv);
            
            if (data.error) {
                addMessage('エラーが発生しました: ' + data.error, false);
            } else {
                addMessage(data.response, false);
            }
        })
        .catch(error => {
            // ローディング表示を削除
            chatMessages.removeChild(loadingDiv);
            addMessage('ネットワークエラーが発生しました。', false);
            console.error('Error:', error);
        });
    }
    
    // 送信ボタンのクリックイベント
    sendButton.addEventListener('click', sendMessage);
    
    // Enterキーでも送信可能に
    userInput.addEventListener('keypress', function(e) {
        if (e.key === 'Enter') {
            sendMessage();
        }
    });
});
コード解説

Webチャットボットのフロントエンド処理 を担当するコード

  • DOMContentLoaded イベントでスクリプトを実行
  • メッセージを追加する関数 (addMessage) を定義
  • ユーザーの入力を取得し、API へリクエスト (fetch)
  • 「考え中…」の表示を入れ、ボットの返答を受け取る
  • 送信ボタンのクリック・Enterキーで送信できるように設定
  • エラーハンドリングでネットワークトラブルにも対応

これで、ChatGPT APIを使ったチャットボットが完成しました!実行すると、以下のようにちゃんとやり取りをすることが出来ました!ちゃんと履歴も保存されてますね!(2回目の質問で、しっかりPythonについての答えが返ってきている)

作成したweb chatbot

今回のコードで特に工夫した点は以下のとおりです。

コードの工夫点
  • ユーザーがメッセージを送っている間に「考え中…」と表示されるようにして、現在の状態を分かるようにしました。
  • もしAPIがうまく動かないときにも、ちゃんとエラーメッセージを表示して、何が起きたのかを伝えるようにしています。
  • Flaskのセッションを使うことで、クライアント側のコードが簡単になり、セキュリティも強化されました。

チャットボットのカスタマイズと機能拡張

チャットボットのカスタマイズ(AIの性格や専門性、temperature)

基本的なチャットボットができたので、次はシステムメッセージを変更してチャットボットの性格や専門性をカスタマイズしてみました。

まずは、プログラミング専門のアシスタントにしてみます。app.pyの該当部分を書き換えましょう。

# app.pyのチャット履歴初期化部分を変更
@app.route('/')
def index():
    # プログラミング専門のアシスタントとしてチャット履歴を初期化
    session['chat_history'] = [
        {"role": "system", "content": "あなたはプログラミングに詳しいアシスタントです。初心者に分かりやすく、具体的なコード例を交えて説明してください。"}
    ]
    return render_template('index.html')

次に、チャットボットの回答の創造性や多様性を調整するために、temperatureパラメータを変更してみました。

# より創造的な回答を得るために温度を上げる
response = client.chat.completions.create(
    model="gpt-3.5-turbo",
    messages=chat_history,
    temperature=0.9,  # 0.7から0.9に変更
    max_tokens=500
)

temperatureを0.9に上げると、確かに回答がより多様になりましたが、時には質問から少し脱線することもありました。逆に0.3に下げると事実に基づいた堅実な回答が増えたので、用途によって適切に調整する必要があると実感しました。

チャットボットの拡張機能(リセットボタン)

ユーザーが新しい会話を始めるための「会話リセット」ボタンも追加してみました。

# app.pyに追加
@app.route('/reset_chat', methods=['POST'])
def reset_chat():
    # チャット履歴をリセット
    session['chat_history'] = [
        {"role": "system", "content": "あなたは親切なAIアシスタントです。簡潔で役立つ回答をしてください。"}
    ]
    return jsonify({"status": "success"})
<!-- templates/index.htmlに追加 -->
<div class="chat-header">
    <h1>FlaskとChatGPT APIで作るWebチャットボット</h1>
    <button id="reset-button">会話をリセット</button>
</div>
// static/js/chat.jsに追加
const resetButton = document.getElementById('reset-button');

resetButton.addEventListener('click', function() {
    // チャット履歴を消去
    while (chatMessages.firstChild) {
        chatMessages.removeChild(chatMessages.firstChild);
    }
    
    // 初期メッセージを表示
    addMessage('こんにちは!新しい会話を始めましょう。何か質問がありますか?', false);
    
    // サーバー側でも履歴をリセットするためのリクエスト
    fetch('/reset_chat', {
        method: 'POST',
        headers: {
            'Content-Type': 'application/json',
        },
        body: JSON.stringify({})
    })
    .then(response => response.json())
    .then(data => {
        if (data.error) {
            console.error('Reset error:', data.error);
        }
    });
});

これで、会話を新しく始めたい場合にも対応できるようになりました。以下のように、リセット前後でしっかり会話の内容が変わりましたね。

今回のwebアプリはPCのクッキーを使って会話内容を保存しているため、会話のリセットがわかりにくかったので、これでかなり使いやすくなりました。

Flaskセッションの活用: 今回のWEBアプリは、セッションを使うことでユーザーが異なるブラウザタブを開いた場合でもセッションクッキーによって会話の文脈が保持されます。また、セッションが期限切れになるまで会話履歴が維持されるので、ユーザーがページをリロードしても会話が途切れません。

まとめ

今回は、これまでに学んだFlaskのWebアプリ開発とChatGPT APIの知識を組み合わせて、実際にWeb上で動作するAIチャットボットの構築に挑戦しました。

環境構築から始まり、Flaskでの画面作成、API連携、sessionを使った履歴管理、そしてエラーハンドリングと、一通りの開発プロセスを経験することができました。もちろん、まだまだ改善点や挑戦したいことはたくさんありますが、自分で考えたものがWebアプリとして形になり、AIと対話できるというのは、非常に大きな達成感がありました!

技術を学ぶ一番の近道は、やっぱり実際に手を動かして何か作ってみることですね!皆さんもぜひ、自分だけのWebアプリ開発に挑戦してみてください!

私も、次は非同期処理やデータベース連携など、さらにステップアップした機能実装に挑戦してみようと思います。その様子もまたブログで共有しますので、お楽しみに!

よかったらシェアしてね!
  • URLをコピーしました!

この記事を書いた人

非IT職のデスクワーカーです。
簡単な業務効率化やデータ分析にPythonをよく活用しています。
このブログを通してAI技術を身に着け、より業務効率化や新しいものを作りたいと考えています。

目次