こんにちは!TC3のAIチームの梅本(@mumeco_ml)です。LLM Advent Calendar 2023の21日目の記事です。

皆さんは普段ChatGPT使っていますか?使っている方の中にはこの機能を自分で作ってみたいと思う方も多いのではないでしょうか。

今日は最近流行りのAIチャット機能を持つブラウザアプリをたった1時間で作ることにチャレンジしてみます。コード類はGithubリポジトリにもアップロードしているので、コーディングが面倒な方はこちらも活用ください。

事前準備

  • Python環境
    • 今回はPython 3.10を使いますが、3.11などでも多分大丈夫です
    • インストールしてない方は”python インストール OS名”で検索してください
  • Poetry(できれば)
    • ライブラリの仮想環境を作ります
    • インストールしてない場合は公式に方法が書いてあります
    • インストールが難しい場合はpoetryは使わずにpipを使ってください

Webアプリの作成

まず最初にWebアプリを作っていきます。Webアプリには爆速でWebアプリを作ることができるStreamlitというライブラリを利用します。まずは今回のコードファイルを含めるフォルダ(この例ではllm-app)を作ります。

mkdir llm-app
cd llm-app

次にpoetryの初期化コマンドを実行します。pipの場合は無視してください。
基本は入力せずにenterで大丈夫ですが、Would you like to define~?という項目にはnoと書いてenterします。

poetry init

Package name [llm-app]:  
Version [0.1.0]:  
Description []:  
Author [user <email>, n to skip]:  
License []:  
Compatible Python versions [^3.11]:  

Would you like to define your main dependencies interactively? (yes/no) [yes] no
Would you like to define your development dependencies interactively? (yes/no) [yes] no

Do you confirm generation? (yes/no) [yes] 

すると、pyproject.tomlというファイルが作成されると思います。次にライブラリを追加します。pipの方はpip installでインストールしてください。streamlitは簡単にWebアプリを作ることができるライブラリで、openaiはOpenAIのAPIを簡単に利用きるライブラリです。

poetry add streamlit openai

次に最小限のWebアプリを作ってみましょう。ルートフォルダ直下(llm-app)にapp.pyというファイルを作って以下のように記載してください。

import streamlit as st

st.title("Hello World")

そしてターミナルから以下のコマンドを実行します。するとURLが表示されるのでそこにアクセスするとHello Worldという単語が表示されたページが表示されます。たった1行でブラウザアプリが作れてしまいました。

poetry run streamlit run app.py

  You can now view your Streamlit app in your browser.

  Local URL: http://localhost:8501

次にそのままapp.pyを編集してst.titleを消して以下のように変更してみます。

import streamlit as st

st.set_page_config(page_title="AIチャットアプリ")

st.subheader("AIとチャットする")
st.chat_message("assistant").write("コンニチハ")
st.chat_message("user").write("こんにちは")

st.chat_input()

ブラウザで先ほどのアプリに戻ると、右上に Source file changed Return Always Returnという表示が出てくるので、Always Returnを押します。これでファイル内容を変更したら自動的にwebページも更新されるようになります。以下のようにチャットアプリのざっくりとした部分ができてしまいました。

コードを少し解説すると

  • st.set_page_config
    • ページ全体の情報を設定します。ブラウザのタブ名などが変わります。
  • st.subheader
    • 大きめの見出し文字を表示
  • st.chat_message
    • チャット形式の文字を表示
  • st.chat_input
    • チャットの入力欄を表示

しかし、この状態でメッセージを入力してエンターを押してもチャットは増えないので、入力したらメッセージが表示されるようにしましょう。st.chat_inputの行を以下のように修正します。

if message := st.chat_input():
    st.chat_message("user").write(message)

すると、文字を入力してエンターを押すとチャットメッセージが増えます!しかし、2回やると先ほどの内容が上書きされてしまいます。これはstreamlitのレンダリング方法が関係していて、streamlitではユーザーからのインタラクトがあると再度pythonスクリプトを読み込むような動作をします。なので、このスクリプトで考えると、初回はmessage := st.chat_input()が空だったのでif文に入らずst.chat_messageが呼ばれないので表示されなかったのが、文字を入力するとif文に入りst.chat_messageが呼ばれ、入力した文字が表示されるというカラクリでした。

つまり、過去に入力したチャットを表示し続けるには過去の入力内容を保持必要があるのですが、streamlitの再レンダリングの際にローカル変数はリセットされてしまいます。そこで再レンダリングでもリセットされない機能st.session_stateがstreamlitには実装されています。st.session_stateを利用してapp.pyを再度書き直してみます。

