OpenAIがChatGPTを更にパワーアップ！「Function Calling」の使い方とは？

ChatGPTにFunction Callingという機能が実装されました。
以下のページに該当機能の説明と例が書かれていましたので、それについて独自解説してみます。

https://openai.com/blog/function-calling-and-other-api-updates


## 概要
### まずは図解
まずは利用イメージをざっくり図解してみます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/77722/7f9d7209-8a78-a933-dcbc-27536ca48ecf.png)

### 利用イメージのステップ
Function callingは大きく3つのステップに分かれます。
① ユーザの質問からどの関数を呼ぶかをChatGPTに決めてもらう
② chatGPTが決めた関数を呼び出して処理実行
③ 関数の実行結果と最初の質問をChatGPTに渡して回答を生成する

図解をするとこんな感じです。大雑把にいうと、はじめに関数の仕様とメッセージを渡してどの関数を実行するか決めてもらい、指示通りに処理を実行した後でもう一度返答のメッセージを作ってもらうと言った、複数回の呼び出しが前提となります。これは先月くらいにリリースされた、ChatGPT plug-inと似た動きとなるので、使ったことがある方はイメージがしやすいのではないかと思います。
## 各ステップの説明

ここからは、利用イメージのステップをサンプルコードと共に説明します。

### ① ユーザからの指示と使える関数の一覧をChatGPTに渡して、どの関数をどうやって呼ぶかを決めてもらう

まずは使える関数の一覧とユーザーからの指示をまとめてChatGPTに渡します。messagesがユーザからの指示、functionsは関数の一覧となります。関数は配列型で渡すことになるため、複数設定することが可能です。
関数名とdescriptionに書かれた説明内容を見て、ユーザ指示に合っている関数を呼び出すことになる為、descriptionは特にしっかりと書きましょう。

```bash
curl <https://api.openai.com/v1/chat/completions> -u :$OPENAI_API_KEY -H 'Content-Type: application/json' -d '{
  "model": "gpt-3.5-turbo-0613",
  "messages": [
    {"role": "user", "content": "名古屋の天気を教えて"}
  ],
  "functions": [
    {
      "name": "get_current_weather",
      "description": "与えられた場所の天気の情報を返す",
      "parameters": {
        "type": "object",
        "properties": {
          "location": {
            "type": "string",
            "description": "天気を知りたい場所の県や市の名前 例）愛知県名古屋市"
          },
          "unit": {
            "type": "string",
            "enum": ["celsius", "fahrenheit"]
          }
        },
        "required": ["location"]
      }
    }
  ]
}'

```

上記のリクエストをChatGPTに投げると、ユーザーからのリクエスト内容と与えられた関数の中から最も最適なものを選んで、渡すべき値を入れた値を返してきます。このとき、関数定義で入力必須にしているとなんらかの値を必ず設定してくれますが、入力必須にしていない場合、該当する値がリクエスト内に無いと判断した場合は、何も値を設定しない形となります。以下にレスポンスの例を示します。

```json
{
    "id": "chatcmpl-7S2hLyuL1eF0gX5AOtkSKRxjYeGB3",
    "object": "chat.completion",
    "created": 1686917911,
    "model": "gpt-3.5-turbo-0613",
    "choices": [
        {
            "index": 0,
            "message": {
                "role": "assistant",
                "content": null,
                "function_call": {
                    "name": "get_current_weather",
                    "arguments": "{\\n  \\"location\\": \\"名古屋市\\"\\n}"
                }
            },
            "finish_reason": "function_call"
        }
    ],
    "usage": {
        "prompt_tokens": 115,
        "completion_tokens": 21,
        "total_tokens": 136
    }
}
```

これを見ると、ChatGPTからの指示としてget_current_weatherという関数を引数としてlocation:名古屋市を設定して呼び出す指示が返ってきていることがわかります。

ここが誤解してしまいやすい、かつポイントになる部分だと思いますが、ChatGPTは、関数の一覧とユーザのリクエストから、どのような関数を呼ぶかを判断するだけで、**ChatGPTが関数呼び出し処理を実際に実行するわけではない**という点に注意する必要があります。

### ② ChatGPTが決めた関数を呼び出して処理実行

次にChatGPTが判断した関数を実行し、その結果を入手します。ChatGPTからの関数実行指示はJSON形式で受け取ることになるため、その値を元に実際の関数を呼び出すプログラムを書く必要があります。

ここはChatGPT外の処理となるため、関数内では、外部のAPIを呼んだり、DBに問い合わせしたり、Lambda実行したりと、必要に応じて何でもできます。

以下の関数は例です。この例では固定で23度、予報は晴れという形で返す様にしています。実際には受け取った値を使って外部APIを呼んだりします。

```python
def get_current_weather(location, unit):

    """Get the current weather in a given location"""

    weather_info = {
        "location": location,
        "temperature": "23",
        "unit": unit,
        "forecast": ["晴れ"],
    }
    return json.dumps(weather_info)
```


以下は呼び出した関数からのレスポンス例です。

```json
{ "location":"名古屋", "temperature": 23, "unit": "celsius", "description": ["晴れ"] }
```

### ③ 関数の実行結果と最初の質問をChatGPTに渡して回答を生成する

関数の実行が完了したら、最初にChatGptに渡した情報と関数の実行結果を一緒に渡します。そうすると、ChatGPTがいい感じの回答を生成してくれます。この例では一つの関数からの結果を渡していますが、複数の結果を渡した場合はその両方の結果を踏まえて回答してくれました。

これなら、社内の商品データベースにある価格情報とAmazonでの実売価格を比べて差額を表示したい場合など、複数の関数を組み合わせて結果を表示することもできそうです。

```bash
curl <https://api.openai.com/v1/chat/completions> -u :$OPENAI_API_KEY -H 'Content-Type: application/json' -d '{
  "model": "gpt-3.5-turbo-0613",
  "messages": [
    {"role": "user", "content": "What is the weather like in Boston?"},
    {"role": "assistant", "content": null, "function_call": {"name": "get_current_weather", "arguments": "{ \\"location\\": \\"Boston, MA\\"}"}},
    {"role": "function", "name": "get_current_weather", "content": "{\\"temperature\\": "22", \\"unit\\": \\"celsius\\", \\"description\\": \\"Sunny\\"}"}
  ],
  "functions": [
    {
      "name": "get_current_weather",
      "description": "Get the current weather in a given location",
      "parameters": {
        "type": "object",
        "properties": {
          "location": {
            "type": "string",
            "description": "The city and state, e.g. San Francisco, CA"
          },
          "unit": {
            "type": "string",
            "enum": ["celsius", "fahrenheit"]
          }
        },
        "required": ["location"]
      }
    }
  ]
}'

```

## まとめ

この記事では、Function Callingを実際に利用する例を具体例を交えてみてきました。
これまでのチャット機能と比べると、返ってきた結果に応じて関数を呼び出したり、その結果を入れたりする必要があり、処理が多少複雑にはなりますが、複数関数呼ぶ機能などを組み合わせると様々なことができそうです。

次回は、実際に外部APIを呼ぶ例をサンプルコードと共に作ってみたいと思います。