python

【Python】Streamlitで簡単なChatBotの作成

今回は、Streamlitで簡単なChatBotの作成を行います。
2023年~2024年にかけてRAGが普及してから社内でChatBotを作成する機会が非常に増えました。
そのため、Streamlitの機能を使用して簡単に作成できるChatBotについてまとめておきます。

それでは早速見ていきましょう。

Chat elementsの解説

StreamlitのChat Elementsは、チャットアプリケーションの構築を容易にするための機能になります。
主に st.chat_message と st.chat_input の2つが提供されています。
これらの要素と、セッションステートを組み合わせることで、チャット履歴の管理や表示ができ簡単にChatBotの作成ができます。

st.chat_message()

st.chat_message() は、チャットのメッセージを表示する関数です。

  • name:メッセージの送信者を示す名前です。
    “user”や”assistant”などのプリセット値を指定すると、デフォルトのアイコンが適用されます。その他の名前を指定した場合、その名前の頭文字がアイコンとして表示されます。
  • avatar:メッセージの横に表示されるアバターを指定します。デフォルトでは、nameの値に応じて自動的にアイコンが設定されます。

サンプルプログラム

import streamlit as st

# ユーザからのメッセージを表示
with st.chat_message("user"):
    st.write("こんにちは!")

# アシスタントからのメッセージを表示
with st.chat_message("assistant"):
    st.write("こんにちは!ユーザさん。")

st.chat_input()

st.chat_input() は、チャットの入力欄を表示する関数です。
この関数では、ページの下部に入力欄を固定表示し、ユーザーがメッセージを入力して送信することができます。

  • placeholder:入力フィールドが空のときに表示されるプレースホルダーテキストです。
  • key:ウィジェットの一意な識別子として使用されるオプションのキーです。省略された場合、ウィジェットの内容に基づいてキーが自動生成されます。
  • max_chars:入力可能な最大文字数を指定します。
  • disabled:ウィジェットを無効化するかどうかを指定します。デフォルトはFalse。
  • on_submit:メッセージが送信されたときに呼び出されるオプションのコールバック関数です。
  • args:on_submit コールバックに渡されるオプションの引数のタプルです。
  • kwargs:on_submit コールバックに渡されるオプションのキーワード引数の辞書です。

サンプルプログラム

import streamlit as st

# ユーザーからの入力を受け取る
user_input = st.chat_input("メッセージを入力してください")

# 入力が存在する場合、その内容を表示
if user_input:
    st.write(f"あなたの入力: {user_input}")

ChatBotの作成

では、StreamlitのChat elementsを用いて、ChatBotの作成を行います。
まず、全体のコードは以下の通りとなります。

import streamlit as st
import openai
import os
from dotenv import load_dotenv

# 環境変数を読み込む
load_dotenv()
openai.api_key = os.getenv("OPENAI_API_KEY")

# Streamlit アプリのタイトル
st.title("ChatGPT Chatbot")

# セッション状態を初期化
if "chat_history" not in st.session_state:
    st.session_state.chat_history = []

# チャット履歴を表示
for message in st.session_state.chat_history:
    with st.chat_message(message["role"]):
        st.write(message["content"])

# チャット入力
user_input = st.chat_input("質問を入力してください:")

if user_input:
    # セッション状態を更新
    st.session_state.chat_history.append({"role": "user", "content": user_input})

    # チャットメッセージとして表示
    with st.chat_message("user"):
        st.markdown(user_input)

    # ストリーミングモードでAPIを呼び出し
    response = openai.chat.completions.create(
        model="gpt-4o-mini",  # 必要に応じてモデルを変更
        messages=st.session_state.chat_history,
        stream=True  # ストリーミングを有効にする
    )

    # チャンクごとに応答を処理
    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.session_state.chat_history.append({"role": "assistant", "content": full_response })

以下順にコードの解説を行います。

環境変数の読み込み

以下でまず、環境変数の読み込みを行います。
環境変数は.envファイルに保存されているOPENAI_API_KEYを読み込むようにしています。