import streamlit as st

st.set_page_config(page_title="AIチャットアプリ")

if st.session_state.get("chat_messages") is None:
    st.session_state["chat_messages"] = []

st.subheader("AIとチャットする")

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

if message := st.chat_input():
    st.chat_message("user").write(message)
    st.session_state["chat_messages"].append({"role": "user", "content": message})

ポイントを簡単に説明すると

  • 5,6行目でsession_stateの初期化をしています。
  • 10,11行目で過去に保持しているチャット内容をfor文で1つづつ表示させます。
  • 15行目でチャットに入力があれば、その情報をsession_stateへ追加します。

これで、ユーザーが入力するとその内容が表示されるところまでできました。これだけだと味気ないので、AIの簡単な返信が来るように変えてみたいと思います。llm.pyというファイルを作成し、以下のようにAIの返事をする関数を定義します。

def llm_response(chat_history):
    response = "ハイ"
    return response

そして、app.pyを以下のように変更します。ポイントとしてはユーザーの入力が来た後に、その内容をai_response関数へ入れAIの返事を取得し、chat_messageでの表示とsesstion_stateへの保存をします。

import streamlit as st

from llm import llm_response

st.set_page_config(page_title="AIチャットアプリ")

if st.session_state.get("chat_messages") is None:
    st.session_state["chat_messages"] = []

st.subheader("AIとチャットする")

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

if message := st.chat_input():
    st.chat_message("user").write(message)
    st.session_state["chat_messages"].append({"role": "user", "content": message})

    ai_response = llm_response(st.session_state["chat_messages"])
    st.chat_message("assistant").write(ai_response)
    st.session_state["chat_messages"].append({"role": "assistant", "content": ai_response})

すると以下のようにとりあえずハイとAIが答えてくれるWebアプリができました。

以上でWebアプリ側の作成は完了です。

AIのチャット返事を実装

先ほどまでの実装ではAIは”ハイ”としか返事しません。これをユーザーの入力した内容に応じて変化させることができればAIチャットアプリの出来上がりです。まずはAI部分の実装にOpenAIのAPIを使うので、登録してない人は登録してAPI Keyを取得します。登録の方法などはこの辺の記事が参考になります。

llm.pyを以下のように編集します。APIキーの入力を忘れないようにしてください。

from openai import OpenAI

client = OpenAI(api_key="ここにAPIキーを入力")
model = "gpt-3.5-turbo"

def llm_response(chat_history):
    response = client.chat.completions.create(model=model, messages=chat_history)
    result = response.choices[0].message.content
    return result

するとこれだけでChatGPTのようにあなたのチャットに応じて返信してくれるAIがWebアプリに実装できてしまいます。

これだけだと、ただChatGPTを自分のWebアプリに実装しただけなので自分だけのAIとは言えないですよね。なので、プロンプトをカスタムしてみましょう。prompt.pyというファイルを作成し、以下のようにAIに鬼軍曹さんになりきってもらえるようにプロンプトを作ります。

SYSTEM_PROMPT = {"role":"system", "content":"あなたは厳しい軍隊の鬼教官です。鬼教官の話し方でユーザと会話してください。"}

そして、app.pyでそのプロンプトをsession_stateの初期値に設定します。

import streamlit as st

import prompt
from llm import llm_response

st.set_page_config(page_title="AIチャットアプリ")

if st.session_state.get("chat_messages") is None:
    st.session_state["chat_messages"] = [prompt.SYSTEM_PROMPT]

st.subheader("AIとチャットする")

for message in st.session_state["chat_messages"]:
    if message["role"] == "system":
        continue
    st.chat_message(message["role"]).write(message["content"])

if message := st.chat_input():
    st.chat_message("user").write(message)
    st.session_state["chat_messages"].append({"role": "user", "content": message})

    ai_response = llm_response(st.session_state["chat_messages"])
    st.chat_message("assistant").write(ai_response)
    st.session_state["chat_messages"].append({"role": "assistant", "content": ai_response})

すると以下のように鬼軍曹と話すことができるAIチャットアプリができました。

まとめ

この記事では爆速でWebアプリを作ることができるStreamlitとOpenAI APIを利用したAIチャットアプリの作り方を解説しました。今回はOpenAI APIを用いたAIの実装でしたが、次回はローカルLLMを用いた実装方法をご紹介予定です。