OpenAI APIのParallel function callingによる複数関数呼び出し
OpenAI APIにはFunction Callingという機能があって会話内容に応じて関数を使ってくれる。
それが11月6日のアップデートでパワーアップした。
- 今まで 事前に与えた複数の関数の中から1つを選んでくれる
- 現在 事前に与えた複数の関数の中から複数を選んでくれる
これは非常にありがたい。
今までがんばって実装しようとしていたことが簡単にできる。
実際に試して確認する。
「Parallel function calling」という単語はOpenAIの公式ドキュメントに載ってる公式用語のはずだが全然見かけない。
1. システムフロー
一般的なFunction Callingの使い方について説明。
Function Callingを使用したシステムは以下の図のように動作する。
青色の項目がOpenAI APIを使用している部分。
Function Callingは
を用意してくれるが関数を実行するわけではない。
関数を実行するところは別途作成する必要がある。
上記の図では関数の実行結果をうけてメッセージを作成しているが、
今回はParallel function callingを試すだけなので関数実行するだけで終わり。
2. 実験
コード全体は最後にまとめて書く。
OpenAIのアップデートによってpythonのopenaiライブラリの記述方法も大きく変わったので注意。
OpenAIに後方互換性の概念なし。
2.1. 用意した関数
動作がよく分かるようにシンプルな3つの関数を用意した。
1 2 3 4 5 6 7 8
| def function_a(args): print(f"関数Aが呼ばれました 引数:", args)
def function_b(args): print("関数Bが呼ばれました 引数:", args)
def function_c(args): print("関数Cが呼ばれました 引数:", args)
|
それぞれ度の関数が呼ばれたか分かるようにprintするだけ。
ついでにGPT-4の見繕ってくれた引数も表示。
2.2. 用意した関数リスト
上記3つの関数をどういう基準で選べばいいかGPT-4に教えてあげる必要がある。
そのために下記のようなリストを作成する。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50
| TOOLS = [ # OpenAI API に選んでもらう関数のリスト { "type": "function", "function": { "name": "function_a", "description": "食べ物に関する話題を処理", "parameters": { "type": "object", "properties": { "topic_word": { "type": "string" } }, "required": ["topic_word"] } } }, { "type": "function", "function": { "name": "function_b", "description": "ゲームに関する話題を処理", "parameters": { "type": "object", "properties": { "topic_word": { "type": "string" } }, "required": ["topic_word"] } } }, { "type": "function", "function": { "name": "function_c", "description": "動物に関する話題を処理", "parameters": { "type": "object", "properties": { "topic_word": { "type": "string" } }, "required": ["topic_word"] } } }, ]
|
これが正しく動作すると、
ユーザーの入力したメッセージに対して、
- 食べ物の話題なら”function_a”
- ゲームの話題なら”function_b”
- 動物の話題なら”function_c”
が実行されて、その根拠となった単語が引数として与えられる。
2.3. 実験結果
実際に試してみた結果を記す。
- 単純なパターン
1 2 3
| テキストを入力してください: コアラがラーメンを作っています 関数Cが呼ばれました 引数: {'topic_word': 'コアラ'} 関数Aが呼ばれました 引数: {'topic_word': 'ラーメン'}
|
正しく動作している。
従来のFunction Callingでは関数Aと関数Cのどちらかしか選んでくれなかった。
- 関数が呼ばれないパターン
1 2
| テキストを入力してください: コンビニへ行って電池を買いました 関数は呼び出されませんでした
|
今回のサンプルプログラムは「食べ物」、「ゲーム」、「動物」の話題に反応する。
どれも与えなければどの関数も選ばれない。
- 難しい単語のあるパターン
1 2 3
| テキストを入力してください: 焼き肉しながらスプラをしました 関数Aが呼ばれました 引数: {'topic_word': '焼き肉'} 関数Bが呼ばれました 引数: {'topic_word': 'スプラ'}
|
スプラトゥーンを省略した「スプラ」をゲームとして認識してくれた。
素晴らしい。
- 同一の関数を複数回呼び出すパターン
1 2 3 4 5 6
| テキストを入力してください: カバとキリンがラーメン屋で餃子をつまみながらマリオカートをしています 関数Aが呼ばれました 引数: {'topic_word': 'ラーメン'} 関数Aが呼ばれました 引数: {'topic_word': '餃子'} 関数Bが呼ばれました 引数: {'topic_word': 'マリオカート'} 関数Cが呼ばれました 引数: {'topic_word': 'カバ'} 関数Cが呼ばれました 引数: {'topic_word': 'キリン'}
|
試しにやってみたらできた。
これは嬉しい。
3. まとめ
従来のFunction Callingの時点で十分に便利だったが、若干不満に思っていた複数関数呼び出しに対応してくれてさらに使いやすくなった。
ちょくちょくライブラリの書き方を変更するのはやめて欲しい。
一応スマートな書き方になっていっているからまあ良い。
4. コード
実験に使用したコードを記す。
python3.11
インストールしたライブラリはopenai
のみ。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100
| import json from openai import OpenAI
# OpenAI APIのクライアントを作成 OpenAIclient = OpenAI()
# OpenAI API に選んでもらう関数の定義 def function_a(args): print(f"関数Aが呼ばれました 引数:", args)
def function_b(args): print("関数Bが呼ばれました 引数:", args)
def function_c(args): print("関数Cが呼ばれました 引数:", args)
# 定数 MODEL = "gpt-4-1106-preview" # 使用するGPTモデル名 TOOLS = [ # OpenAI API に選んでもらう関数のリスト { "type": "function", "function": { "name": "function_a", "description": "食べ物に関する話題を処理", "parameters": { "type": "object", "properties": { "topic_word": { "type": "string" } }, "required": ["topic_word"] } } }, { "type": "function", "function": { "name": "function_b", "description": "ゲームに関する話題を処理", "parameters": { "type": "object", "properties": { "topic_word": { "type": "string" } }, "required": ["topic_word"] } } }, { "type": "function", "function": { "name": "function_c", "description": "動物に関する話題を処理", "parameters": { "type": "object", "properties": { "topic_word": { "type": "string" } }, "required": ["topic_word"] } } }, ] FUNCTIONS_DICT = { # 関数名と関数の対応を定義した辞書 "function_a": function_a, "function_b": function_b, "function_c": function_c }
def conversation(user_message): # ステップ1: GPTにユーザークエリと利用可能な関数を送信 messages = [{"role": "user", "content": user_message}] response = OpenAIclient.chat.completions.create( model=MODEL, messages=messages, tools=TOOLS, tool_choice="auto", # APIに関数の選択を任せる ) response_message = response.choices[0].message # GPTからのレスポンスを取得 tool_calls = response_message.tool_calls # GPTが選んだ関数のリスト
# ステップ2: GPTが関数を呼び出すかどうかを確認 if tool_calls is None: print("関数は呼び出されませんでした") else: # ステップ3: 関数を呼び出す場合 for tool_call in tool_calls: # 選ばれた関数を順に呼び出す function_name = tool_call.function.name # 関数名を取得 function_to_call = FUNCTIONS_DICT[function_name] # 関数名から関数を取得 function_args = json.loads(tool_call.function.arguments) # 関数の引数を取得 function_to_call(function_args) # 関数を呼び出す # ユーザーにメッセージを入力してもらう user_message = input("テキストを入力してください: ") conversation(user_message)
|