AIと一緒に話す時代！マルチユーザー✖️マルチAIのチャットを作ってみた

## はじめに
最近ChatGPTを使っていて「コレ、他の人も参加してもらってブレストとかできたら面白いな。。」と思ったので、**AI参加型のマルチチャット - BrAIns**を作ってみました。

~~↓からアクセスできます。~~(停止中)

![IMG_9177.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/649608/3b1ae112-2518-6be4-fdea-f8c82e739a72.jpeg)

![trim.4DD20BD0-3E13-42FF-BBBA-099062276F85.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/649608/5e2d8000-5dda-2118-ef50-bf81cd1be313.gif)


## 機能
主な機能はチャットです。
複数人が同時に参加でき、複数のAIを参加させることができます。
※Streamlitのコミュニティにも投稿したので英語と日本語を切り替えられるようにしています。


### 入出力
Markdownに対応しています。
画像やYoutubeのURLを添付するとインラインで表示できたり、グラフや図の可視化もできます。(Graphviz)
![trim.BFE2F7DF-ECDF-4E25-B0AF-D8180A8E44E0.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/649608/c0b92f27-4575-b5c2-95e3-bbfcc8f3c37f.gif)



### ルームの共有
ルーム名を自由に入力することができるので、ルーム名を共有することで同じルームに参加してもらうことができます。(十分に長い名前などにすればプライベートに利用することが可能かと思います。)

### AIの役割設定
各AIには名前と役割を設定することができ、プリセットとしてブレストや雑談、振り返り、会議進行などを用意しています。また、自動生成も可能です。
![BrAIns_clip_gen.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/649608/a0532240-b88e-9b8e-d3ae-fec42b2a6c54.gif)



## 技術要素など
- アプリケーション周り：Streamlit(PythonのWebフレームワーク)
- データベース：Supabase(FirebaseのOSS実装)
- ホスト先：Streamlit Community Cloud(Streamlit社が提供するクラウド環境)




そこまで特殊なことはしていませんが、いくつか工夫した点などをご紹介できればと思います。
[こちら](https://zenn.dev/nishijima13/articles/ef935cb816235e)の記事を大いに参考にさせていただきました。ありがとうございます！

※コード全文はGitHubをご確認ください。

https://github.com/ppspps824/BrAIns

### Supabaseとの接続
公式から案内も出ているのですが、

https://docs.streamlit.io/knowledge-base/tutorials/databases/supabase

以下のライブラリを利用するとすんなり繋がりました。

https://github.com/SiddhantSadangi/st_supabase_connection

### チャット内容の共有
Straamlitでは別のセッションの情報(他に接続している人の情報)を取得できないので、やり取りの内容を一旦DBに格納して定期的な画面更新とともに全取得&表示することで実現しています。
画面の自動更新は```streamlit_autorefresh```を利用しています。


```python
# 2秒毎に更新
count = st_autorefresh(
        interval = 2 * 1000, limit = None, key = "fizzbuzzcounter"
    )
```

### 右上にフローティングするメニュー
![IMG_9178.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/649608/3b301acc-d1f0-4585-eeb5-f3fdad38da16.jpeg)

Streamlitの標準コンポーネントだけだと対応できないので、以下のようにCSSを追加で設定しています。(フローティングさせたい要素と```st.markdown('<div class="floating_right"></div>', unsafe_allow_html=True)```をコンテナに詰め込むだけでいいのでお手軽です)

```python
def setting_header():
    hide_streamlit_style = """
                    <style>
                    div:has( >.element-container div.floating_right) {
                        display: flex;
                        text-align: right;
                        position: fixed;
                    }
                    
                    div.floating_right {
                        height:0%;
                    }
                  
                    </style>
                    """
    st.markdown(hide_streamlit_style, unsafe_allow_html=True)


    if member_names_text:
        room_info = f"{st.session_state.chat_id} / {st.session_state.brains_action}"
        room_member = f"@{member_names_text}"
    else:
        room_info = f"{st.session_state.chat_id}"
        room_member = "No Members"

    with st.container():
        cols = st.columns([10, 1, 1])
        cols[2].button("🚪", on_click=back_to_main)
        if cols[1].button("🤖"):
            switch_page("brains")
        st.caption(room_info)
        st.caption(room_member)
        st.markdown('<div class="floating_right"></div>', unsafe_allow_html=True)

```

### 参加者一覧の表示
こちらもDBを利用して一工夫しました。
セッションから抜けた。という情報を取得するやり方がわからなかったので、逆にセッションとして存在しているよ。というのを定期的にDBに書き込んで、直近の定期更新の間隔より後の参加者だけ表示するという方法をとっています。


### BrAIns自動生成(ペルソナ自動生成)
ここは単純にAPIにプロンプトを投げて作成してもらっています。レスポンスの形を指定してうまくハマるまで3回トライします。

```python
prompt = [
        {
            "role": "system",
            "content": f"""
Please create 2-3 personas in the same format as the sample below.
Please do not explain the contents, etc., and output only the generated product.
- sample
{sample}
""",
        }
    ]

correct = False
for count in range(3):
    with st.spinner(f"Generating...:{count+1}"):
        result = openai.ChatCompletion.create(
                model=const.MODEL_NAME,
                messages=prompt,
            )
        try:
            gen_ai_set = json.loads(result["choices"][0]["message"]["content"])
            correct = True
            break
        except:
            continue
if not correct:
    st.write("Please Retry😢")

```


## おわりに
LineやSlackなどのグループチャットにBotを参加させるサービスはいくつかありそうですが、複数のAIをカスタマイズしつつ参加させられるというのが差別化になっている？かもしれません。

今後はLangChainと組み合わせて検索したり、独自資料をアップして前提知識として持たせたり、音声でやり取りできるようにするのも面白そうです。

そろそろStreamlitだけで頑張るのをやめた方がいいかな、、と思っているんですが、GitHub連携が便利すぎて離れられません。

※タイトルはChatGPTに考えてもらいました。