OpenAI Assistants APIで生成したファイルをダウンロードする

2023.12.31

2023年11月のアップデートで、OpenAI社はChatGPTを利用したAIアシスタントを構築できる Assistants APIを追加しました。

2023年12月現在はまだベータ版ですが、このAPIを利用すると、ChatGPT Web版で利用できた、Code Interpreterの機能をプログラム上で実行させることができます。 Web版のCode InterpreterではプログラムをChatGPT上で動かし、結果として生成されたファイルを受け取ることができました。当然このAPIを使っても、ファイルを生成させることができます。

ただ、では生成したファイルはどうやったらダウンロードできるのでしょう?

返ってくるメッセージの本文にダウンロードのパスが記載されるのではないの?と思われるかもしれません。しかし、私が試しにダミーデータを生成する指示を与えるプログラムを作成してみたところ、返ってきたメッセージの本文は次のようなものでした:

ダミーデータを10件生成し、CSV形式で出力しました。以下のリンクからダウンロードできます。

[ダウンロード dummy_data.csv](sandbox:/mnt/data/dummy_data.csv)

sandbox:/mnt/data/ って言われてもどこやねん、となってしまいますね。。

ということで自分が詰まったので、その対処法をメモとしてブログに残しておきます。

結論

  • 結果として返ってくるメッセージデータに、生成したファイルのID(file_id)が含まれているので、これを取り出す
  • files.contentメソッドでfile_idを引数として指定することでファイルを取得する

これだけの話ではありますが、以下、実際のコードを交えて説明します。

まずはAssistants APIの基本的な処理のおさらい

Assistants APIの基本的な処理の流れは以下の通りです。 - アシスタントAIを作成 - アシスタントAIに指示するスレッドを作成 - 結果のデータを取得

以下、簡単なダミーデータを生成するコードのサンプルを示します。

import requests
import time
from openai import OpenAI

# アシスタント作成処理
client = OpenAI()
assistant = client.beta.assistants.create(
    name="My Assistant",
    instructions="あなたはデータ処理を行うAIアシスタントです。命令に従ってCSVを生成してください。",
    tools=[
        {"type": "code_interpreter"},
    ],
    model="gpt-4-1106-preview"
)

# スレッド作成・実行
thread = client.beta.threads.create()
message = client.beta.threads.messages.create(
    thread_id=thread.id,
    role="user",
    content="「品物」「単価」のフィールドを持つダミーデータを10件生成してください。品物の名前は日本語の果物名として、エントリ内でユニークとしてください。単価は日本円として、100円〜1000円の範囲で設定してください。結果はCSV形式で出力してください。"
)
run = client.beta.threads.runs.create(
  thread_id=thread.id,
  assistant_id=assistant.id
)

# 結果が生成される(Statusがcompletedになる)まで待つ
while run.status != 'completed':
    run = client.beta.threads.runs.retrieve(
      thread_id=thread.id,
      run_id=run.id
    )
    print(run.status)
    time.sleep(5)

# 出力メッセージの取得
output_messages = client.beta.threads.messages.list(
    thread_id=thread.id
)
print(output_messages.data) #debug用

client.beta.assistants.create()でアシスタントを作成、続いてclient.beta.threads.create()でスレッドを作成。スレッド内で指示するメッセージをclient.beta.threads.messages.create()で定義して、client.beta.threads.runs.create()で実行しています。

途中の while run.status != 'completed'から始まる処理は、スレッドの実行ステータスを確認して、ChatGPTの処理が終わるまで待つ処理です。ステータスがcompletedになると、出力結果が取り出せるようになります。ここでは出力結果をoutput_messagesに格納しています。

ファイル情報の取得

出力結果を格納したoutput_messages.data内に含まれるデータは以下のような感じです。

[ThreadMessage(id='xxx', assistant_id='xxx', content=[MessageContentText(text=Text(annotations=[TextAnnotationFilePath(end_index=118, file_path=TextAnnotationFilePathFilePath(file_id='xxx'), start_index=79, text='sandbox:/mnt/data/dummy_fruits_data.csv', type='file_path')], value='ダミーデータを10件生成し、CSV形式で出力しました。以下のリンクからダウンロードできます。\n\n[ダウンロード dummy_data.csv](sandbox:/mnt/data/dummy_data.csv)'), type='text')], created_at=1703952985, file_ids=['file-xxx'], metadata={}, object='thread.message', role='assistant', run_id='run_xxx', thread_id='thread_xxx'), (以下省略)]

valueに含まれるのが回答の本文ですが、先ほども説明したようにこの回答に含まれるパスはダウンロードが可能なパスではありません。 実際に重要なのは、このデータ内に別に含まれているfile_idsというフィールド内のIDとなります。このIDを指定してclient.files.content(file_id)を実行することで、実際のファイルのデータを取得することができます。以下、上記を踏まえた先ほどのコードの続きです。output_messages.data内では複数のファイルIDが含まれる可能性があるため、それらを抽出しfor文で順次処理しています。

# 出力ディレクトリの指定
output_dir = "results"

# 生成されたファイルのID情報をメッセージから抽出
file_ids = [
        file_id
        for m in output_messages.data
        for file_id in m.file_ids
    ]
print(file_ids) #debug用

# 取得したファイルIDをもとにローカルにファイルを書き込み
for file_id in file_ids:
    output_path="{}/{}.csv".format(output_dir, file_id)
    write_file_data = client.files.content(file_id)
    file_data_bytes = write_file_data.read()
    with open(output_path, "wb") as file:
        file.write(file_data_bytes)

おわりに

というわけで、OpenAI assistant APIを利用したファイルのダウンロードの方法の紹介でした。説明したようにメッセージの本文にあるパス情報が有効ではない点にやや戸惑いますが、ベータ版ですので今後改善するかもしれません。このAPIでファイルを生成・ダウンロードする機能は非常に便利だと思いますので、試してみてはいかがでしょうか。ではでは。