OpenAI Assistants APIを利用する際の注意点をまとめてみた

2024.02.13

OpenAIのAPIとして11月にリリースされたAssistants APIを利用すると、アップロードしたファイルを参照させたやり取りをしたり(Retrieve)、コードを生成して実行結果させたり(Code Interpreter)するアシスタントAIを簡単に作成できます。

このAssistants APIでは、内部的にはどうやらアシスタントAIの実行環境のサンドボックスを作成するようなのですが、その関係もあってかやや扱いや料金に特殊な点があります。というわけで、この記事では注意点を簡単にまとめてみたいと思います。請求やセキュリティにも関わってくるので、Assistants APIを利用する方はぜひ目を通してみてください。

TL;DR

  • 作成したアシスタントは明示的に削除しない限り残り続ける(ので、不要であれば明示的に削除しましょう)
  • アシスタントのデータは同Organization内で共有される(ので、Organizationを適切に分割するなど、セキュリティに配慮しましょう)
  • 「Retrieval」ツールを有効としたアシスタントでは、アップロードしたデータを保持する料金がかかる(一時的にファイルを利用したいだけならばCode Interpreterの利用を検討しましょう)
  • アシスタントで同じスレッドをずっと利用しているとトークンが累積し利用料金が上がっていく(ので、あまり長期に使い回さないよう配慮しましょう)

作成したアシスタントは明示的に削除しない限り残り続ける

アシスタントは、以下のようにプログラム内でclient.beta.assistants.create()をコールすると作成することができます。

client = OpenAI()
assistant = client.beta.assistants.create(
    name="Eguchi_test_assistant",
    instructions="あなたはアシスタントAIです。アップロードされたファイルの情報に従って回答を行ってください。",
    tools=[
        {"type": "retrieval"}, 
    ],
    model="gpt-4-1106-preview"
)

このアシスタントはプログラム内で作成するインスタンスなので、プログラムが終了するとインスタンスも破棄されるのかな・・・と思いきや、作成されたアシスタントは明示的に削除しない限り残ります。 そのOrganizationに現在存在するアシスタントは、Web UI上で確認ができます。以下のURLにアクセスしてみてください。

https://platform.openai.com/assistants

なので、検証と思ってアシスタントをcreateする処理を入れたスクリプトを何度も実行していると、アシスタントがたくさん生成されて残っている・・・という状態になってしまいます。上のスクリーンショットでも同じ名前・指示のアシスタントが複数作成されていますね。

この仕様のメリットとしては、アシスタントを別のプログラムで使い回せる、という点などがあります。なので別に問題というわけではないのですが、この後説明するように、

  • アシスタントのデータは同Organization内で共有される
  • 「Retrieval」ツールを有効としたアシスタントでは、アップロードしたデータを保持する料金がかかる

という点は注意して利用する必要があります。

アシスタントのデータは同Organization内で共有される

まず、アシスタント自身や、APIによって作成されたファイルその他のデータは、同一のOrganization全体で共有されます。この点については、アシスタントの動作の仕様を説明した公式ドキュメントのページでは、以下のように説明があります。

Data access guidance

Currently, assistants, threads, messages, and files created via the API are scoped to the entire organization. As such, any person with API key access to the organization is able to read or write assistants, threads, messages, and files in the organization.

現在、APIを介して作成されたアシスタント、スレッド、メッセージ、およびファイルのスコープはOrganization全体です。そのため、OrganizationにAPIキーでアクセスできる人であれば誰でも、組織内のアシスタント、スレッド、メッセージ、ファイルを読み書きできます。

実際、上で示したWeb UIによるアシスタントの一覧ページでは、Organization内で他のメンバーが作成したアシスタントも表示されます。また、少なくとも2024年2月現在は、「Reader」権限のユーザーであっても、(自分が作成した以外のものでも)アシスタントの削除が可能なようです。

このため、本番のプロジェクトでアシスタントAIを動かしていて、Organizationにそのプロジェクトに関係ないメンバーが参加している場合、想定外のアクセスや誤削除などが発生するおそれがあります。これを避けるには、適切にOrganizationを使い分ける必要があります。(その他、OpenAI公式の推奨の対応がありますが、後述します)

なお、アシスタントで利用しているファイルについては、

https://platform.openai.com/files

で確認ができます。

