![見出し画像](https://assets.st-note.com/production/uploads/images/150552255/rectangle_large_type_2_2b7d70e83993d8d0189a66e5a97bbfe7.png?width=1200)
OpenAI function callingの機能比較を実装(OpenAI AssistantsとChatCompletion)
実装して、両者を比較してみた。
個人的備忘録。
参照したAPIのドキュメント
https://platform.openai.com/docs/guides/function-calling
https://platform.openai.com/docs/assistants/tools/function-calling
でも、英語が苦手なので、試行錯誤で実装した。(苦労した)
Chat Completionで実装
function call で実装する機能
ユーザーが誕生日を任意の書式で入力する
アシスタントは、ユーザーの誕生日から数えて今日が何日目かを計算する関数をcallする
アシスタントは、今日まで生きてきたユーザーをお祝いする
処理手順
生年月日から今日が何日目かを数える関数を定義する
gptにこの関数を伝える
1回目の問い合わせ。ユーザーの誕生日を送る
gptは、誕生日から今日までの日数を算出する関数をcallする
gptがcallした関数を実行し、今日が何日目かを取得する
2回目の問い合わせで、お祝いの文章を書いてもらう
生年月日から今日が何日目かを数える関数を定義する
from datetime import datetime
def cal_you_lived(birthday):
try:
# 生年月日を文字列から日付に変換
birthdate = datetime.strptime(birthday, "%Y-%m-%d")
# 今日の日付を取得
today = datetime.today()
# 生まれてからの日数を計算
days_since = (today - birthdate).days
return f"今日は、生まれてから {days_since} 日目です。"
except ValueError:
return "生年月日の書式が正しくありません。"
birthday = "1968-09-07"
print(cal_you_lived(birthday))
今日は、生まれてから 20429 日目です。
この関数をgptがcallする。
ユーザが元号などで入力した誕生日を、関数の引数の書式に(yyyy-mm-dd)変換して、引数とともに関数をcallするようにする。
gptにこの関数を伝える
ユーザーが誕生日を入力した時に、呼び出す関数と引数の書式を定義する
tools = [
{
"type": "function",
"function": {
"name": "cal_you_lived",
"description": "ユーザーが入力した誕生日から数えて今日は何日目かを計算して、日数を返す",
"parameters": {
"type": "object",
"properties": {
"birthday ": {
"type": "string",
"description": "ユーザーの生年月日。format:yyyy-mm-dd. e.g. 2001-11-10 ",
},
},
"required": ["birthday "],
},
},
}
]
1回目の問い合わせ。ユーザーの誕生日を送る
from openai import OpenAI
import json
from google.colab import userdata
openai_api_key = userdata.get('OPENAI_API_KEY') # google colabのシークレット機能でAPIキーを設定
client = OpenAI(api_key=openai_api_key)
# messageを保存するlistの初期化
messages = []
# gptに、ユーザーの入力した誕生日から今日までの日数を計算する関数を呼んでもらう
message = "私の誕生日は、昭和41年10月10日です。"
messages = [
{'role':'system', 'content': "ユーザが入力した誕生日から数えて、今日が何日目かを計算してください"},
{"role": "user", "content": message}
]
response = client.chat.completions.create(
model="gpt-4o-mini",
messages=messages,
tools=tools,
tool_choice="auto",
)
gptは、誕生日から今日までの日数を算出する関数をcallする
response_message = response.choices[0].message
print(response_message)
ChatCompletionMessage(content=None, refusal=None, role='assistant', function_call=None, tool_calls=[ChatCompletionMessageToolCall(id='call_lXNoKqVKFadWNaWYWbLFcQ2I', function=Function(arguments='{"birthday":"1966-10-10"}', name='cal_you_lived'), type='function')])
responce に callする関数と引数がresponseに入っている。
function=Function(arguments='{"birthday":"1966-10-10"}', name='cal_you_lived' ・・・
gptがcallした関数を実行し、今日が何日目かを取得する
# gptのresponseをmessagesに追加
messages.append(response_message)
# callする関数
tool_calls = response_message.tool_calls
if tool_calls:
available_functions = {
"cal_you_lived": cal_you_lived,
}
for tool_call in tool_calls:
# tool_callsにあるfunctionを実行
function_name = tool_call.function.name
function_to_call = available_functions[function_name]
function_args = json.loads(tool_call.function.arguments)
# 関数を実行
function_response = function_to_call(
birthday=function_args.get("birthday"),
)
# 呼び出して実行した関数の情報と引数をmessagesに追加
messages.append(
{
"tool_call_id": tool_call.id,
"role": "tool",
"name": function_name,
"content": function_response,
}
) # extend conversation with function response
実行した関数を確認してみる
print(function_name)
print(function_to_call)
print(function_args)
cal_you_lived
<function cal_you_lived at 0x7ab7d9b225f0>
{'birthday': '1966-10-10'}
関数を実行した結果を追加したmessageを確認してみる
messages
[{'role': 'system', 'content': 'ユーザが入力した誕生日から数えて、今日が何日目かを計算してください'},
{'role': 'user', 'content': '私の誕生日は、昭和41年10月10日です。'},
ChatCompletionMessage(content=None, refusal=None, role='assistant', function_call=None, tool_calls=[ChatCompletionMessageToolCall(id='call_lXNoKqVKFadWNaWYWbLFcQ2I', function=Function(arguments='{"birthday":"1966-10-10"}', name='cal_you_lived'), type='function')]),
{'tool_call_id': 'call_lXNoKqVKFadWNaWYWbLFcQ2I',
'role': 'tool',
'name': 'cal_you_lived',
'content': '今日は、生まれてから 21127 日目です。'}]
2回目の問い合わせで、お祝いの文章を書いてもらう
message = {'role':'user','content':'あなたは、ユーザーが今日生まれてから何日目であることを、古今東西の名言を引用して祝福してください'}
messages.append(message)
# 次のgpt問い合わせ
second_response = client.chat.completions.create(
model="gpt-4o-mini",
messages=messages,
)
second_response.choices[0].message.content
「生まれてきたことは、すでに特別な奇跡だ。」 - アルバート・アインシュタイン
今日までの21127日、お誕生日を迎えられたあなたに、心からお祝い申し上げます。この日々があなたにとって素晴らしい経験と成長の積み重ねであったことを願っています。これからの未来も、素晴らしい発見に満ちた日々でありますように!
assistansを使って実装する
生年月日から今日が何日目かを数える関数を定義する
from datetime import datetime
def cal_you_lived(birthday):
try:
# 生年月日を文字列から日付に変換
birthdate = datetime.strptime(birthday, "%Y-%m-%d")
# 今日の日付を取得
today = datetime.today()
# 生まれてからの日数を計算
days_since = (today - birthdate).days
return f"今日は、生まれてから {days_since} 日目です。"
except ValueError:
return "生年月日の書式が正しくありません。"
gptにこの関数を伝える
tools = [
{
"type": "function",
"function": {
"name": "cal_you_lived",
"description": "ユーザーが入力した誕生日から数えて今日は何日目かを計算して、日数を返す",
"parameters": {
"type": "object",
"properties": {
"birthday ": {
"type": "string",
"description": "ユーザーの生年月日。format:yyyy-mm-dd. e.g. 2001-11-10 ",
},
},
"required": ["birthday "],
},
},
}
]
1回目の問い合わせ。ユーザーの誕生日を送る
import json
# assistantsの作成
assistant = client.beta.assistants.create(
name="birthday_calculator",
instructions="古今東西の名言を引用して祝福するプロのコピーライダーです。ユーザーを幸せな気持ちにします",
model="gpt-4o",
# ツールの定義
tools = tools
)
# threadの作成
thread = client.beta.threads.create()
# messageの作成
message = client.beta.threads.messages.create(
thread_id=thread.id,
role="user",
content="私の誕生日は、昭和41年11月11日です。今日まで生きてきた私を祝福してください。"
)
# assistantを実行する
run = client.beta.threads.runs.create_and_poll(
thread_id=thread.id,
assistant_id=assistant.id,
)
if run.status == 'completed':
messages = client.beta.threads.messages.list(
thread_id=thread.id
)
print(messages)
else:
print(run.status)
requires_action
actionが必要なステータスになる
gptは、誕生日から今日までの日数を算出する関数をcallする
# 必要なactionを確認してみる
run.required_action.submit_tool_outputs.tool_calls
[RequiredActionFunctionToolCall(id='call_nHdMs7sw4JZxU5dJMeRLQpNh', function=Function(arguments='{"birthday":"1966-11-11"}', name='cal_you_lived'), type='function')]
gptがcallした関数を実行し、今日が何日目かを取得する
tool_outputs = []
available_functions = {
"cal_you_lived": cal_you_lived,
}
for tool in run.required_action.submit_tool_outputs.tool_calls:
function_name = tool.function.name
function_to_call = available_functions[function_name]
function_args = json.loads(tool.function.arguments)
# 関数の実行
output = function_to_call(
birthday=function_args.get("birthday"),
)
# 実行した関数を確認
print(tool)
print(function_name)
print(function_to_call)
print(function_args)
print(output)
RequiredActionFunctionToolCall(id='call_nHdMs7sw4JZxU5dJMeRLQpNh', function=Function(arguments='{"birthday":"1966-11-11"}', name='cal_you_lived'), type='function')
cal_you_lived
<function cal_you_lived at 0x7ab7b37bcaf0>
{'birthday': '1966-11-11'}
今日は、生まれてから 21095 日目です。
2回目の問い合わせで、お祝いの文章を書いてもらう(required actionの実行)
tool_outputs.append({
"tool_call_id": tool.id,
"output": output
})
# 実行した関数の出力をtool_outputsに入れた
tool_outputs
tool_outputs はこんな感じ
[{'tool_call_id': 'call_nHdMs7sw4JZxU5dJMeRLQpNh',
'output': '今日は、生まれてから 21095 日目です。'}]
2度目のrun
# tool_outputsを使って2回目のrunを実行
if tool_outputs:
try:
run = client.beta.threads.runs.submit_tool_outputs_and_poll(
thread_id=thread.id,
run_id=run.id,
tool_outputs=tool_outputs
)
print("Tool outputs submitted successfully.")
except Exception as e:
print("Failed to submit tool outputs:", e)
else:
print("No tool outputs to submit.")
if run.status == 'completed':
messages = client.beta.threads.messages.list(
thread_id=thread.id
)
print(messages)
else:
print(run.status)
Tool outputs submitted successfully.
SyncCursorPage[Message](data=[Message(id='msg_tAhohLJa0VlSVmnyKmFCtf6F', assistant_id='asst_0C5i4jI3xmzfhcxAqQdG3fMo', attachments=[], completed_at=None, content=[TextContentBlock(text=Text(annotations=[], value='あなたがこの世に誕生してから、今日で21095日が経ちました。これはとても素晴らしいことで、あなたの一つ一つの日々が織りなす美しい物語を象徴しています。\n\n"今日という日は、残りの人生の最初の日である。" - アメリカの詩人・ラルフ・ワルド・エマーソン\n\nこれからも新たな日々が、あなたに幸せと豊かさをもたらしますように。21095日の素晴らしい経験を胸に、さらに輝かしい未来へと進んでください。心からお祝い申し上げます。💐'), type='text')], created_at=1723517487, incomplete_at=None, incomplete_details=None, metadata={}, object='thread.message', role='assistant', run_id='run_QeWIET4fkUIG31qOdRS9x1eq', status=None, thread_id='thread_YScos8voU1srjwXffyIEZqmO'), Message(id='msg_ctzfSuHyifeLk68oWD0mV1g9', assistant_id=None, attachments=[], completed_at=None, content=[TextContentBlock(text=Text(annotations=[], value='私の誕生日は、昭和41年11月11日です。今日まで生きてきた私を祝福してください。'), type='text')], created_at=1723517209, incomplete_at=None, incomplete_details=None, metadata={}, object='thread.message', role='user', run_id=None, status=None, thread_id='thread_YScos8voU1srjwXffyIEZqmO')], object='list', first_id='msg_tAhohLJa0VlSVmnyKmFCtf6F', last_id='msg_ctzfSuHyifeLk68oWD0mV1g9', has_more=False)
tool outputs の送信が成功し、回答が返ってきた。
messagesの内容を確認してみる。
messages_ = client.beta.threads.messages.list(thread.id)
最初のユーザーメッセージと、回答のシステムメーセージ。
for thread_message in messages_:
print(thread_message.content[0].text.value)
print('\n')
あなたがこの世に誕生してから、今日で21095日が経ちました。これはとても素晴らしいことで、あなたの一つ一つの日々が織りなす美しい物語を象徴しています。
"今日という日は、残りの人生の最初の日である。" - アメリカの詩人・ラルフ・ワルド・エマーソン
これからも新たな日々が、あなたに幸せと豊かさをもたらしますように。21095日の素晴らしい経験を胸に、さらに輝かしい未来へと進んでください。心からお祝い申し上げます。💐
私の誕生日は、昭和41年11月11日です。今日まで生きてきた私を祝福してください。