
OpenAIのAssistants APIを使ってビットコインの分析をしてみる
概要
続いて、今回発表されたAssistants APIも触ってみよう。もう一日経っており、それなりに使い方も広まっている気がするので、少しばかり面白みのあることをやっていこう。Assistants APIを用いると、指示に基づいてモデルやツールを活用して、ユーザのクエリに応答できる。今回はコードインタプリターを使ってビットコインのチャートを表示したり、簡易的にトレンドの分析をさせてみよう。
Assistants APIについて
これまで、OpenAI APIはGPTの仕組みを使い文を生成するものであった。チャットっぽい振る舞いをしているが、実は内部で過去の履歴を直接渡すなどしていた。Function Callingをはじめ色んな機能があったものの、やっていることは文の生成だ。これに対して、Assistants APIは、その辺の機能をラップして、内部で履歴を持っていたりデータを保持したり、更にPythonの実行環境があったりと、今ChatGPT上で実行しているようなリッチな環境を、そのままAPI化したものに見える。正確なところはマニュアルには書いていないが、そんな感じに見えている。これにより、データをアップロードした上で分析したり、分析結果をダウンロードしたり、外部のAPIと連携したりしながら、自動的に処理を実行できるようになっている。中身はOpenAI APIだとは思うが、その周りのインフラがあることで簡単にリッチな機能を使えるのが面白そう。
Assistants APIの使い方
まずは、Assistants APIの使い方を簡単に説明する。最初にクライアントを作成しておく。
import openai
openai.api_key = "<ここにAPI Key>"
client = openai.OpenAI()
最初にアシスタントに対して指示を与えてスレッドを作成する。ここではツールとしてcode_interpreterを追加しておく。もしエラーが起きるようなら、openaiのライブラリを最新のものにしておこう。
assistant = client.beta.assistants.create(
name="ビットコイン分析官",
instructions="あなたは優秀なAIアシスタントです。ユーザの質問に回答してください。",
tools=[{"type": "code_interpreter"}],
model="gpt-3.5-turbo-1106"
)
thread = client.beta.threads.create()
print(thread.model_dump_json(indent=2))
スレッドにはidが振られ、この中に会話のログが溜まったり、実行環境が作られたりする。
{
"id": "thread_xxxxxxxxxxxxxxxxxxxxxxxx",
"created_at": 1699363872,
"metadata": {},
"object": "thread"
}
続いて、ユーザーから質問をしてみよう。スレッドのIDとロール、本文をそれぞれ与える。
message = client.beta.threads.messages.create(
thread_id=thread.id,
role="user",
content="今日の日付を教えてください。"
)
json.loads(message.model_dump_json(indent=2))
送ったメッセージは以下のようになっている。
{'id': 'msg_xxxxxxxxxxxxxxxxxxxxxxxxxxx',
'assistant_id': None,
'content': [{'text': {'annotations': [], 'value': '今日の日付を教えてください。'},
'type': 'text'}],
'created_at': 1699367000,
'file_ids': [],
'metadata': {},
'object': 'thread.message',
'role': 'user',
'run_id': None,
'thread_id': 'thread_xxxxxxxxxxxxxxxxxxxxxxxx'}
ユーザの質問に対して、AIアシスタントにどう答えるかを指示して、回答を作成しよう。
run = client.beta.threads.runs.create(
thread_id=thread.id,
assistant_id=assistant.id,
instructions="ユーザーの問いに答えてあげて下さい。"
)
print(json.loads(run.model_dump_json(indent=2)))
上記のように実行をすると、以下のようなレスポンスが返ってくる。中身を見ると、会話にidが振られ、statusがqueuedになっており、実行が開始されていることがわかる。
{'id': 'run_xxxxxxxxxxxxxxxxxxxxxxxxxx',
'assistant_id': 'asst_xxxxxxxxxxxxxxxxxxxxxxxxxxx',
'cancelled_at': None,
'completed_at': None,
'created_at': 1699367012,
'expires_at': 1699367612,
'failed_at': None,
'file_ids': [],
'instructions': 'ユーザーの問いに答えてあげて下さい。',
'last_error': None,
'metadata': {},
'model': 'gpt-3.5-turbo-1106',
'object': 'thread.run',
'required_action': None,
'started_at': None,
'status': 'queued',
'thread_id': 'thread_xxxxxxxxxxxxxxxxxxxxxxxx',
'tools': [{'type': 'code_interpreter'}]}
実行中の会話の状態は以下のように取得できる。
run = client.beta.threads.runs.retrieve(
thread_id=thread.id,
run_id=run.id
)
print(json.loads(run.model_dump_json(indent=2)))
取得結果の例は以下となる。statusがcompletedになり、レスポンスが生成されたことを確認できる。
{'id': 'run_xxxxxxxxxxxxxxxxxxxxxxxxxx',
'assistant_id': 'asst_xxxxxxxxxxxxxxxxxxxxxxxxxxx',
'cancelled_at': None,
'completed_at': 1699367014,
'created_at': 1699367012,
'expires_at': None,
'failed_at': None,
'file_ids': [],
'instructions': 'ユーザーの問いに答えてあげて下さい。',
'last_error': None,
'metadata': {},
'model': 'gpt-3.5-turbo-1106',
'object': 'thread.run',
'required_action': None,
'started_at': 1699367012,
'status': 'completed',
'thread_id': 'thread_xxxxxxxxxxxxxxxxxxxxxxxx',
'tools': [{'type': 'code_interpreter'}]}
結果を取得しよう。メッセージの履歴を取得して最近のものを表示してみよう。
messages = client.beta.threads.messages.list(thread_id=thread.id)
json_messages = json.loads(messages.model_dump_json(indent=2))
print(json_messages["data"][0])
以下のように本日の日付が取得されていることを確認できる。どんなコードを使ったかは見えないみたいね。
{'id': 'msg_xxxxxxxxxxxxxxxxxxxxxxxxxxx',
'assistant_id': 'asst_xxxxxxxxxxxxxxxxxxxxxxxxxxx',
'content': [{'text': {'annotations': [], 'value': '今日の日付は2023年11月7日です。'},
'type': 'text'}],
'created_at': 1699367014,
'file_ids': [],
'metadata': {},
'object': 'thread.message',
'role': 'assistant',
'run_id': 'run_xxxxxxxxxxxxxxxxxxxxxxxxxx',
'thread_id': 'thread_xxxxxxxxxxxxxxxxxxxxxxxx'}
ビットコインの簡易的な分析
次はビットコインの分析をしてみよう。まずは、アシスタントを以下のように作ってみる。
assistant = client.beta.assistants.create(
name="ビットコイン分析官",
instructions="あなたはビットコインのチャート分析者です。コードを書いて実行し、指示に従い分析をしてください。",
tools=[{"type": "code_interpreter"}],
model="gpt-3.5-turbo-1106"
)
thread = client.beta.threads.create()
とりあえず、チャートを見てみたいのでプロットしてもらう。メッセージを与え、回答を作成する。
message = client.beta.threads.messages.create(
thread_id=thread.id,
role="user",
content="2023年8月から現在までのビットコインのチャートをプロットしてください。"
)
run = client.beta.threads.runs.create(
thread_id=thread.id,
assistant_id=assistant.id,
instructions="ユーザーの問いに答えてあげて下さい。"
)
少し待ってメッセージを確認してみよう
messages = client.beta.threads.messages.list(thread_id=thread.id)
json_messages = json.loads(messages.model_dump_json(indent=2))
print(json_messages["data"][0]["content"][0]["text"]["value"])
すると、以下のように言われた。ChatGPTっぽい返事だね。
申し訳ありませんが、私の環境ではリアルタイムのビットコイン価格データを取得することができません。
ただし、過去のビットコイン価格データを使用してプロットすることは可能です。
もしご興味があれば、過去のビットコイン価格データを使用してチャートをプロットすることができます。
それでよろしければ、お知らせください。
リアルタイムと過去が、それぞれいつ頃の事かわからないけど、とりあえずOKしてみよう。
message = client.beta.threads.messages.create(
thread_id=thread.id,
role="user",
content="はい、それで良いです"
)
run = client.beta.threads.runs.create(
thread_id=thread.id,
assistant_id=assistant.id,
instructions="ユーザーの問いに答えてあげて下さい。"
)
print(json_messages["data"][0]["content"])
結果は以下のようになる。出力されたファイル情報とテキストが返ってくるらしい。
[{'image_file': {'file_id': 'file-xxxxxxxxxxxxxxxxxx'},
'type': 'image_file'},
{'text': {'annotations': [],
'value': '上記のチャートは、2023年8月から現在までの仮想的なビットコイン価格データに基づいてプロットされています。実際のビットコイン価格変動とは異なることにご留意ください。'},
'type': 'text'}]
ファイルIDに示されたデータをダウンロードして表示してみよう。以下のようにするとバイナリデータとしてダウンロードできるようだ。
file_id = messages.data[0].content[0].image_file.file_id
response = client.files.with_raw_response.retrieve_content(file_id)
Image.open(io.BytesIO(response.content))

