より高速に効率的にFunction callingを実行できる!!OpenAI DevDayで発表されたFunction callingの並列実行について試してみた。
こんちには。
データアナリティクス事業本部 インテグレーション部 機械学習チームの中村です。
今回は前回の記事で書ききれなかった「Function callingの並列化」にフォーカスして試してみます。
機能の概要
Function callingが更新され、1つのメッセージで複数の関数を呼び出すように動作するようになりました。(正確には複数呼び出すようにレスポンス側に指示が来ます)
こちらはgpt-4-1106-preview
とgpt-3.5-turbo-1106
で使用可能です。
また、Function calling自体の精度も向上され、正しい関数パラメータを返す可能性が高くなっているようです。
上記の公式ガイドでは並列的なFunction callingの例として、例えば3箇所の天気を取得する機能を例として説明されています。
こちらについて試してみようと思います。
試してみた
前提
- Pythonのopenaiモジュールをインストール済み
- OPENAI_API_KEY取得済み
ツールの定義
まずはツールとして呼び出したい関数を定義します。
import json def get_current_weather(location, unit="celsius"): """Get the current weather in a given location""" if "tokyo" in location.lower(): return json.dumps({"location": location, "temperature": "10", "unit": "celsius"}) elif "san francisco" in location.lower(): return json.dumps({"location": location, "temperature": "72", "unit": "fahrenheit"}) else: return json.dumps({"location": location, "temperature": "22", "unit": "celsius"})
次にこの関数をツールとしてもつtoolsを定義します。(データ型はList[ChatCompletionToolParam]となるようにします)
from openai.types.chat import ChatCompletionToolParam tools = [ ChatCompletionToolParam({ "type": "function", "function": { "name": "get_current_weather", "description": "指定した場所の現在の天気を取得する", "parameters": { "type": "object", "properties": { "location": { "type": "string", "description": "県名や都市名(例:東京、サンフランシスコ)", }, "unit": {"type": "string", "enum": ["celsius", "fahrenheit"]}, }, "required": ["location"], }, }, }) ]
こちらのtoolsの定義ですが、functionsを使う場合とは構造が変わっており注意が必要です。
toolsの場合は"type": "function"
というtypeの指定が増え、ネストが1段階増えているようです。
関数以外の様々な用途を想定したtoolsという形に変わっており、今後にアップデートにも期待が膨らむところです。
ツールを指定してチャット
そして以下のようにtoolsを引数に指定してチャットAPIを呼び出します。
from openai import OpenAI client = OpenAI( api_key = "ここにOpenAI APIキーを記載" ) response = client.chat.completions.create( model="gpt-3.5-turbo-1106", temperature=0.0, messages=[ {"role": "user", "content": "サンフランシスコ、東京、大阪の天気は?"}, ], tools=tools, tool_choice="auto", # auto is default, but we'll be explicit )
呼び出し時もtools
、tool_choice
という新しい引数を使用します。(以前はfunctions
、function_call
でしたね)
結果を確認してみましょう。
print(response.model_dump_json(indent=2)) # { # "id": "chatcmpl-8IDRfTGaObZEqfL5kpuVx2IeVjIDH", # "choices": [ # { # "finish_reason": "tool_calls", # "index": 0, # "message": { # "content": null, # "role": "assistant", # "function_call": null, # "tool_calls": [ # { # "id": "call_uVBN0txk9txPyqkmhkAZrJnf", # "function": { # "arguments": "{\"location\": \"San Francisco\"}", # "name": "get_current_weather" # }, # "type": "function" # }, # { # "id": "call_5YDoZydCJaoRrE8NEZg9Rjmu", # "function": { # "arguments": "{\"location\": \"Tokyo\"}", # "name": "get_current_weather" # }, # "type": "function" # }, # { # "id": "call_fEGI3Wf2ZPFvW9S95RtmFtR8", # "function": { # "arguments": "{\"location\": \"Osaka\"}", # "name": "get_current_weather" # }, # "type": "function" # } # ] # } # } # ], # "created": 1699352279, # "model": "gpt-3.5-turbo-1106", # "object": "chat.completion", # "system_fingerprint": "fp_eeff13170a", # "usage": { # "completion_tokens": 63, # "prompt_tokens": 114, # "total_tokens": 177 # } # }
"finish_reason": "tool_calls"
となっており、message
にもtool_calls
が含まれています。
tool_calls
にはどのツールを呼び出すべきかのリストが渡されていることが分かります。
これにより1回のレスポンスで複数のツール(今回はfunction)を開発者側で呼び出すことができます。
ツールを開発者側で呼び出す
各ツールの結果を以下のようなフォーマットでコンテキストして格納して渡すと、続きのチャットが可能です。
{ "tool_call_id": tool_call.id, "role": "tool", "name": function_name, "content": function_response, }
まずは関数呼び出しの結果を取得します。
from openai.types.chat import ChatCompletionToolMessageParam, ChatCompletionUserMessageParam tool_response_messages = [] if response.choices[0].message.tool_calls is not None: tool_calls = response.choices[0].message.tool_calls for tool_call in tool_calls: if tool_call.type == "function": function_call = tool_call.function function_name = function_call.name available_functions = [t.get("function").get("name") for t in tools if t.get("type") == "function"] if function_name in available_functions: function_arguments = function_call.arguments function_response = eval(function_name)(**eval(function_arguments)) else: raise Exception tool_response_messages.append( ChatCompletionToolMessageParam({ "tool_call_id": tool_call.id, "role": "tool", "content": function_response, }) ) # extend conversation with function response
このツールの結果と、初回のリクエストとレスポンスを一つのリストにまとめます。
history_messages = [ ChatCompletionUserMessageParam({"role": "user", "content": "サンフランシスコ、東京、大阪の天気は?"}), response.choices[0].message, *tool_response_messages ]
これを次のリクエストに渡すことで、続きを得ることができます。
続きのチャットを得る
以下で続きを得るためのリクエストを実行します。
response = client.chat.completions.create( model="gpt-3.5-turbo-1106", temperature=0.0, messages=[ *history_messages ], ) print(response["choices"][0]["message"]["content"]) # サンフランシスコの天気は摂氏22度で晴れ、東京の天気は摂氏10度で曇り、大阪の天気は摂氏22度で晴れです。
正しく使用できました。
まとめ
いかがでしたでしょうか。
ほぼ従来のFunction callingと使い方は同じですが、構造や引数名が変わっている点には注意が必要そうです。
本記事がご参考になれば幸いです。