🔖

Azure OpenAI Assistants APIを使って可視化する

2024/09/10に公開

やること

・Assistants APIの基本的な使い方をおさえる
・CSVファイルを与えてデータの可視化を試みる

参考記事

https://zenn.dev/microsoft/articles/533488db14e442#4.-[補足]-thread-や-assistant-の削除

データセット

CSVのサンプルとして以下サイト内の成績一覧表を用いました。
https://www.icon-co.jp/csvlapot/dl/help/samplecsv/1_seiseki.zip

前提

・AOAIにgpt-4o-mini(2024-02-15-preview)をデプロイ済み
・所定のディレクトリにCSVファイルを格納しておく

依存ライブラリ

pip install openai==1.20.0

※何も考えないで最新のバージョンでやるとエラーが起きます。
https://zenn.dev/headwaters/articles/59da027d895888

基本的な流れ

それでは実際にAssistants APIを利用する流れを見ていきます。

STEP1:AOAIクライアントの作成

client = AzureOpenAI(
    api_key=os.getenv("AZURE_OPENAI_API_KEY"),
    api_version=os.getenv("AZURE_OPENAI_API_VERSION"),
    azure_endpoint=os.getenv("AZURE_OPENAI_ENDPOINT")
)

STEP2:Assistantの作成

def create_data_analyst_assistant(
        client,
        csv_file_id,
        assistant_name="Data Analyst Assistant", 
        model_name="dev-gpt-4o-mini"):

    assistant_1 = client.beta.assistants.create(
        name=assistant_name,
        instructions=f'''
        あなたは優秀なデータアナリストです。
        ある学級の試験成績のCSVファイルを使って、データを分析してください。
        また、分析結果を可視化してください。
        ''',
        tools=[{"type": "code_interpreter"}],
        model=model_name,
        file_ids = [csv_file_id]
    )

    for k, v in dict(assistant_1).items():
        print(f"{k}: {v}")
    
    return assistant_1

STEP3:Threadの作成・実行

def create_and_run_thread(client, csv_file_name, assistant_1):
    # Threadの作成
    thread_1 = client.beta.threads.create()

    # Messageの追加
    message_1 = client.beta.threads.messages.create(
        thread_id=thread_1.id,
        role="user",
        content=f'''
        {csv_file_name}の分析結果を可視化してください。
        棒グラフやヒストグラムのタイトルやバーの表示は英語で問題ありません。
        可視化結果はファイルに保存してください。
        '''
    )

    # Threadの実行
    run1_1 = client.beta.threads.runs.create(
        thread_id=thread_1.id,
        assistant_id=assistant_1.id
    )

    return thread_1, message_1, run1_1

STEP4:AOAIからの結果待ち

def wait_for_assistant_response(client, thread_id, run_id):
    while True:
        time.sleep(5)
        run = client.beta.threads.runs.retrieve(
            thread_id=thread_id,
            run_id=run_id
        )
        status = run.status
        if status in ["completed", "cancelled", "expired", "failed"]:
            print(status)
            break

STEP5:結果の出力

def retrieve_and_print_messages(client, thread_id, verbose, out_dir=None):
    # メッセージリストをクライアントから取得
    messages = client.beta.threads.messages.list(thread_id=thread_id)

    # ユーザーとアシスタントの役割を表示する辞書
    display_role = {"user": "User query", "assistant": "Assistant response"}
    prev_role = None  # 前回のメッセージの役割を保存する変数

    # verboseモードが有効な場合、会話の開始を表示
    if verbose:
        print("\n\nCONVERSATION:")

    # メッセージを逆順で処理
    for message_data in reversed(messages.data):
        # 前回のメッセージがアシスタントで現在がユーザーなら区切りを表示
        if prev_role == "assistant" and message_data.role == "user" and verbose:
            print("------ \n")
        # 各メッセージの内容を処理
        for message_content in message_data.content:
            txt_val = None
            # メッセージがテキストの場合、テキストを取得
            if message_content.type == "text":
                txt_val = message_content.text.value
            # メッセージが画像ファイルの場合、画像データを保存する
            elif message_content.type == "image_file" and out_dir is not None:
                image_data = client.files.content(message_content.image_file.file_id)
                out_dir_path = Path(out_dir)
                if out_dir_path.exists():
                    image_path = out_dir_path / f"{message_content.image_file.file_id}.png"
                    with image_path.open("wb") as f:
                        f.write(image_data.read())
            # テキストが存在する場合、メッセージの役割と内容を表示
            if txt_val is not None and verbose:
                print(f"{display_role.get(message_data.role, 'Unknown')}:\n{txt_val}")
        prev_role = message_data.role
    return messages

実行コード

if __name__ == "__main__":
    csv_file = client.files.create(
        file=open("<your csv file>", "rb"),
        purpose='assistants'
)
    csv_file_id = csv_file.id
    csv_filename = csv_file.filename
    out_dir = "<your directory>"
    # Assistantの作成
    assistant_1 = create_data_analyst_assistant(client, csv_file_id)
    # Threadの作成と実行
    thread_1, message_1, run1_1 = create_and_run_thread(client,csv_filename, assistant_1)
    # AOAIからの結果待ち
    wait_for_assistant_response(client, thread_1.id, run1_1.id)
    # 結果の出力
    retrieve_and_print_messages(client, thread_1.id, True, out_dir)

結果の可視化

コメントなど

 なんとか可視化まではこぎつけましたが、グラフのバーが一部文字化けしてしまっています。これは色々調べてメッセージに追加するプロンプトで「英語でいいよ」と言葉を添えたり、japanaese-matplotlibをクライアントに渡すとかを試しても、結局データセットのカラム名が日本語の場合きちんと表示させるのは難しいようです(上手く表示させる方法知っている人いたら教えてください・・・)
 また、AOAIのAssistants APIは9月4日時点でプレビュー版であるため、今後仕様変更に伴い上記の書き方も変わるかもしれません。(https://learn.microsoft.com/ja-jp/azure/ai-services/openai/concepts/assistants )ただでさえ挙動が不安定(何回かに1回はちゃんと出力しない)であることに加えて、openaiやSDKのバージョンによってAssistantやThreadに与える引数も変わるようなので、要注意です。ただ、それを差し置いてもCSVデータを渡すだけでデータ分析の一端を担ってくれる便利なツールだなと感じたので、今後の進展に期待したいところです。

ヘッドウォータース

Discussion