Browser-Useで実現する最新情報応答チャット

![IMG_2655.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3784222/786e2dbb-4c9a-fd81-ab56-878ff5bb8407.jpeg)


# はじめに  
前回の記事では、**browser-use**の将来性について述べました。  
**selenium**と**browser-use**を活用することで、Webサイト上の豊富で最新な情報をより効率的に自動取得できる可能性があると考えています。  

https://qiita.com/ogi_kimura/items/2bff25e43ecbfed1a624

今回は、この**browser-use**エンジンを活用して、チャット形式でのやり取りを実現できないかと模索しました。  
その手段として、**streamlit**を使用したプロトタイプの構築に挑戦しています。  
具体的には、チャット形式での使用感や性能（レスポンス時間など）の確認を目的としています。  



# プログラム  
今回のプログラムは、以前私が投稿した**streamlit**に関する記事で紹介したプログラムを基に作成しました。  

https://qiita.com/ogi_kimura/items/7e2e00049ba14aca716a

この記事内の「chroma_streamlit.py」をカスタマイズして、新しい機能を実現しています。  

:::note warn  
この記事や前回のプログラムをご覧になった方は、**OPENAI API KEY**がプログラム内に記載されていないことに気づかれたかもしれません。  
これは、**GitHub**にプログラムをPUSHしようとした際、「API KEY情報がプログラムに含まれている」としてエラーが発生したためです。  

その対策として、環境変数に「OPENAI_API_KEY」を新たに定義し、そこにAPIキー情報を設定しました。  
皆様も同様に、環境変数の設定を行うことをお勧めします。  

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3784222/0e1a4596-3dfe-9fb6-5a96-b6bdd5b39440.png)  
:::  



## プログラムソースコード  
以下にプログラムソースコードを示します。同じ内容は、**GitHub**リポジトリにもアップロードされています（`app2.py`）。ぜひダウンロードしてお試しください。

https://github.com/kimkimkim5/browser-use


```python:app2.py
import streamlit as st
import asyncio
import sys

from langchain_openai import ChatOpenAI
from browser_use import Agent
import asyncio

if sys.platform.startswith("win"):
    asyncio.set_event_loop_policy(asyncio.WindowsProactorEventLoopPolicy())
    
STR_JAPANESE = "日本語で訳して。"

async def main(agent):
    result = await agent.run()
    print(result)
    print('\n')
    print('【ここから】')
    if len(result.history) > 0:
        return result
    return ''

st.title("BROWSER-USE システム")

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"])

input_message = st.chat_input("準備ができました。メッセージを入力してください。")
if input_message:
    st.session_state.messages.append({"role": "user", "content": input_message})
    print(f"入力されたメッセージ: {input_message}")
    
    # ===== ユーザ側マークダウン =====
    with st.chat_message("user"):
        st.markdown(input_message)

    # ===== アシスタント側マークダウン =====
    with st.chat_message("assistant"):
        agent = Agent(
            task=input_message + STR_JAPANESE,
            llm=ChatOpenAI(model="gpt-4o-mini"),
        )
        
        # 非同期関数で結果を取得
        try:
            result = asyncio.run(main(agent))
            
            # デバッグ用に結果を確認
            print(f"Result history: {result.history}")
            
            # データが存在するか確認
            if not result.history or len(result.history) == 0:
                response = "エージェントから結果を取得できませんでした。"
            else:
                # 安全にデータを取り出す
                last_result = result.history[-1]  # 最後の結果を取得
                if hasattr(last_result, 'result') and last_result.result:
                    response = last_result.result[0].extracted_content
                else:
                    response = "結果の形式が予期したものと異なります。"
        except Exception as e:
            response = f"エラーが発生しました: {e}"
            print(f"エラー詳細: {e}")
        
        # アシスタントのレスポンスを表示
        st.markdown(response)
        st.session_state.messages.append({"role": "assistant", "content": response})
```


## 特筆する内容  
今回のプログラムでは、**browser-use**を**streamlit**に適用する際、いくつかの課題に直面しました。特に以下の2点について工夫を行いました。

### 1. 非同期処理  
**streamlit**の仕様上、非同期処理を用いる必要がありました。そのため、`run`メソッドの呼び出し部分を別関数に分離して非同期処理を実装しました。

```python
async def main(agent):
    result = await agent.run()
    print(result)
    print('\n')
    print('【ここから】')
    if len(result.history) > 0:
        return result
    return ''
```

呼び出し側もこれに対応する形で、`asyncio.run`を用いて`main`関数を実行しました。

```python
result = asyncio.run(main(agent))
```

### 2. Windows環境での非同期処理  
Windows環境で実行する際、上記の非同期処理ではエラーが発生しました。そのため、エラー内容を**ChatGPT**に相談したところ、以下のコードを追加するよう提案されました。この変更により、エラーを解消できました。

```python
if sys.platform.startswith("win"):
    asyncio.set_event_loop_policy(asyncio.WindowsProactorEventLoopPolicy())
```

このコードでは、**asyncio**ライブラリのイベントループポリシーをWindows専用のプロアクターイベントループに設定しています。これにより、Windows特有の非同期処理の問題を回避することができました。




# 実行  
では、プログラムを実行してみます。

```dosbatch
python -m streamlit run app2.py
```

### 初期画面  
久しぶりに**streamlit**を動かしてみました。以下が初期画面です。  
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3784222/31177542-3a9e-5721-ba7e-7ed00104d1a5.png)

### 質問例  
「2024年の流行語大賞は？」と質問してみました。  
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3784222/7e700f6c-f38c-511a-75a6-39964a10591a.png)

結果が英語で表示されましたが、内容は日本語の「ふてほど」に対応しているようです。  
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3784222/93ef9577-55bc-f49b-bae0-1b44ed3107bf.png)

### 性能について  
実行時間は約20秒程度でした。  
ただ、チャット形式の対話としては、レスポンスがやや遅く感じられます。



# おわりに  
**browser-use**を**streamlit**で活用し、チャット形式で質問と回答ができる仕組みを構築できることが分かりました。ただし、レスポンスが遅いため、チャット形式よりも、処理を一括で実行する「バッチ処理」向きかもしれません。

また、最近のクラウドソーシング界隈では、生成AIを使った記事やブログの自動作成が注目されています。この流れにおいて、**browser-use**はLLMが苦手とする「最新情報に基づく回答」を補完するツールとして活躍が期待できます。

さらにプログラムを工夫すれば、ユーザの質問に応じて、**function calling**を使い、**browser-use**と**RAG（Retrieval-Augmented Generation）**の適切な切り替えが可能になると考えます。

最後までお読みいただき、ありがとうございました！