【Streamlit】GPT-4oとストリーミングを使って自然なチャットを実現

Streamlit
この記事は約13分で読めます。

こんにちは、JS2IIUです。
StreamlitとOpenAIのAPIを組み合わせることで、GPT-4oを使ったチャットボットを簡単に作成できます。今回は、ストリーミングという技術を使って、まるで人間がタイピングしているかのような、より自然なチャット体験を実現する方法を解説します。今回もよろしくお願いします。

はじめに

今回のサンプルコードに使うテクニックはこちらの記事で詳しく説明しているので、事前に確認しておくとスムーズに進めることができます。st.chat_message()st.chat_input()、そしてst.session_state()の使い方について解説しています。

サンプルコード

まずは、今回解説するサンプルコード全体をご紹介します。API KEYのところだけご自身のものを入力してから実行して下さい。

Python
import streamlit as st
from openai import OpenAI

# OpenAIクライアントを生成
client = OpenAI(api_key="YOUR API KEY HERE")  # ここにあなたのAPIキーを入力してください

# セッションステートを初期化
if "messages" not in st.session_state:
    st.session_state.messages = []

# メッセージ履歴を表示
for message in st.session_state.messages:
    with st.chat_message(message["role"]):
        st.markdown(message["content"])

# ユーザーからの入力を受け取る
if prompt := st.chat_input("What is up?"):
    # ユーザーのメッセージを履歴に追加
    st.session_state.messages.append({"role": "user", "content": prompt})
    # チャットメッセージとして表示
    with st.chat_message("user"):
        st.markdown(prompt)

    # OpenAI APIを使ってAIからの応答をストリーミングで生成
    response = client.chat.completions.create(
        model="gpt-4o-mini",
        messages=[
            {"role": "system", "content": "You are a helpful assistant."},
            *st.session_state.messages,
        ],
        stream=True  # ストリーミングを有効にする
    )

    # AIのメッセージをチャンクごとに表示
    with st.chat_message("assistant"):
        full_response = ""
        placeholder = st.empty()  # プレースホルダーを作成
        for chunk in response:
            content = chunk.choices[0].delta.content
            if content is not None:
                full_response += content
                placeholder.markdown(full_response + "")  # 途中経過を表示
        placeholder.markdown(full_response)  # 最終的な応答を表示

    # AIのメッセージを履歴に追加
    st.session_state.messages.append({"role": "assistant", "content": full_response})

解説

それでは、上記のコードをステップバイステップで詳しく解説していきます。

1. 必要なライブラリをインポート

Python
import streamlit as st
from openai import OpenAI
  • streamlit: ユーザーインターフェースを構築するためのライブラリです。
  • openai: OpenAIのAPIにアクセスするためのライブラリです。

2. OpenAIクライアントを生成

Python
client = OpenAI(api_key="YOUR API KEY HERE")  # ここにあなたのAPIキーを入力してください
  • OpenAI(api_key="YOUR API KEY HERE") でOpenAIのAPIにアクセスするためのクライアントオブジェクトを生成します。
  • YOUR API KEY HERE の部分を、ご自身のOpenAI APIキーに置き換えてください。

3. セッションステートを初期化

Python
if "messages" not in st.session_state:
    st.session_state.messages = []
  • st.session_state は、Streamlitが提供するセッション状態管理機能です。
  • このコードでは、messages というキーでチャットのメッセージ履歴をリストとして保存します。
  • if "messages" not in st.session_state: で、セッション状態に messages が存在しない場合(つまり、アプリが初めて起動された場合)に、空のリスト []st.session_state.messages に代入して初期化しています。

4. メッセージ履歴を表示

Python
for message in st.session_state.messages:
    with st.chat_message(message["role"]):
        st.markdown(message["content"])
  • st.session_state.messages に保存されているメッセージ履歴を for ループで一つずつ取り出します。
  • 各メッセージについて、st.chat_message(message["role"]) を使ってチャットメッセージを表示します。
    • message["role"] は、メッセージの送信者が “user” か “assistant” かを示します。
  • st.markdown(message["content"]) で、メッセージの内容をMarkdown形式で表示します。

