🎆

OpenAIのAssistant API試してみた【基本編】

2023/12/05に公開

OpenAIの大型のアップデートが2023/11/7に発表されましたね。
GPT4-Turboがやはり目玉ではありますが、Assistant APIというのもβ版で公開されました。

概要を読んでみると、langchainのAgentのようなものをOpenAIが公式に実装したという感じですね。
周辺ツールとして作成されていたものを本家が作りに来るこのパターン、これまでにもよく見てきたパターンですがここでも始まってきましたね。

playgroundでもAPI経由でも使うことができるが、ここではAPI経由での使い方で試しながら、その特徴を見ていきましょう。

まずはSDKをアップデートする。assistantがbetaとして入っているバージョンにする。1.2.4は記事執筆時点での最新版

$ pip install --upgrade 'openai==1.3.7'

OpenAIのサイトに分かりやすいレファレンスがあり、ちょっと動かしてみるのだけであればコードをコピペするだけでOK。
本稿では基本編ということで、アシスタントの作成、スレッドの定義、ランして結果取得、までを一連の流れでやってみる。
なお、APIは執筆時点で最新の1.3.7を使っているが、ものすごい勢いで日々アップデートされているのと、そもそもβ版なので最新のコードは公式のレファレンスを参照して下さい。
以下、環境変数のOPENAI_API_KEYは既に設定されている前提で進みます。

from openai import OpenAI
client = OpenAI()

my_assistant = client.beta.assistants.create(
    name="CrimeAnalyzer",
    tools=[{"type": "retrieval"}],
    model="gpt-4-1106-preview",
)

ちなみにmodelをgpt-4などに指定すると、下記のようなエラーメッセージが出て、toolでretrievalを使うならそのmodelはダメだよ、とちゃんと出てきます。

The requested model 'gpt-4' cannot be used with the 'retrieval' tool. Consider using 'gpt-4-1106-preview', 'gpt-3.5-turbo-1106', or a later model.

次に作成したAssistantがちゃんとできているかリスト表示してみましょう。

from openai import OpenAI
import os

client = OpenAI()

my_assistants = client.beta.assistants.list(
    order="desc",
    limit="20",
)
print(my_assistants.data)
assistant_id = my_assistants.data[0].id

また、assistant_idをこの後使うので変数に控えておきます。
次にThreadを作ってみましょう。ThreadとはAssistantと行う一連のChatプロセスのようなもので、メッセージのやり取りもこの後保存してくれる、まさに会話のスレッドです。

from openai import OpenAI

client = OpenAI()

empty_thread = client.beta.threads.create()
print(empty_thread)
thread_id = empty_thread.id

この段階ではthreadとassistantはまだ関係ないんですね。
thread_idも後で必要になるので控えておきます。また、重要なところなんですがThreadをリストするAPIは、記事執筆時点では提供されていないため、一度作ったスレッドは、thread_idが分からなくなってしまうと永遠に行方不明です。必ずどこかにthread_idは退避しておきましょう…

次に、Threadにメッセージを入れてみましょう。メッセージとはプロンプトに相当するもので、userによるものなのか、assistantによるものなのかを指定します。
ここで、重要な点は、メッセージを入れただけではまだ処理は始まらないということです。処理の開始は後述のRUNで行います

from openai import OpenAI

client = OpenAI()

thread_message = client.beta.threads.messages.create(
    thread_id=thread_id,
    role="user",
    content="日本の窃盗事件のここ数年の傾向とその原因を教えて下さい",
)
print(thread_message)

thread_messageにrun_idというプロパティがありますが、ここがNoneになっていてまだRunが登録されていないことがわかります。

ThreadMessage(id='msg_xxxxxxx', assistant_id=None, content=[MessageContentText(text=Text(annotations=[], value='日本の窃盗事件のここ数年の傾向とその原因を教えて下さい'), type='text')], created_at=1701745130, file_ids=[], metadata={}, object='thread.message', role='user', run_id=None, thread_id='thread_yyyyyyy')

登録後しばらくたってからthread内のmessageのリストを受け取ってみても、最初に入れたmessageしかなく、まだ動いていないようです。

from openai import OpenAI

