CCCMKホールディングス TECH Labの Tech Blog

TECH Labスタッフによる格闘記録やマーケティング界隈についての記事など

AzureのFunction calling機能について

こんにちは、CCCMKホールディングスTECH LABの井上です。

7月にAzureでもFunction callingが使えるようになったので実際に使ってみました。 (この機能を使うにはapi-versionを2023-07-01-previewに設定する必要があります。)
Function callingとは、「ユーザの入力内容から適切な関数を判定してくれる処理」となります。 あくまでも判定だけなので判定後の処理は個別実装が必要となります。

使うにあたっての準備と手順は下記の通りです
①呼び出す関数の定義作成
 ChatGPTのAPIを呼び出すときに渡す関数となります
②呼び出す関数の実体作成
 実際に処理をする関数
③ChatGPTのAPIの呼び出し処理作成
④ChatGPTのAPIのResponseMessageの処理作成
⑤プロンプト入力と関数の実行(③④の実行)

以下、詳細になります。

①呼び出す関数の定義作成

functions = [
    {
        "name": "get_recipe",
        "description": "与えられた料理のレシピを取得します。",
        "parameters": {
            "type": "object",
            "properties": {
                "food": {
                    "type": "string",
                    "description": "レシピを検索する料理、例:ポトフ、ピラフ"
                }
            },
            "required": ["food"]
        }
    },
    {
        "name": "get_weather",
        "description": "インターネットの天気予報サイトから指定された都市の天気を取得します。",
        "parameters": {
            "type": "object",
            "properties": {
                "city": {
                    "type": "string",
                    "description": "天気を取得する都市、例:東京、大阪、ニューヨーク、ロンドン"
                }
            },
            "required": ["city"]
        }
    },
    {
        "name": "get_entertainment",
        "description": "インターネットのエンタメサイトから気分にあったコンテンツを提供します。",
        "parameters": {
            "type": "object",
            "properties": {
                "feeling": {
                    "type": "string",
                    "description": "気分を表す言葉、例:うきうき、楽しい、悲しい、疲れた、ラッキー等"
                },
                "category": {
                    "type": "string",
                    "description": "エンタメ種類、例:映画、本、コミック、音楽、動画、演劇"
                }
            },
            "required": ["feeling", "category"]
        }
    }
]

ここにあるdescriptionの内容は重要です。プロンプトエンジニアリング同様、適切な記述をすることで意図した関数を判定してくれる精度が上がります。

②呼び出す関数の実体作成

def get_recipe(food):
  funcname = sys._getframe().f_code.co_name
  return f"{funcname}がFunction callingされました。引数:({food})"

def get_weather(city):
  funcname = sys._getframe().f_code.co_name
  return f"{funcname}がFunction callingされました。引数:({city})"

def get_entertainment(feeling, category):
  funcname = sys._getframe().f_code.co_name
  return f"{funcname}がFunction callingされました。引数:({feeling}, {category})"


function_registerd = {"get_recipe":get_recipe,
                      "get_weather":get_weather,
                      "get_entertainment":get_entertainment}

とりあえず、呼び出された関数名と引数を返すだけの簡単な処理を実装しています。
function_registeredは④でレスポンス内の関数名を元にその関数が実行できるように辞書に関数を格納したものです。

③ChatGPTのAPIの呼び出し処理作成

def make_response(messages):
  response = openai.ChatCompletion.create(
    engine=deployment, # デプロイ名
    messages=messages,
    functions=functions,
    function_call="auto",
  )
  return response

function_callは"auto"と"none"が設定できますが、function callingsを使うには"auto"にしておく必要があります。"none"にすると関数は呼び出されずただのChatモードになります。

④ChatGPTのAPIのResponseMessageの処理作成

def process_response(response_message):
  if response_message.get("function_call"):
    print("Recommended Function call:")
    print(response_message.get("function_call"))
    print()   
    function_name = response_message["function_call"]["name"]
    function_to_call = function_registerd[function_name]
    function_args = json.loads(response_message["function_call"]["arguments"])
    function_res = function_to_call(**function_args)
    return function_res
  else:
    print(response_message['content'])
    return response_message

response_messageの中に"function_call"という要素が含まれているかで関数の利用の判定を行います。 適切な関数がない場合はこの要素が設定されていません。

⑤プロンプト入力と関数の実行(③④の実行)

messages = [{"role": "system", "content": "あなたはさまざまな事をサポートするアシスタントです。"}, 
            {"role": "user", "content": "今日の夕食はどうしようかな。玉ねぎと牛肉があったかな。"}
          ]
res = make_response(messages)
response_message = res["choices"][0]["message"]
print(process_response(response_message))

# 【実行結果】
# Recommended Function call:
# {
#  "name": "get_recipe",
#  "arguments": "{\n  \"food\": \"\u7389\u306d\u304e\u3068\u725b\u8089\"\n}"
# }

# get_recipeがFunction callingされました。引数:(玉ねぎと牛肉)


messages = [{"role": "system", "content": "あなたはさまざまな事をサポートするアシスタントです。"}, 
            {"role": "user", "content": "明日の福岡の天気を知りたい"}
          ]
res = make_response(messages)
response_message = res["choices"][0]["message"]
print(process_response(response_message))

# 【実行結果】
# Recommended Function call:
# {
#   "name": "get_weather",
#   "arguments": "{\n  \"city\": \"\u798f\u5ca1\"\n}"
# }

# get_weatherがFunction callingされました。引数:(福岡)


messages = [{"role": "system", "content": "あなたは天気とおススメショップに詳しいアシスタントです。"}, 
            {"role": "user", "content": "とてもつらいのでオススメのYoutubeを教えて"}
          ]
res = make_response(messages)
response_message = res["choices"][0]["message"]
print(process_response(response_message))

# 【実行結果】
# Recommended Function call:
# {
#   "name": "get_entertainment",
#   "arguments": "{\n  \"feeling\": \"\u3064\u3089\u3044\",\n  \"category\": \"\u52d5\u753b\"\n}"
# }

# get_entertainmentがFunction callingされました。引数:(つらい, 動画)

messages = [{"role": "system", "content": "あなたはさまざまな事をサポートするアシスタントです。"}, 
            {"role": "user", "content": "日本一長い川は?"}
          ]
res = make_response(messages)
response_message = res["choices"][0]["message"]
print(process_response(response_message))

# 【実行結果(該当処理なし)】
# 日本一長い川は信濃川です。
# {
#   "role": "assistant",
#   "content": "\u65e5\u672c\u4e00\u9577\u3044\u5ddd\u306f\u4fe1\u6fc3\u5ddd\u3067\u3059\u3002"
# }


function callingを使ったときの関数の利用判定の精度に依存するところが大きいと思いますが、業務への活用としては既存の処理をAPI化して自然言語で問い合わせできるようにする、ということが考えられます。