load_dotenv()
openai.api_key = os.getenv("OPENAI_API_KEY")

セッション状態の初期化

st.session_stateを使い、ユーザーのチャット履歴をアプリの状態として保持するようにしています。
初めてアプリが起動したときに、chat_historyキーが存在しない場合、空のリストで初期化を実施しています。

if "chat_history" not in st.session_state:
    st.session_state.chat_history = []

チャット履歴の表示

過去ユーザが入力した内容と、chatGPTの回答を表示するようにしています。

for message in st.session_state.chat_history:
    with st.chat_message(message["role"]):
        st.write(message["content"])

ユーザー入力の処理

st.chat_inputで、ユーザーが入力できるテキストボックスを表示しています。
ユーザー入力がされるとchat_historyにユーザー入力内容を追加しています。
そして、入力内容を即座にチャットメッセージとして画面に表示しています。

user_input = st.chat_input("質問を入力してください:")

if user_input:
    st.session_state.chat_history.append({"role": "user", "content": user_input})
    with st.chat_message("user"):
        st.markdown(user_input)

OpenAI APIの呼び出し

OpenAI APIの呼び出しを実施します。
今回モデルは、「gpt-4o-mini」を使用しており、ストリーミングを有効にしています。

response = openai.chat.completions.create(
    model="gpt-4o-mini",  # 必要に応じてモデルを変更
    messages=st.session_state.chat_history,
    stream=True  # ストリーミングを有効にする
)

ストリーミング応答の処理

ストリーミング機能をsteamlit上で実現させるためのコードになります。
st.empty()をプレースホルダーとして使用し、応答が完全に送信される前でも、途中経過をリアルタイムで表示するようにしています。

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)  # 最終的な応答を表示

コードを実行した結果がこちらになります。
問題なく回答を生成してくれました。

Perplexityを使用したChatBotの作成

最後に、PerplexityのAPIを用いてWeb検索を行う生成AIを作成しました。
内容としては、OpenAIのAPIではなく、PerplexityのAPIを使うだけでそのほかの部分は変わりありませんので、コードをそのまま載せておきます。

import streamlit as st
from openai import OpenAI
import os
from dotenv import load_dotenv

# 環境変数を読み込む
load_dotenv()
api_key = os.getenv("Perplexity_API_KEY")

# Streamlit アプリのタイトル
st.title("ChatGPT Chatbot with Streaming")

# セッション状態を初期化
if "chat_history" not in st.session_state:
    st.session_state.chat_history = []

# チャット履歴を表示
for message in st.session_state.chat_history:
    with st.chat_message(message["role"]):
        st.write(message["content"])

# チャット入力
user_input = st.chat_input("質問を入力してください:")

if user_input:
    # セッション状態を更新
    st.session_state.chat_history.append({"role": "user", "content": user_input})

    # チャットメッセージとして表示
    with st.chat_message("user"):
        st.markdown(user_input)

    messages = [
        {
            "role": "system",
            "content": (
                "You are an artificial intelligence assistant and you need to "
                "engage in a helpful, detailed, polite conversation with a user."
            ),
        },
        {
            "role": "user",
            "content": (
                user_input
            ),
        },
    ]
    client = OpenAI(api_key=api_key, base_url="https://api.perplexity.ai")

    response = client.chat.completions.create(
        model="llama-3.1-sonar-large-128k-online",
        messages=messages,
        stream=True
    )

    # チャンクごとに応答を処理
    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.session_state.chat_history.append({"role": "assistant", "content": full_response })

まとめ

Streamlitで作成するChatBotの簡単なコードを確認しました。
今回作成したコードのうち、ChatBotのロジック部分はChatGPTやPerplexityにそのまま回答させている形のため、非常に簡易的なものになっています。

ChatBotのロジック部分にRAGを適用したりすることで、応用的なChatBotを作成していくことができますので試してみてください。

今後もしばらくは、ChatBot需要はありそうですので、様々なChatBotアプリを作成していこうと思います。