仕様に従い、他のメンバーがアップロードしたファイルが確認できます。またアシスタント同様、2024年2月現在ではどの権限のユーザーでもファイルの削除が可能なようです(上の画像の「ゴミ箱」アイコン)。 また、AIが生成したファイルであれば、ダウンロードも可能です(上の画像中にダウンロードアイコンがありますね)。当社の環境で確認する限り、AIが生成したファイルに限った挙動で、ユーザーがアップロードしたファイルに関してはWeb UIからダウンロードすることはできませんでした。あくまでスコープは「AIが生成したデータ」ということで、こうした仕様となっているものと思います。

スレッドに関してもWeb UIで一覧が確認できるページがあるのですが、これに関してはOrganizationsの設定でこの情報ページをどこまで見せるかを変更できます。デフォルトはhiddenとなっており、スレッド一覧のページは無効化されています。

スレッドはアシスタントAIを利用した実際のやりとりが残るため、セキュリティ面からの配慮でしょう。

このように、データの種別によって扱いにやや異なる点はありますが、ほとんどのデータはOrganization全体で共有され、閲覧できるということは認識しておいたほうがよいでしょう。

対処

対処としては、先ほど引用した公式ドキュメントで以下のように推奨されています。

We strongly recommend the following data access controls:

- Implement authorization. Before performing reads or writes on assistants, threads, messages, and files, ensure that the end-user is authorized to do so. For example, store in your database the object IDs that the end-user has access to, and check it before fetching the object ID with the API. - Restrict API key access. Carefully consider who in your organization should have API keys and periodically audit this list. API keys enable a wide range of operations including reading and modifying sensitive information, such as messages and files. - Create separate accounts. Consider creating separate accounts / organizations for different applications in order to isolate data across multiple applications.

以下のデータアクセス制御を強く推奨します:

  • 認可の導入。アシスタント、スレッド、メッセージ、およびファイルに対して読み取りまたは書き込みを実行する前に、エンドユーザーにその権限があることを確認する。例えば、エンドユーザーがアクセスできるオブジェクトIDをデータベースに保存し、APIでオブジェクトIDを取得する前にそれをチェックする。
  • APIキーへのアクセスを制限する。組織内で誰がAPIキーを持つべきかを慎重に検討し、このリストを定期的に監査する。APIキーは、メッセージやファイルなどの機密情報の読み取りや変更を含む、幅広い操作を可能にする。
  • 個別のアカウントを作成する。複数のアプリケーションにまたがるデータを分離するために、アプリケーションごとに個別のアカウント/organizationを作成することを検討する。

「Retrieval」ツールを有効としたアシスタントでは、アップロードしたデータを保持する料金がかかる

アシスタントでは、「tools」プロパティで動作を助ける機能を有効化できます。2024年2月現在、プログラムを生成・実行する「Code Interpreter」と、アップロードされたファイルを参照して回答できる「Retrieval」が利用可能です。

Retrievalを利用すると、アップロードされたファイルを保持して以後の回答に利用するわけですが、今後データの保持の費用が発生するようです。 OpenAIの公式の価格情報には、Retrieval利用時の費用の情報は下記のようになっています。

Retrieval $0.20 / GB / assistant / day (free until 03/01/2024)

アシスタントごと・ファイル1GBごとに、毎日0.20$が課金されるということですね。0.20$といっても30日ならば6$ですし、アシスタントが多いとさらに課金が増えることになります。03/01/2024までは無料ということですが、今後注意していく必要があります。

なお一時的にファイルを使いたい(たとえば、「使いまわしたいわけではないが、あるファイルを参考にしたアウトプットがほしい」という場合)は、Code Interpreterを利用するのが良いようです。Code Interpreterを利用する場合、1セッションあたり0.03$がかかりますが、セッションの寿命が1時間と設定されているため、毎日の課金は発生しません。

公式ドキュメントでは、この点について下記のように解説されています。

You do not pay for files attached to an Assistant or Message when used with Code Interpreter. You are only charged for files that are indexed for retrieval which happens automatically if the Retrieval tool is enabled.

Code Interpreterを使用した場合、アシスタントやメッセージに添付されたファイルに対する料金は発生しません。課金されるのは、Retrievalツールが有効になっている場合に自動的に行われる、検索のためにインデックスが作成されたファイルに対してのみです。

アシスタントで同じスレッドをずっと利用しているとトークンが累積し利用料金が上がっていく

アシスタントでは、やりとりをスレッドの単位で管理しますが、同じスレッドでやり取りを続けることで、やり取りの履歴を蓄積し、その履歴をもとにした回答が行われるようになります。これは非常に便利ですが、蓄積したやり取りの履歴がTokenとして渡されるため、ずっとやり取りを続けていくと利用料金が上がっていく、という課題があるようです。

これについては、体験談をNoteに書いておられる方がいるので、詳細はそちらをご覧ください。

