ChatGPTの新機能「Function Calling」にはやはりPostmanが便利

2023年6月13日、OpenAIはGPTの新バージョンの追加、大幅値下げと合わせて、[Chat Completions APIを使った新機能「Function Calling」](https://openai.com/blog/function-calling-and-other-api-updates)を発表しました。

これは簡単に説明すると、ChatGPTのAPIを呼んで、ユーザーの質問を元に自分が持っている機能（Function）のうちどれをどのように呼ぶべきかをChatGPTに解釈させ、さらに結果をChatGPTに渡せばユーザーに返すフレーズを作ってくれる、というものです。すでにデータベースや情報サービスを持っている方がターゲットになるかと思います。

このような外部連携は、今までであればChatGPTプラグインを作って提供したり、プロンプトエンジニアリングをがんばって機能を連携させて実現していたものですが、それがシンプルなAPIだけでできるようになったというのは画期的です。さらに、OpenAIが公式にサポートしているということもあり、正確性はかなり上がる、というように[OpenAIのシェイン・グウさんも話しています](https://twitter.com/shanegJP/status/1668796143931887617)。

本エントリでは、APIコールの扱いが簡単になるツール[Postman](https://www.postman.com)を使って、この新機能を試してみます。

# 準備

まずOpenAIにサインアップして[APIキーを取得](https://qiita.com/kotattsu3/items/b936a65d173a5d39dad0)します。

次に、[Postmanデスクトップ版をダウンロード](https://qiita.com/ponsuke0531/items/03483449ea0df505a540)します。ユーザー登録をしておくと、ブラウザでWebバージョンを使うことができるので便利です。ダウンロードが完了したらPostmanアプリを起動します。

Postmanには世界中のAPIのカタログが備わっており、例に漏れずOpenAIのAPIも登録されています。Postmanアプリ画面の上部中央にある検索フィールドで「OpenAI」を検索してみましょう。そうすると一番上に、バッジの付いたOpenAIのワークスペースが出てくるのでこれを選択します。
![Screenshot 2023-06-14 at 9.50.02 PM.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/32610/312663a0-360d-07ac-3cda-adebb338cadd.png)
そして、ワークスペースのAPIコレクション「OpenAI」の右端にある「･･･」を押すと出てくるメニューから「Create a fork」を選びます。Forkというのは、カタログに登録されているAPIコレクションをコピーして、自分のワークスペースに持ってくる操作です。一旦自分のワークスペースに持って来れば、好きなようにパラメータや設定を変更して保存することができるようになります。
![Screenshot 2023-06-14 at 10.02.47 PM.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/32610/2494c0fe-d496-1178-41d5-8dbe7eae5993.png)
Forkしたコピーを識別するラベル（Fork label）、ワークスペース（Workspace、これはデフォルトで各自に用意されているMy Woekspaceを選びましょう）を指定したら、「Fork Collection」ボタンを押します。
![Screenshot 2023-06-14 at 10.08.36 PM.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/32610/6a8ac2ee-b753-6893-965e-4879b789566b.png)
今度は画面上部の「Workspaces」メニューから「My Workspace」を選んで自分のワークスペースに移動します。
![Screenshot 2023-06-14 at 10.17.06 PM.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/32610/8ba1edb7-a40e-dfc0-d497-9860b5591d19.png)
ワークスペースの中にあるコレクションの階層を「OpenAI」→「Chat」→「Create chat message completion」とたどって出てきた項目が、今回使用するChat Completions APIです。
![Screenshot 2023-06-14 at 10.22.28 PM.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/32610/e43b5394-ae73-d934-0d6b-8cfdcbb017ef.png)
準備の仕上げとして、エントリ冒頭で取得したOpenAIのAPIキーを設定します。PostmanにはEnvironmentという設定で、あちこちで使用する値を変数として格納しておくことができます。まずは画面左端のナビゲーションから「Environments」を選択し、リストの左上にある「＋」ボタンを押して新しいEnvironmentを作成します。

Envronmentの名前として「ChatGPT Test」と入力して、テーブルの最初の行に、変数（Variable）として「token」、タイプ（Type）に「secret」、現在値（Current value）に取得したAPIキーを入力します。APIキーは取扱注意の秘密情報ですので、変数を「secret」タイプにしておけば表示がマスクされるので安心です。最後に「Save」ボタンを押して保存することを忘れないでください。
![Screenshot 2023-06-14 at 10.34.42 PM 1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/32610/94ad84a1-8deb-4fe4-e9f5-6fb3ceedfa13.png)
このEnvironmentの設定を実際に使うときには、画面右上のEnvironment切り替えメニューからここで作成した「ChatGPT Test」に切り替えておく必要があります。
![Screenshot 2023-06-14 at 10.42.49 PM.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/32610/e390b162-d572-3279-4650-240b404fb969.png)

# OpenAIのサンプルをそのまま試してみる

では、まず[こちらのページのサンプル](https://openai.com/blog/function-calling-and-other-api-updates)をPostmanを使ってコールしてみましょう。このサンプルは、ユーザーが天気について問いかけたときに、現在の天候を返す機能の呼び出し方をChatGPTに作らせるというものです。

再度コレクションから「Create chat message completion」を選ぶと、リクエストの編集画面になります。Chat Completions APIはPOSTメソッドの呼び出しなので、パラメータは「Body」タブに切り替えた上で、画面下部のエディタを使ってJSON形式で入力します。サンプルの通り、次のデータをペーストしてみてください。
![Screenshot 2023-06-14 at 10.48.08 PM.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/32610/b0798edb-41e8-0217-c365-c8205533522d.png)
`messages`がユーザーが入力した質問のテキスト、`functions`がこのAPIを使用する開発者が用意する機能（Function）の仕様を定義したものですね。この例では1つしかFunctionを指定していませんが、複数指定しておけばChatGPTがユーザーの質問に対して最もよく合うFunctionを選んで、パラメータの指定までやってくれるというわけです。

```json
{
  "model": "gpt-3.5-turbo-0613",
  "messages": [
    {"role": "user", "content": "What is the weather like in Boston?"}
  ],
  "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"]
      }
    }
  ]
}
```

「Send」ボタンを押すと、OpenAIのサーバにAPIリクエストが送られます。レスポンスはこのような感じです。
![Screenshot 2023-06-14 at 10.59.00 PM.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/32610/934827df-fc63-58b6-cc62-6f4ca83ed719.png)
注目すべきポイントは、ChatGPTが天気を尋ねているユーザーの意図を汲んで`get_current_weather`というFunctionを選択し、かつ、Bostonという地名を質問から抜き出して`location`に「Boston, MA」という値を埋めてくれているという点です。

```json
{
  "id": "chatcmpl-7RMEfHarZvZ7DsTkhioDG6dPyJ4PE",
  ...
  "choices": [
    {
      "index": 0,
      "message": {
        "role": "assistant",
        "content": null,
        "function_call": {
          "name": "get_current_weather",
          "arguments": "{\n  \"location\": \"Boston, MA\"\n}"
        }
      },
      "finish_reason": "function_call"
    }
  ],
  ...
}
```

次に、開発者は天気を検索するシステムを持っていると仮定して、上で得られたパラメータを元に、こんなJSONデータが取得できたものとします。

```json
{ "temperature": 22, "unit": "celsius", "description": "Sunny" }
```

再度、このデータ出力を取り込んでChat Completions APIを呼び出します。
![Screenshot 2023-06-14 at 11.20.09 PM.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/32610/63bddf94-942f-50e3-27c5-79b24d5cb5ed.png)
リクエストのBodyに入力する内容は、一番最初のリクエストをベースにしていますが、`messages`にChatGPTが返してきたFunctionの選択とパラメータ、そして外部機能を使って取得したデータ出力を追加しています。これは、あたかもこのような会話の経緯でデータを取得してきた、ということをChatGPT側に教えてあげているようなものですね。

```json
{
  "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"]
      }
    }
  ]
}
```

「Send」ボタンを押して返ってくるレスポンスはこのような感じです。
![Screenshot 2023-06-14 at 11.28.50 PM.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/32610/266a1c8d-d387-d44f-a584-b54b314baca8.png)
`messages`には、ユーザーに対する自然な回答の文が入っています。すごい・・。

```json
{
  "id": "chatcmpl-7RMZdrjDXaDny6pE5FfadTvrXd8DC",
  ...
  "choices": [
    {
      "index": 0,
      "message": {
        "role": "assistant",
        "content": "The weather in Boston is currently sunny with a temperature of 22 degrees Celsius."
      },
      "finish_reason": "stop"
    }
  ],
  ...
}
```

# OpenAIのサンプルのデータを日本語にしたら機能するのか？

じゃあ、これ日本語でも上手く動くの？と気になってきました。

そこで、入力としてこのようなデータをリクエストで送ってみました。質問だけでなく、Functionの定義も日本語です。ちなみにFunctionの`name`は英数字しか受け付けませんでした。

```json
{
  "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": ["摂氏", "華氏"]
          }
        },
        "required": ["location"]
      }
    }
  ]
}
```

すると、こんなレスポンスが返って来ます。横浜と聞いたのに対し、神奈川県横浜市、と県名まで埋めてきました。すごい・・。

```json
{
  "id": "chatcmpl-7RMqnNiRPVVmoGI0K6EHxQ1BJq8yV",
  ...
  "choices": [
    {
      "index": 0,
      "message": {
        "role": "assistant",
        "content": null,
        "function_call": {
          "name": "get_current_weather",
          "arguments": "{\n  \"location\": \"神奈川県横浜市\"\n}"
        }
      },
      "finish_reason": "function_call"
    }
  ],
  ...
}
```

そしてデータも日本語で埋めたリクエストです。

```json
{
  "model": "gpt-3.5-turbo-0613",
  "messages": [
    {"role": "user", "content": "横浜の天気はどんな感じ？"},
    {"role": "assistant", "content": null, "function_call": {"name": "get_current_weather", "arguments": "{ \"location\": \"神奈川県横浜市\"}"}},
    {"role": "function", "name": "get_current_weather", "content": "{\"temperature\": 22, \"unit\": \"摂氏\", \"description\": \"晴れ\"}"}
  ],
  "functions": [
    {
      "name": "get_current_weather",
      "description": "与えられた場所の現在の天気を取得",
      "parameters": {
        "type": "object",
        "properties": {
          "location": {
            "type": "string",
            "description": "県と市区町村。例: 愛知県名古屋市"
          },
          "unit": {
            "type": "string",
            "enum": ["摂氏", "華氏"]
          }
        },
        "required": ["location"]
      }
    }
  ]
}
```

それに対するレスポンスがこれです。完璧に返してきました。

```json
{
  "id": "chatcmpl-7RMx7CDcxQnqkKHHND2lvUr9iAVaA",
  ...
  "choices": [
    {
      "index": 0,
      "message": {
        "role": "assistant",
        "content": "横浜の天気は晴れで、気温は22℃です。"
      },
      "finish_reason": "stop"
    }
  ],
  ...
}
```

# 終わりに

以上のような感じで、新機能「Function Calling」のすごさが伝わったかと思います。シェイン・グウさんは「大規模言語モデル（Software 2.0）自体は計算や知識には制限があり、現実世界での実行能力はゼロでした。これにより既存の全てのソフト（Software 1.0）と簡単に連携ができます」と表現していましたが、まさにレガシーなデータ資産が、この機能によりAIの世界と結びついて活用が進んでいくのでは、という期待を抱かせてくれます。

ところでツールとして紹介したPostman、このようにAPIを繰り返し呼び出したり、色々なパラメータを試してみるときに便利だなという感想を持たれたのではないでしょうか。おそらく、このAI連携のような機能をきっかけとして、APIの重要性が格段に大きくなる世界がくると予感しています。そのときに、PostmanのようなAPIツールも注目が集まってくるかと思います。

ちょうどそんなタイミングで、Postmanが東京オフィスをオープンします。2023年7月4日には東京オフィス・プレオープンイベントとして [**Postman Tokyo Meetup 2023.7**](https://postman.connpass.com/event/286626/) をリアル会場・オンラインのハイブリッドで開催予定ですので、ご興味ある方はぜひご参加ください！
[![postman-api-platform-social-preview-2.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/32610/f6b2bb07-8da5-4b64-7ec1-037ae32e7d21.jpeg)](https://postman.connpass.com/event/286626/)