Gradioを使用してAmazon Bedrockの簡単なインターフェイスを構築する

## 概要

LLMのデモで見かけるフレームワークにGradioがある。
調べた感じ簡単なコードの記載でLLMのフロント部分を構築することが出来そう。
用途としてエンドユーザ向けというより、動作確認などデモの用途で利用することが多そうだが、内部向けにさくっと公開するなど、フロントをリッチに作りこむ必要がない場合に利用できそう。
普段業務で利用する機会の多い、AWSのサービスAmazon Bedrockと組み合わせて簡単なチャットアプリケーションを作成してみる。

調査しつつ、備忘の意味もこめて記載する。


## Gradioとは

https://github.com/gradio-app/gradio

GradioはPythonのオープンソースライブラリで、機械学習アプリのWebインターフェースを簡単に構築・共有できる。
数行のコードでUIを作成できる。（gr.Interfaceを使って関数に入力・出力コンポーネントを指定してlaunch()するだけで動作する）
テキストボックスや画像入力、スライダーなどのUIコンポーネントが用意されていて利用できる。
機械学習モデルや任意のPython関数をバックエンドに設定可能。
Gradioの大きな利点は手軽な共有とデプロイ。作成したアプリはJupyter/Colabノートブック上に埋め込んだり、ワンクリックで公開リンクを生成共有することが可能（自分のPC上で動いているGradioアプリをリモートのユーザーがブラウザ経由で操作できる）。
また、HuggingFaceが提供するSpacesにワンクリックでデプロイして公開することもできるらしい。


## 最新バージョン
https://huggingface.co/blog/gradio-5

Gradioは積極的にアップデートが重ねられており、2024年10月に「Gradio 5.0」がメジャーリリースされた。
主な変更点は以下

- **パフォーマンス改善:**  
SSR導入で読み込みがほぼ瞬時になり、「Loading...」が不要になった。重いモデルでも初期表示が速く、ユーザー体験が向上。

- **UIデザイン刷新とテーマ追加:**  
主要コンポーネントがモダンな外観に一新され、複数のビルトインテーマが追加された。コードを変更せずにスタイリッシュな見た目が適用でき、古さが改善。

- **リアルタイム処理とストリーミング対応:**  
低遅延なストリーミング機能が強化され、WebSocketやWebRTCを活用して映像や音声のリアルタイム処理がスムーズになった。公式ドキュメントには、Webカメラ映像での物体検出、音声認識・合成、対話型チャットボットの例が豊富に紹介されている。

- **AIによるコード生成支援 (AI Playground):**  
自然言語のプロンプトからGradioアプリのコードを自動生成・提案する実験機能が追加され、初心者でも容易にアプリ構築ができる。

- **セキュリティ強化:**  
独立監査に基づく指摘事項がGradio 5に反映され、安全なデフォルト設定でアプリ公開が可能になった。企業利用に耐えるセキュリティ水準が確保されている。

## ChatInterfaceを使用してチャット形式の画面を作る
https://www.gradio.app/docs/gradio/chatinterface

作成したいのは簡素なチャットベースの画面になるので`ChatInterface`を使用する。
`ChatInterface`は、チャットボットのUIを作成するためのGradioの高レベルのクラス。
数行のコードでチャットボットモデルの周りのWebベースのデモを作成することができる。

```python
# デモアプリ 入力するとYes,Noをランダムに返す
import random
import gradio as gr

def random_response(message, history):
    return random.choice(["Yes", "No"])

demo = gr.ChatInterface(random_response, type="messages", autofocus=False)

if __name__ == "__main__":
    demo.launch()
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1031771/e671383d-8c74-45ff-b9b6-c4a458bf705a.png)

上記は入力するとYes,Noをランダムに返すデモのコードと、生成される画面。
上記の`random_response`をAWS SDKに置き換えてAmazon BedrockのAPIをコールするように変更していく。


## Amazon Bedrockを呼び出してみる
PythonでのAWS SDKとしてboto3を使用する。

まず、画面部分は上記のデモアプリケーションを拡張して実装。
GradioではChatInterfaceの中でhistoryという変数でこれまでのユーザーとアシスタントのやり取りの履歴を管理してくれる。
（コールバック関数fnの第二引数で各要素が`[user_message, assistant_response]`のリスト）
これだと、BedrockのConverseAPIを実行する場合に渡す`messages`オブジェクトと形式が違うので別途Stateのコンポーネントを追加して、BedrockのConverseAPIに渡すメッセージ群はこちらで管理するようにした。
```python:frontend.py
import gradio as gr
from bedrock import get_bedrock_response
from dotenv import load_dotenv

def submit_text(input_text):
    # Call the bedrock API and get the response
    response = get_bedrock_response(input_text)
    return response

def chat_interface(user_message, history,state):
    if history is None:
        history = []
    if state is None:
        state = []
    response,new_state = get_bedrock_response(user_message, history,state)
    return response,new_state

chat_state_component = gr.State(value = None)

demo = gr.ChatInterface(
    fn=chat_interface,
    title="Amazon Bedrock Chat",
    additional_inputs=[chat_state_component],
    additional_outputs=[chat_state_component],
    description="チャット形式でAmazon Bedrockとの連携を行うインターフェースです。"
)

if __name__ == "__main__":
    load_dotenv()
    demo.launch()

```

以下はboto3を使用してAmazon BedrockのAPIを呼び出す部分
```python:bedrock.py
import os
import json
import boto3

def get_bedrock_response(input_text, history,state):
    """
    AWS認証情報を用いてAmazon Bedrock Converse APIを呼び出す。
    """

    BEDROCK_MODEL_ID = "us.amazon.nova-pro-v1:0"
    system = [{"text": "You are a helpful AI assistant."}]
    state.append({
                "role": "user",
                "content": [{"text": input_text}]
            })
    
    messages = state.copy()

    try:
        client = boto3.client("bedrock-runtime")
    except Exception as e:
        return f"Error creating boto3 client: {e}", history

    try:
        response = client.converse(
            modelId=BEDROCK_MODEL_ID,
            messages=messages,
            system=system,
            inferenceConfig={
                "maxTokens": 300,
                "topP": 0.1,
                "temperature": 0.3
            }
        )
        output_message = response['output']['message']
        messages.append(output_message)
        response_text = ""
        for content in output_message['content']:
            if 'text' in content:
                response_text += content['text'] + "\n"
        # 履歴はタプル形式にして追加する
        # history.append((input_text, response_text))
        return response_text,messages
    except Exception as e:
        return f"Error invoking Bedrock Converse API: {e}",messages

```

AWSの認証情報は.envファイルに記載しておくことで、実行時にdotenvで自動的にセットされる。

上記のfrontend.pyを実行した状態で`http://localhost:7860/`にブラウザアクセスすることでチャット画面にアクセスできた。
以下のように過去の会話履歴も反映されたチャットが可能。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1031771/b0bbdc99-7242-4cf1-bcdc-2c713982a98f.png)


## 所感
ほとんど作り込まずにAmazon Bedrockをチャットベースで利用できる画面を準備できた。
Amazon Bedrockナレッジベースなど内部向けのデータを反映させたモデルのデモなどで、
AWSアカウントを持っていない非エンジニア方向けに公開する時などに利用できそうだなと感じた。
（どこにホストするとかは考えないといけないけど）