https://note.com/nike_cha_n/n/n65a6101d59d7

各データの保持期間をまとめた公式ページによると、スレッドのデータはそもそも60日しか保持されないので、あまり長期にスレッドを使い回さない仕様とするのが良さそうです。

不要なアシスタントやファイルの削除

以上、ざっと注意点を説明してきました。ここまで読んで、検証で作った不要なアシスタントやファイルなど削除したい、という方もいると思うので、その方法を簡単に紹介していきます。

Web UIでの削除

もっとも簡単なのはWeb UIでの削除です。すでにご紹介した、アシスタントやファイルの一覧を表示するページから、各アシスタント・ファイルの削除を行うことができます。各エントリを選んで、表示されるゴミ箱マークから削除するだけです。 ただし、「一括削除ができない(個々のエントリごとに削除する必要がある)」「他のユーザーのアシスタント/ファイルも削除できてしまう」という点には十分に注意してください。

ファイルの一覧には、アシスタントに関連するファイル以外も存在する可能性があります。アシスタントに関連するファイルは、「Purpose」というフィールドが「assistants」となっているので、区別することが可能です。

いずれにしても、検証環境などで複数のメンバーが各々使っているような場合は、別ユーザーが誤削除しないよう、分かりやすい名前を付けておくことをおすすめします。2024年2月現在は、作成者の名前なども出ないため、同じような名前が並ぶと区別が非常に付きづらいので。。

APIでの削除

もちろん、APIで削除することも可能です。削除する際は、対象のオブジェクトのIDが必要となります。 以下のスクリプトは、スクリプト内で生成したアシスタント、アップロードしたファイル、AIが生成されたファイルを最後にすべて削除する例です。

import requests
import time
from openai import OpenAI
client = OpenAI()

# ファイルのアップロード。後に削除するためのIDはupload_file.idで参照可能
upload_file = client.files.create(
    file=open("data/eguchi-test.csv", "rb"),
    purpose="assistants"
)

# アシスタントの生成。後に削除するためのIDはassistant.idで参照可能
assistant = client.beta.assistants.create(
    name="Eguchi_test_assistant",
    instructions="あなたはデータ解析を行うアシスタントです。命令に従ってCSVを解析し、指示に従った新しいCSVを生成してください。",
    tools=[
        {"type": "retrieval"}, 
        {"type": "code_interpreter"},
    ],
    model="gpt-4-1106-preview",
    file_ids=[upload_file.id]
)

thread = client.beta.threads.create()

message = client.beta.threads.messages.create(
    thread_id=thread.id,
    role="user",
    content="購入した小計が1000円を越えたエントリを教えてください。結果はCSV形式で出力してください。ファイル名は'eguchi-test-download_<yyyymmddhhmmss>.csv'としてください。<yyyymmddhhmmss>部分には、作成日時のタイムスタンプが入ります。"
)

run = client.beta.threads.runs.create(
  thread_id=thread.id,
  assistant_id=assistant.id
)

while run.status != 'completed':
    run = client.beta.threads.runs.retrieve(
      thread_id=thread.id,
      run_id=run.id
    )
    print(run.status)
    time.sleep(5)

messages = client.beta.threads.messages.list(
    thread_id=thread.id
)

print(messages.data)
# messages オブジェクトの data 属性からメッセージを取得
messages_data = messages.data

# 各メッセージから 生成したファイルのIDを取得
file_ids = [message.file_ids for message in messages_data]
print(file_ids)
download_file_id = file_ids[0][0]

# ファイルのローカルへのダウンロード
if len(download_file_id) > 0:
    print("writing file...")
    output_path="results/{}.csv".format(download_file_id)
    write_file_data = client.files.content(download_file_id)
    file_data_bytes = write_file_data.read()
    with open(output_path, "wb") as file:
        file.write(file_data_bytes)


# 以後、削除処理
# ファイルの削除 : アップロードしたファイル、ダウンロードしたファイルいずれも削除する
for file in [upload_file.id, download_file_id]:
    res = client.files.delete(file)
    print(res)

## アシスタントの削除
res =client.beta.assistants.delete(assistant_id=assistant.id)
print(res)

すでに存在するアシスタントやファイルを削除する場合は、リストを取得して処理することになります。 Githubでアシスタントを一括削除するサンプルコードを上げている方もいるので、参考にしてみてください。

おわりに

ということで、OpenAI Assitants API利用時の注意事項のご紹介でした。アシスタントAIはいろいろなことができて非常に便利なものなので、ここで挙げたような点には注意しつつ、活用していただければと思います。

ではでは。