ちゃんとグラフが取れていることが確認できる。次はトレンドを判定してもらおう。ここからはコードは同じなので、入力と結果のみ示す。
message = client.beta.threads.messages.create(
thread_id=thread.id,
role="user",
content="直近10日のビットコインのトレンドを分析して上昇トレンドか下降トレンドかを判定してください。"
)
(省略)
結果は以下のようになった。おぉ、トレンドを出力してくれている。
最後の10日間のビットコイン価格の分析によると、上昇トレンドであることが判定されました。
根拠がわからないので、聞いてみよう。
message = client.beta.threads.messages.create(
thread_id=thread.id,
role="user",
content="その根拠を教えてください。"
)
こう返ってきた。シンプル~~~。でも、トレンド分析に有用なツールや、トレンド分析の方法を指示で与えてあげれば、もっと良い感じで分析してくれることでしょう。
最後の10日間のビットコイン価格データを分析した結果、最新の価格が最初の価格よりも高いことから、上昇トレンドであると判断されました。
つまり、価格が上昇している傾向にあるという指標に基づいています。
まとめ
少し長くなったが、OpenAIのAssistants APIを使ってみた。画像の表示や、少しだけ実利に近いビットコインの分析もやってみて、中々使えそうな感じがする。非常に面白いけど、マネージドすぎるので、個人的にはバックエンドも自分で整備してもう少し柔軟に使えるようにしたいかな。