5. ユーザーからの入力を受け取る

Python
if prompt := st.chat_input("What is up?"):
    # ユーザーのメッセージを履歴に追加
    # ...

6. ユーザーのメッセージを履歴に追加 & 表示

Python
    st.session_state.messages.append({"role": "user", "content": prompt})
    with st.chat_message("user"):
        st.markdown(prompt)
  • ユーザーが入力した prompt を、{"role": "user", "content": prompt} という辞書形式で st.session_state.messages に追加します。
  • これにより、メッセージ履歴にユーザーの入力が保存されます。
  • st.chat_message("user") を使って、ユーザーのメッセージをチャット画面に表示します。

7. OpenAI APIを使ってAIからの応答をストリーミングで生成

Python
    response = client.chat.completions.create(
        model="gpt-4o-mini",
        messages=[
            {"role": "system", "content": "You are a helpful assistant."},
            *st.session_state.messages,
        ],
        stream=True  # ストリーミングを有効にする
    )
  • client.chat.completions.create(...) で、OpenAIのAPIを呼び出してAIからの応答を生成します。
    • model="gpt-4o-mini" : 使用する言語モデルをGPT-4o-miniに指定します。
    • messages=[...] : AIに渡すメッセージのリストを指定します。
      • {"role": "system", "content": "You are a helpful assistant."} は、AIの役割を「役に立つアシスタント」として設定します。
      • *st.session_state.messages で、これまでのメッセージ履歴をすべてAIに渡します。これにより、AIは過去の会話の流れを踏まえて応答を生成できます。
    • stream=True: ストリーミング応答を有効にします。これにより、AIの応答をチャンクごとに受け取ることができます。

8. AIのメッセージをチャンクごとに表示

Python
    with st.chat_message("assistant"):
        full_response = ""
        placeholder = st.empty()  # プレースホルダーを作成
        for chunk in response:
            content = chunk.choices[0].delta.content
            if content is not None:
                full_response += content
                placeholder.markdown(full_response + "")  # 途中経過を表示
        placeholder.markdown(full_response)  # 最終的な応答を表示
  • st.chat_message("assistant") を使って、AIからのメッセージを表示する領域を作成します。
  • full_response = "": AIからの応答全体を格納する変数を初期化します。
  • placeholder = st.empty(): 応答を表示するためのプレースホルダーを作成します。
  • for chunk in response:: ストリーミングで受け取った応答をチャンクごとに処理します。
    • content = chunk.choices[0].delta.content: チャンクから応答テキストを取り出します。
    • if content is not None: 応答テキストが存在する場合にのみ、以下の処理を実行します。
      • full_response += content: 応答テキストを full_response に追加します。
      • placeholder.markdown(full_response + "▌"): プレースホルダーに現在の応答内容と「▌」を表示することで、タイピング中の様子を表現します。
  • placeholder.markdown(full_response): すべてのチャンクを受信後、最終的な応答をプレースホルダーに表示します。

9. AIのメッセージを履歴に追加

Python
    st.session_state.messages.append({"role": "assistant", "content": full_response})
  • AIが生成した full_response を、{"role": "assistant", "content": full_response} という辞書形式で st.session_state.messages に追加します。

まとめ

StreamlitとOpenAIのAPI、そしてストリーミングを組み合わせることで、まるで人間と会話しているかのような自然なチャットボットを構築できます。ぜひ、ご自身のアプリケーションにこの技術を組み込んで、ユーザーに新しい体験を提供してみてください。

さらに発展として、LangChainを使ったアプリ構築について、こちらの記事で確認してみて下さい。

参考リンク

最後に、書籍のPRです。

最新のOpenAIのチャットAPIの使い方もしっかりと解説されている良書です。2024年11月初版発行、「LangChainとLangGraphによるRAG・AIエージェント[実践]入門」西見、吉田、大嶋著。

最後まで読んでいただきありがとうございました。73

コメント

タイトルとURLをコピーしました