client = OpenAI()

thread_messages = client.beta.threads.messages.list(thread_id)
print(thread_messages.data)

では実際に処理を開始してみましょう。RUNをcreateします。
RUNの段階でassistantを紐付ける仕様になっているので、その気になれば同じスレッドでもRUN毎にassistantを変更できるってことですね。

from openai import OpenAI

client = OpenAI()

run = client.beta.threads.runs.create(thread_id=thread_id, assistant_id=assistant_id)
print(run)

runの状態は下記のようになっていて、作成直後のRUNはqueuedの状態になっていることがわかります。

Run(id='run_xxxxxx', assistant_id='asst_D5KUxsuxJKbdajziS9ji71dV', cancelled_at=None, completed_at=None, created_at=1701753234, expires_at=1701753834, failed_at=None, file_ids=[], instructions=None, last_error=None, metadata={}, model='gpt-4-1106-preview', object='thread.run', required_action=None, started_at=None, status='queued', thread_id='thread_xxxxxx', tools=[ToolAssistantToolsRetrieval(type='retrieval')])

しばらくたった後、messagesを取得してみます。

from openai import OpenAI

client = OpenAI()

thread_messages = client.beta.threads.messages.list(thread_id)
print(thread_messages.data[0])

配列の最初にassistantが入れてくれたmessageが入っているのが分かります。

ThreadMessage(id='msg_yyyyyyyy', assistant_id='asst_xxxxx', content=[MessageContentText(text=Text(annotations=[], value='日本の窃盗事件の傾向とその原因を理解するには、犯罪統計や専門家の分析、並びに社会的な調査結果などを参照する必要があります。現在の情報と統計データに基づいて、一般的な傾向と考えられる原因を以下に述べますが、最新の統計や詳細については日本の警察庁または犯罪学の研究機関による公式のデータを確認する必要があります。\n\n傾向:\n1. 急減傾向: 近年、日本の犯罪発生率は全般的に減少しており、特に窃盗犯罪も減少傾向にあります。これには経済的状況の改善、防犯意識の向上、治安機関の効果的な予防策などが要因として考えられます。\n2. 高齢者の犯罪増加: 一方で、高齢者による窃盗事件の増加が指摘されており、これは高齢化社会における生活困窮や孤独感が背景にあるとされています。\n3. サイバー犯罪の増加: テクノロジーの進展とともに、インターネット経由での個人情報窃取やオンライン詐欺などのサイバー窃盗が増加しています。\n\n原因:\n1. 経済状況: 経済的な困窮は窃盗の動機となることがありますが、日本の場合、経済の長期的な改善によって伝統的な窃盗犯罪は減少しています。\n2. 社会的孤立: 社会的なつながりの希薄化が進んでいることが、特に高齢者や若者の犯罪に影響を与えている可能性があります。\n3. 予防策と意識の向上: 家屋のセキュリティシステムの普及や防犯カメラの増加、市民の防犯意識向上などが犯罪減少に寄与しています。\n4. 法の執行: 警察による取締りの強化や犯罪に対する法の執行の厳格化が、犯罪抑止に効果を上げています。\n5. テクノロジーの進化: クレジットカードや電子マネーの利用の増加により、現金を持ち歩く人が減っていることも、伝統的な窃盗の機会を減らしています。\n\nこれらの情報には時差があるため或いは日本国内の最新の報告や研究などによって、さらに詳しい傾向や原因が明らかになっている可能性があります。日本の警察庁が毎年発表している「犯罪白書」や大手新聞社などの報道、専門家の分析を確認することをお勧めします。'), type='text')], created_at=1701753235, file_ids=[], metadata={}, object='thread.message', role='assistant', run_id='run_xxxxxx', thread_id='thread_yyyyyyyyy')

以上で、assistantの生成からメッセージのやり取りまで行いました。

今回のRUNではassistantからのmessageは1つだけでしたが、回答を完成させるのに複数messageが必要であるとmodelが判断した場合は複数個のmessageが順番に出力されるようです。
今回は基礎編ということでtoolは与えてはいるものの使ってはいないので、次回以降はどこかでtoolも使わせてみながら動作を確認します。

Discussion